• 【数据结构】排序(2)—冒泡排序 & 快速排序


       

                                 

    目录

    一. 冒泡排序

    基本思想

    代码实现

    时间和空间复杂度

    稳定性

    二. 快速排序

    基本思想

    代码实现

    hoare法

    挖坑法

    前后指针法

    时间和空间复杂度

    稳定性


    一. 冒泡排序

           基本思想

               冒泡排序是一种交换排序。两两比较数组元素,如果是逆序(即排列顺序与排序后的顺序相     反)就交换,直到所有元素都有序为止。

          方法步骤:                   

              ① 比较相邻的元素。如果第一个比第二个大,就交换他们两个。

              ② 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后                  的元素会是最大的数

              ③ 针对所有的元素重复以上的步骤,除了最后一个。

              ④ 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较

            图示

            

    代码实现

      

    1. //冒泡排序
    2. void BubbleSort(int* a, int n)
    3. {
    4. for (int i = 0; i < n - 1; i++)
    5. {
    6. int flag = 0; //作为判断是否交换的标志
    7. for (int j = 1; j < n - i; j++)
    8. {
    9. if (a[j-1] > a[j])
    10. {
    11. flag = 1;
    12. int tmp = a[j-1]; //交换
    13. a[j-1] = a[j];
    14. a[j] = tmp;
    15. }
    16. }
    17. if (flag == 0)
    18. break;
    19. }
    20. }

    时间和空间复杂度

         若初始序列为正序序列,则只需进行一趟排序,在排序过程中进行n-1次比较不移动元素;若初始序列为逆序序列,则需进行n-1趟排序n(n-1) / 2次比较,每次比较都需要移动 3 次,移动次数为  3n(n-1) / 2.

        时间复杂度:O(n^2)

        空间复杂度:O(1)

    稳定性

         冒泡排序:稳定排序

    二. 快速排序

          基本思想

           任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止
     

         方法步骤:       

    1. 从数列中挑出一个元素,称为 "基准"(pivot);

    2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

    3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

         图示

    代码实现

       

     hoare法

            思想方法:

               定义两个指针 left 和 right,分别指向左边和右边,左指针从左向右找大( 大于pivotkey),右      指针从右向左找小(小于pivotkey),左大右小就交换,相遇时与基准值交换

             图解:

                   

            

     

    1. int PartSort(int* a, int left, int right) //给数组分区,返回枢轴元素下标
    2. {
    3. int pivotkey = left;
    4. while (left < right)
    5. {
    6. //右边找比pivotkey小的
    7. while (left < right && a[right] >= a[pivotkey])
    8. right--;
    9. //左边找比pivotkey大的
    10. while (left < right && a[left] <= a[pivotkey])
    11. left++;
    12. Swap(&a[left], &a[right]);
    13. }
    14. Swap(&a[left], &a[pivotkey]); //把记录的枢轴元素,交换到枢轴位置
    15. return left; //返回枢轴所在的位置下标
    16. }

       

     挖坑法

          思想方法

                  定义两个指针 left 和 right,分别指向左边和右边,先将第一个数据元素放在临时变量 pivotkey 中,形成一个坑位右指针先走,当指向的值小于 pivotkey 就停下,形成新的坑位;让左指针走,当指向的值大于 pivotkey 就停下,使其形成此次的新坑位,直到两指针相遇,把pivotkey的值放入坑中。

     

            图解:

             

               

    1. int PartSort(int* a, int left, int right) //给数组分区,返回枢轴元素下标
    2. {
    3. int pivotkey = a[left]; //保存第一个数据元素的值
    4. while (left < right)
    5. {
    6. while (left < right && a[right] >= pivotkey) //找小
    7. {
    8. right--;
    9. }
    10. a[left] = a[right]; //右边形成新的坑
    11. while (left < right && a[left] <= pivotkey) //找大
    12. {
    13. left++;
    14. }
    15. a[right] = a[left]; //左边形成新的坑
    16. }
    17. a[left] = pivotkey;
    18. return left;
    19. }

      前后指针法

          思想方法:

            定义两个指针 prev 和 cur ,初始时,prev指针指向序列开头,cur指针指向prev指针的后一个位置;cur的值与pivotkey的值比较,cur的值小,prev先后移一步,cur再后移;当cur的值大时,就prev的值与cur的值交换;直到cur为空时,prev的值与pivotkey的值交换。

           图解:

                  

           

    1. int PartSort(int* a, int left, int right) //给数组分区,返回枢轴元素下标
    2. {
    3. int prev = left;
    4. int cur = left + 1;
    5. int pivotkey = left;
    6. while (cur <= right)
    7. {
    8. if (a[cur] < a[pivotkey] && ++prev != cur)
    9. Swap(&a[cur], &a[prev]);
    10. cur++;
    11. }
    12. Swap(&a[pivotkey], &a[prev]);
    13. return prev;
    14. }

    时间和空间复杂度

             在最优情况下,partition每次都划的很均匀,此时时间复杂度为O(nlogn);平均情况下,其时   间复杂度也为O(nlogn)。在最坏的情况下,待排序的序列为正序或逆序时,递归树是一棵斜树,   此时,快速排序会堕落为冒泡排序,其时间复杂度为O(n^2),不过可以通过优化,使其提升为O(nlogn),总的来说还是O(nlogn)。

            空间复杂度,主要是递归造成的栈空间的使用,最好情况及平均情况下,树的递归深度为logn,空间复杂度均为O(logn);最坏情况,空间复杂度为O(n).

           时间复杂度:O(nlogn)

           空间复杂度:O(logn)

      稳定性

          由于元素的比较和交换是跳跃进行的,因此

          快速排序:不稳定排序

  • 相关阅读:
    面向对象的原型:prototype,原型链
    jmeter模拟多IP访问
    AVProVideo☀️四、视频播放案例
    Polygon zkEVM哈希状态机——Keccak-256和Poseidon
    P 算法与 K 算法
    代码实现:求前N个数字的阶乘
    vue3使用pinia实现数据缓存
    图解 Redis 分布式锁,写得太好了!
    基于Matlab实现图像分割技术(附上源码+图像)
    利用InnoSetup在VS编译时自动构建安装包
  • 原文地址:https://blog.csdn.net/HZ_ENG/article/details/133494141