• 【数据结构】快速排序


    1.引入

    快速排序的基本思想其实就是交换排序;我们所了解的冒泡排序就是一种交换排序;但是冒泡排序的时间复杂度为O(n^2),并不实用,而快速排序就是一种非常高效的排序,稳定性也比较不错,时间复杂度为O(n * lng n);

    先来看一下冒泡排序

    1. void Bubble(int* a,int n)
    2. {
    3. int i, j;
    4. for(i = 0; i < n - 1; i++)
    5. {
    6. for(j = 0; j < n - 1- i; j++)
    7. {
    8. if(a[j] > a[j + 1])
    9. {
    10. int tmp = a[j];
    11. a[j] = a[j + 1];
    12. a[j + 1] = tmp;
    13. }
    14. }
    15. }
    16. }

    显然 这种交换排序的主要特点就是将值较大的记录向序列的尾部移动,值较小的记录向序列的前部移动;(升序,降序反之)。

    先分析单趟的排序;单躺的冒泡排序是将 最大的值放到数组的最后面,之后就不考虑这个最大的数了,而快速排序的单趟是将准备好的 key 值放到 适当的位置(排好序的位置);

    下面先来分析一下快速排序的单趟排序:

    用两个参数 left 与 right ,分别从数组的两边出发,right 找比 key 值小的,找到之后停下,left 找比 key 值大的,找到之后也停下,互换两个数的,循环 直到 left == right;停下再将 key 与 left 互换;这样就是一个单趟的排序;将 key 值放到了适当的位置。 

    1. int QuickSort_along(int* a,int left;int right)
    2. {
    3. int keyi = left;
    4. while(left < right)
    5. {
    6. // R 先走 找小 L再找大(只找大小)
    7. while ((a[right] >= a[keyi]) && (left < right))
    8. {
    9. right--;
    10. }
    11. while ((a[left] <= a[keyi]) && (left < right))
    12. {
    13. left++;
    14. }
    15. if(left < right)
    16. swap(&a[left],&a[right]);
    17. }
    18. swap(&a[left],&a[keyi]);
    19. return left;
    20. }

    排完单趟的快速排序,现在来看整体的;

    1. #include
    2. QuickSort(int* a,int left,int right)
    3. {
    4. if(left < right)
    5. {
    6. return;
    7. }
    8. int keyi = QuickSort_along(a,left,right);
    9. QuickSort(a,left,keyi - 1);
    10. QuickSort(a,keyi + 1,right);
    11. }
    12. int main()
    13. {
    14. int a[] = {10,9,8,7,6,5,4,3,2,1};
    15. QuickSort(a,0,9);
    16. }

    上面的方法其实是有缺陷的,如果当数据恰好是按照降序排的,又恰巧数据很大,这时就会发生递归过深,栈溢出的现象;我们可以有两个优化的方法,

    1.三数取中:

    1. int GetMidIndex(int* a, int left, int right)
    2. {
    3. int mid = left + (right - left) / 2;
    4. if (a[left] > a[right])
    5. {
    6. if (a[left] > a[mid])
    7. {
    8. if (a[mid] > a[right])
    9. return mid;
    10. else
    11. return right;
    12. }
    13. else
    14. return left;
    15. }
    16. else
    17. {
    18. if (a[right] > a[mid])
    19. {
    20. if (a[mid] > a[left])
    21. return mid;
    22. else
    23. return left;
    24. }
    25. else
    26. return right;
    27. }
    28. }
    29. int QuickSort_along(int* a,int left;int right)
    30. {
    31. int mid = GetMidIndex(a, left, right);
    32. swap(&a[mid], &a[left]);
    33. int keyi = left;
    34. while(left < right)
    35. {
    36. // R 先走 找小 L再找大(只找大小)
    37. while ((a[right] >= a[keyi]) && (left < right))
    38. {
    39. right--;
    40. }
    41. while ((a[left] <= a[keyi]) && (left < right))
    42. {
    43. left++;
    44. }
    45. if(left < right)
    46. swap(&a[left],&a[right]);
    47. }
    48. swap(&a[left],&a[keyi]);
    49. return left;
    50. }

    2.最小区间优化:

    我们直到,当快速排序的递归模式和二叉树很像,递归越到深层,函数的调用就越多;而且调用的越深所排的数组就越有序,此时,我们跳出来,不让他进行快排的递归了,我们直接进行插入排序或者其他的排序来解决这一个问题。

    1. void InserSort(int* a, int n)
    2. {
    3. for (int i = 0; i < n - 1; i++)
    4. {
    5. int end = i;
    6. int tmp = a[end + 1];
    7. while (end >= 0)
    8. {
    9. if (a[end] > tmp)
    10. {
    11. a[end + 1] = a[end];
    12. --end;
    13. }
    14. else
    15. break;
    16. }
    17. a[end + 1] = tmp;
    18. }
    19. }
    20. QuickSort(int* a,int left,int right)
    21. {
    22. if(left < right)
    23. return;
    24. int keyi = QuickSort_along(a,left,right);
    25. if(keyi - left > 8)
    26. InserSort(a,keyi);
    27. else
    28. {
    29. QuickSort(a,left,keyi - 1);
    30. QuickSort(a,keyi + 1,right);
    31. }
    32. }

    除了上面的方法还有两种 单排的方法:

    1.挖坑法:

    1. int QuickSort_along(int* a, int left, int right)
    2. {
    3. // 三数取中优化
    4. int mid = GetMidIndex(a, left, right);
    5. swap(a[mid], a[left]);
    6. int key = a[left];
    7. int hole = left;
    8. while (left < right)
    9. {
    10. // 先从右边找小 赋值挖坑
    11. while (a[right] >= key && (left < right))
    12. {
    13. --right;
    14. }
    15. a[hole] = a[right];
    16. hole = right;
    17. // 先从左边找大 赋值挖坑
    18. while (a[left] <= key && (left < right))
    19. {
    20. ++left;
    21. }
    22. a[hole] = a[left];
    23. hole = left;
    24. }
    25. a[hole] = key;
    26. return hole;
    27. }

    2.前后指针法:

    1. int QuickSort_along(int* a, int left, int right)
    2. {
    3. int mid = GetMidIndex(a, left, right);
    4. swap(a[mid], a[left]);
    5. int keyi = left;
    6. int prev = left;
    7. int cur = prev + 1;
    8. while(cur >= right)
    9. {
    10. if(a[cur] < a[keyi] && ++prev != cur)
    11. swap(&a[cur],&a[prev]);
    12. cur++;
    13. }
    14. return prev;
    15. }

    最后还有一个:非递归的快速排序:

    利用数据结构的栈非递归快排
    其实就是将递归的思想用代码表现出来,利用了栈的后进先出的特性。

    1. void QuickSort_Stack(int* a, int left, int right)
    2. {
    3. Stack SL;
    4. StackInit(&SL);
    5. StackPush(&SL,left);
    6. StackPush(&SL,right);
    7. while (!StackEmpty(SL))
    8. {
    9. int end = StackFront(SL);
    10. StackPop(&SL);
    11. int begin = StackFront(SL);
    12. StackPop(&SL);
    13. /*if (begin >= end)
    14. {
    15. continue;
    16. }*/
    17. int keyi = PartSort2(a, begin, end);
    18. if (keyi + 1 < end)
    19. {
    20. StackPush(&SL, keyi + 1);
    21. StackPush(&SL, end);
    22. }
    23. if (begin < keyi - 1)
    24. {
    25. StackPush(&SL, begin);
    26. StackPush(&SL, keyi - 1);
    27. }
    28. }
    29. StackDestory(&SL);
    30. }
  • 相关阅读:
    Java8 为什么在接口中引入default方法,以及default方法的使用
    Cluster API 检索从未如此简单
    【知识回顾】Java常用类库-Java Runtime
    这资源也太强了!8月版最新AI脚本插件合集来了
    信创迁移适配实战-SpringBoot服务以war包部署后无法注册到Consul
    答粉丝问)【问题记录&解决】Python & MySQL:备份和恢复、灾难恢复方法(附代码) | 如何实现前端连python 后端传数据,生成前端折线图,echarts?
    【疯壳·平板教程3】手把手教你做平板电脑-LCD 驱动实验教程
    vue CORS 跨域问题 的终极解决方案
    深入理解Java虚拟机(第3版)学习笔记——线程安全与锁优化(超详细)
    JavaScript:二维码生成与解析
  • 原文地址:https://blog.csdn.net/weixin_57560405/article/details/126661170