• 八大排序(二)快速排序


    一、快速排序的思想

    快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

    二、快速排序的三种实现方法

    2.1、Hoare

    思想:取最左边key为基准值,用right指针找比key值小的元素,用left指针找比key位置大的元素,

    将两位置值进行交换,最后,将key值放在二者相遇位置上,就可保证key左边都是比key小的值,

    右边都是比key大的值,然后进行递归即可实现,从相遇点分割成两部分,在分别对左右两部分重

    复上述排序。

    代码实现 :           

    1. void Swap(int* p1, int* p2)
    2. {
    3. int temp = *p1;
    4. *p1 = *p2;
    5. *p2 = temp;
    6. }
    7. //Hoare
    8. int partSort1(int* a, int left, int right)
    9. {
    10. int key = left;
    11. while (right > left)
    12. {
    13. //从右往左找小
    14. while (right > left && a[right] >= a[key])
    15. {
    16. right--;
    17. }
    18. //从左往右找大
    19. while (right > left && a[left] <= a[key])
    20. {
    21. left++;
    22. }
    23. Swap(&a[left], &a[right]);
    24. }
    25. Swap(&a[left], &a[key]);
    26. return left;
    27. }
    28. void QuickSort(int* arr, int begin,int end)
    29. {
    30. if (begin >= end)
    31. {
    32. return;
    33. }
    34. int keyi = partSort1(arr, begin, end);
    35. QuickSort(arr, begin, keyi - 1);
    36. QuickSort(arr, keyi + 1, end);
    37. }

    2.2、挖坑法                                                                                             

    思想:取最左边或最右边值做key,右边形成一个坑,定义两个指针left、right指向头和尾。右边找

    小值放到左边坑中右边形成新坑,左边找大值放到右边左边形成新坑,将key放到相遇位置。这时

    key左边值均小于key,右边值均大于key。       

    代码实现:  

    1. void Swap(int* p1, int* p2)
    2. {
    3. int temp = *p1;
    4. *p1 = *p2;
    5. *p2 = temp;
    6. }
    7. //挖坑法
    8. int partSort2(int* a, int left, int right)
    9. {
    10. int hole = left;
    11. int key = a[left];
    12. while (right > left)
    13. {
    14. //从右往左找小
    15. while (right > left && a[right] >= key)
    16. {
    17. right--;
    18. }
    19. a[hole] = a[right];
    20. hole = right;
    21. //从左往右找大
    22. while (right > left && a[left] <= key)
    23. {
    24. left++;
    25. }
    26. a[hole] = a[left];
    27. hole = left;
    28. }
    29. a[hole] = key;
    30. return hole;
    31. }
    32. void QuickSort(int* arr, int begin,int end)
    33. {
    34. if (begin >= end)
    35. {
    36. return;
    37. }
    38. int keyi = partSort2(arr, begin, end);
    39. QuickSort(arr, begin, keyi - 1);
    40. QuickSort(arr, keyi + 1, end);
    41. }

       2.3、双指针法 

    思想:     

    1.选择数组中的第一个元素arr[startIndex]作为轴(pivot)

    2.左指针为left,从最左边开始寻找第一个比pivot大的数

    3.右指针为right,从最右面的一个元素开始向左寻找第一个小于等于pivot的数值

    4.经过2,3两个步骤后,将会出现以下两种情况

    ​ (1):left和right没有相遇,此时进行交换,swap(arr,left,right);

    ​ (2):left和right相遇,做swap(arr,startIndex,left),然后返回left

    5.partition中返回pivot用于分割数组,下一次用于排序的数组被分割为(startIndex,pivot-1),(pivot+1,endIndex)两段,进行递归操作

    代码实现:

    1. void Swap(int* p1, int* p2)
    2. {
    3. int temp = *p1;
    4. *p1 = *p2;
    5. *p2 = temp;
    6. }
    7. int partSort3(int* a, int left, int right)
    8. {
    9. int prev = left;
    10. int cur = prev + 1;
    11. int key = left;
    12. while (cur <= right)
    13. {
    14. if (a[cur] < a[key] && ++prev != cur)
    15. {
    16. Swap(&a[prev], &a[cur]);
    17. }
    18. cur++;
    19. }
    20. Swap(&a[prev], &a[key]);
    21. return prev;
    22. }
    23. void QuickSort(int* arr, int begin,int end)
    24. {
    25. if (begin >= end)
    26. {
    27. return;
    28. int keyi = partSort3(arr, begin, end);
    29. QuickSort(arr, begin, keyi - 1);
    30. QuickSort(arr, keyi + 1, end);
    31. }

    三、快速排序的优化

    3.1、三数取中

    当要排序的数组有序或者相对有序,比如我们要把一个逆序的数组按顺序排列,这时我们如果还选

    择left为key的话,效率就会非常的低。我们要排除这种低效的可能就要让Key的值相对靠中间一

    点,对此我们可以在实现一个函数,选择处left ,right ,和mid三个数中数值中间的那个数。用这个

    数作为key就会避免我们遇到的这类问题。


                            

    代码实现:

    1. //三数取中
    2. int Getmid(int* a, int left, int right)
    3. {
    4. int mid = (left + right) / 2;
    5. // left mid right
    6. if (a[left] < a[mid])
    7. {
    8. if (a[mid] < a[right])
    9. {
    10. return mid;
    11. }
    12. else if (a[left] > a[right]) // mid是最大值
    13. {
    14. return left;
    15. }
    16. else
    17. {
    18. return right;
    19. }
    20. }
    21. else // a[left] > a[mid]
    22. {
    23. if (a[mid] > a[right])
    24. {
    25. return mid;
    26. }
    27. else if (a[left] < a[right]) // mid是最小
    28. {
    29. return left;
    30. }
    31. else
    32. {
    33. return right;
    34. }
    35. }
    36. }

    对此我们就可以改进上面的三种方法,都可以在三种方法的开头添加这段代码,使之让key为靠中间的数,避免数组为有序的排序时间效率低的问题。

    1. int midi = Getmid(a, left, right);
    2. Swap(&a[left], &a[midi]);

    3.2、小区间优化 

    我们递归的深度越高效率越高,但是我们刚开始递归时深度很低,所以效率低下,所以我们可以采用高深度的时候用快速排序,在低深度的时候用直接插入排序,会对运行效率有所提高。

    1. void QuickSort(int* a, int begain, int end)
    2. {
    3. if (begain >= end)
    4. return;
    5. //小区间优化法 当数据量比较大的时候可以通过调整参数(20),来减小递归次数,提高性能
    6. if ((end - begain) > 20)
    7. {
    8. int meeti = HoareSort(a, begain, end);
    9. QuickSort(a, begain, meeti - 1);
    10. QuickSort(a, meeti + 1, end);
    11. }
    12. else
    13. {
    14. //数量比较少的时候用直接插入来排序
    15. InsertSort(a + begain, end - begain + 1);
    16. }
    17. }

    四、非递归实现快速排序

    递归需要在栈上为函数开辟空间,32位下,栈可使用的内存大小不超过2G,如果递归较深,依然可能会发生栈溢出,这个时候递归排序就不大适用,所以需要非递归出场。

    利用栈来存储区间下标,代码如下:要注意先数组头,后入数组尾。出栈时栈顶的数据为数组尾,在出才为头位置下标。

    代码如下:

    1. //交换函数
    2. void Swap(int* p1, int* p2)
    3. {
    4. int tmp = *p1;
    5. *p1 = *p2;
    6. *p2 = tmp;
    7. }
    8. //三数取中
    9. int GetMinIndex(int* arr, int left, int right)
    10. {
    11. int mid = (left + right) >> 1;
    12. if (arr[left] < arr[mid])
    13. {
    14. if (arr[mid] < arr[right])
    15. {
    16. return mid;
    17. }
    18. if (arr[left] < arr[right] && arr[right] < arr[mid])
    19. {
    20. return right;
    21. }
    22. return left;
    23. }
    24. else//arr[left] >= arr[mid]
    25. {
    26. if (arr[left] < arr[right])
    27. {
    28. return left;
    29. }
    30. if (arr[mid] < arr[right] && arr[right] < arr[left])
    31. {
    32. return right;
    33. }
    34. return mid;
    35. }
    36. }
    37. //快排非递归
    38. void QuickSort(int* arr, int n)
    39. {
    40. ST st;
    41. StackInit(&st);
    42. //把左右区间压栈,先压右边
    43. StackPush(&st, n - 1);
    44. //后压左边
    45. StackPush(&st, 0);
    46. //只要栈不为空,就继续分割排序
    47. while (!StackEmpty(&st))
    48. {
    49. //从栈里面取出左右区间
    50. int left = StackTop(&st);
    51. StackPop(&st);
    52. int right = StackTop(&st);
    53. StackPop(&st);
    54. int index = GetMinIndex(arr, left, right);
    55. //因为我们下面的逻辑都是把第一个数作为key
    56. //为了避免改动代码,这里我们直接交换就可以
    57. Swap(&arr[left], &arr[index]);
    58. //开始单趟排序
    59. int begin = left;
    60. int end = right;
    61. int pivot = begin;
    62. int key = arr[begin];
    63. while (begin < end)
    64. {
    65. //end开始找小
    66. while (begin < end && arr[end] >= key)
    67. {
    68. end--;
    69. }
    70. arr[pivot] = arr[end];
    71. pivot = end;
    72. //begin开始找大
    73. while (begin < end && arr[begin] <= key)
    74. {
    75. begin++;
    76. }
    77. arr[pivot] = arr[begin];
    78. pivot = begin;
    79. }
    80. pivot = begin;
    81. arr[pivot] = key;
    82. //区间分为[left,pivot-1]pivot[pivot+1,right]
    83. //利用循环继续分割区间
    84. //先入右子区间
    85. if (pivot + 1 < right)
    86. {
    87. //说明右子区间不止一个数
    88. //先入右边边界
    89. StackPush(&st, right);
    90. //再入左边边界
    91. StackPush(&st, pivot+1);
    92. }
    93. //再入左子区间
    94. if (left < pivot-1)
    95. {
    96. //说明左子区间不止一个数
    97. //先入右边边界
    98. StackPush(&st, pivot-1);
    99. //再入左边边界
    100. StackPush(&st, left);
    101. }
    102. }
    103. StackDestory(&st);
    104. }

    五、时间复杂度 

    快速排序的时间复杂度:

    最坏情况下,时间复杂度是O(n^2); (逆序)

    最优情况下,时间复杂度是O(nlogn);

    平均时间复杂度是O(nlogn);

    快速排序是时间复杂度:O(logn)

  • 相关阅读:
    Excel中行列范围的转换
    算法拾遗十六二叉树相关算法
    Java毕设项目个人博客网站计算机(附源码+系统+数据库+LW)
    23.2、Android -- OkHttp3 基础学习 自定义设置
    微信CRM系统:企业管理的黄金工具!
    Spring Security入门教程:利用Spring Security实现安全控制
    在matlab中使用模糊编辑器实现模糊控制器的设计详解
    搭载开源鸿蒙系统的嵌入式XM-RK3568工业互联方案
    Android集成腾讯TBS_X5内核的一些解决方法
    服务器感染的病毒有哪些特点呢?
  • 原文地址:https://blog.csdn.net/m0_69323023/article/details/133238409