• 【数据结构】排序--快速排序


    目录

    一 概念

    二 快速排序的实现

    1. hoare版本

    (1)代码实现

    (2)单趟排序图解

    (3) 递归实现图解

    (4)细节控制

    (5)时间复杂度

    (6)三数取中优化

    2 挖坑法

    (1)代码实现

    (2)单趟图解 

    3 前后指针法 

    (1) 代码实现 

    (2) 单趟图解

    ​4 优化子区间 

    5 非递归快速排序

    ​ 三 快速排序的特性总结


    一 概念

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

    二 快速排序的实现

    上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像

    1. hoare版本

    (1)代码实现

    1. #include
    2. void Swap(int* x, int* y)
    3. {
    4. int tmp = *x;
    5. *x = *y;
    6. *y = tmp;
    7. }
    8. int PartSort1(int* a, int left, int right)
    9. {
    10. int keyi = left;
    11. while (left < right)
    12. {
    13. //找小
    14. while (left < right && a[right] >= a[keyi])
    15. {
    16. right--;
    17. }
    18. //找大
    19. while (left < right && a[left] <= a[keyi])
    20. {
    21. left++;
    22. }
    23. Swap(&a[left], &a[right]);
    24. }
    25. Swap(&a[keyi], &a[left]);
    26. return left;
    27. }
    28. void QuickSort(int* a, int begin, int end)
    29. {
    30. if (begin >= end)
    31. {
    32. return;
    33. }
    34. int key = PartSort1(a, begin, end);
    35. QuickSort(a, begin, key - 1);
    36. QuickSort(a, key + 1, end);
    37. }
    38. int main()
    39. {
    40. int arr[] = {6,1,2,7,9,3,4,5,10,8};
    41. QuickSort(arr, 0, (sizeof(arr) / sizeof(int)) - 1);
    42. for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
    43. {
    44. printf("%d ", arr[i]);
    45. }
    46. }

    (2)单趟排序图解

    我们看看单趟排序怎么排的

     

    (3) 递归实现图解

    再来看看递归怎么实现的

     

    (4)细节控制

    对细节控制上 我要做一下解释

     那这里相遇位置一定比a[keyi]小呢?  右边先走导致的

    (5)时间复杂度

    我们来算一下快速排序的时间复杂度(需要对二叉树的基本性质熟悉)

     

    (6)三数取中优化

    那针对有序 的情况 我们可以采取三数取中的方式解决

    1. #include
    2. void Swap(int* x, int* y)
    3. {
    4. int tmp = *x;
    5. *x = *y;
    6. *y = tmp;
    7. }
    8. int GetMidi(int* a, int left, int right)
    9. {
    10. int mid = (left + right) / 2;
    11. if (a[left] < a[mid])
    12. {
    13. if (a[mid] < a[right])
    14. {
    15. return mid;
    16. }
    17. else if (a[left] > a[right])
    18. {
    19. return left;
    20. }
    21. else
    22. {
    23. return right;
    24. }
    25. }
    26. else// a[left] > a[mid]
    27. {
    28. if (a[left] < a[right])
    29. {
    30. return left;
    31. }
    32. else if (a[mid] > a[right])
    33. {
    34. return mid;
    35. }
    36. else
    37. {
    38. return right;
    39. }
    40. }
    41. }
    42. int PartSort1(int* a, int left, int right)
    43. {
    44. int midi = GetMidi(a, left, right);
    45. Swap(&a[midi], &a[left]);
    46. int keyi = left;
    47. while (left < right)
    48. {
    49. //找小
    50. while (left < right && a[right] >= a[keyi])
    51. {
    52. right--;
    53. }
    54. //找大
    55. while (left < right && a[left] <= a[keyi])
    56. {
    57. left++;
    58. }
    59. Swap(&a[left], &a[right]);
    60. }
    61. Swap(&a[keyi], &a[left]);
    62. return left;
    63. }
    64. void QuickSort(int* a, int begin, int end)
    65. {
    66. if (begin >= end)
    67. {
    68. return;
    69. }
    70. int key = PartSort1(a, begin, end);
    71. QuickSort(a, begin, key - 1);
    72. QuickSort(a, key + 1, end);
    73. }
    74. int main()
    75. {
    76. int arr[] = {6,1,2,7,9,3,4,5,10,8};
    77. QuickSort(arr, 0, (sizeof(arr) / sizeof(int)) - 1);
    78. for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
    79. {
    80. printf("%d ", arr[i]);
    81. }
    82. }

     2 挖坑法

      (1)代码实现

    1. #include
    2. void Swap(int* x, int* y)
    3. {
    4. int tmp = *x;
    5. *x = *y;
    6. *y = tmp;
    7. }
    8. int GetMidi(int* a, int left, int right)
    9. {
    10. int mid = (left + right) / 2;
    11. if (a[left] < a[mid])
    12. {
    13. if (a[mid] < a[right])
    14. {
    15. return mid;
    16. }
    17. else if (a[left] > a[right])
    18. {
    19. return left;
    20. }
    21. else
    22. {
    23. return right;
    24. }
    25. }
    26. else// a[left] > a[mid]
    27. {
    28. if (a[left] < a[right])
    29. {
    30. return left;
    31. }
    32. else if (a[mid] > a[right])
    33. {
    34. return mid;
    35. }
    36. else
    37. {
    38. return right;
    39. }
    40. }
    41. }
    42. int PartSort2(int* a, int left, int right)
    43. {
    44. int midi = GetMidi(a, left, right);
    45. Swap(&a[left], &a[midi]);
    46. int key = a[left];
    47. //保存key值以后 左边形成第一个坑位
    48. int hole = left;
    49. while (left < right)
    50. {
    51. //右边先走,找小,填到左边的坑,右边形成新的坑位
    52. if (left < right && a[right] >= key)
    53. {
    54. right--;
    55. }
    56. a[hole] = a[right];
    57. hole = right;
    58. //左边再走,找大,填到右边的坑,左边形成新的坑位
    59. if (left < right && a[left] <= key)
    60. {
    61. left++;
    62. }
    63. a[hole] = a[left];
    64. hole = left;
    65. }
    66. a[hole] = key;
    67. return hole;
    68. }
    69. void QuickSort(int* a, int begin, int end)
    70. {
    71. if (begin >= end)
    72. {
    73. return;
    74. }
    75. int key = PartSort2(a, begin, end);
    76. QuickSort(a, begin, key - 1);
    77. QuickSort(a, key + 1, end);
    78. }
    79. int main()
    80. {
    81. int arr[] = {6,1,2,7,9,3,4,5,10,8};
    82. QuickSort(arr, 0, (sizeof(arr) / sizeof(int)) - 1);
    83. for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
    84. {
    85. printf("%d ", arr[i]);
    86. }
    87. }

    (2)单趟图解 

    3 前后指针法 

    (1) 代码实现 

    1. int PartSort3(int* a, int left, int right)
    2. {
    3. int keyi = left;
    4. int prev = left;
    5. int cur = prev + 1;
    6. while (cur <= right)
    7. {
    8. if (a[cur] < a[keyi] && ++prev != cur)
    9. {
    10. Swap(&a[cur], &a[prev]);
    11. }
    12. cur++;
    13. }
    14. Swap(&a[keyi], &a[prev]);
    15. return prev;
    16. }
    17. void QuickSort(int* a, int begin, int end)
    18. {
    19. if (begin >= end)
    20. {
    21. return;
    22. }
    23. int key = PartSort3(a, begin, end);
    24. QuickSort(a, begin, key - 1);
    25. QuickSort(a, key + 1, end);
    26. }
    27. int main()
    28. {
    29. int arr[] = {6,1,2,7,9,3,4,5,10,8};
    30. QuickSort(arr, 0, (sizeof(arr) / sizeof(int)) - 1);
    31. for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
    32. {
    33. printf("%d ", arr[i]);
    34. }
    35. }

    (2) 单趟图解

    4 优化子区间 

    递归到小的子区间时, 不在递归分割排序,可以考虑使用插入排序

    因为区间比较小的时候节点数开的很多  特别是最后一层 节点数占了整个数大致百分之五十

     

    1. int PartSort(int* a, int left, int right)
    2. {
    3. int midi = GetMidi(a, left, right);
    4. Swap(&a[left], &a[midi]);
    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. {
    12. Swap(&a[cur], &a[prev]);
    13. }
    14. cur++;
    15. }
    16. Swap(&a[prev], &a[keyi]);
    17. return prev;
    18. }
    19. void QuickSort(int* a, int begin, int end)
    20. {
    21. if (begin >= end)
    22. {
    23. return;
    24. }
    25. if ((end - begin + 1) > 10)
    26. {
    27. int keyi = PartSort(a, begin, end);
    28. QuickSort(a, begin, keyi - 1);
    29. QuickSort(a, keyi + 1, end);
    30. }
    31. else
    32. {
    33. //插入排序
    34. InsertSort(a + begin, end - begin + 1);
    35. }
    36. }

    5 非递归快速排序

    需要有对栈的基础  不会的可以看前面的博客

    1. #include
    2. #include
    3. #include
    4. #include
    5. typedef struct STList
    6. {
    7. int* a;
    8. int size;
    9. int capacity;
    10. }ST;
    11. void STInit(ST* ps)
    12. {
    13. assert(ps);
    14. ps->a = NULL;
    15. ps->size = ps->capacity = 0;
    16. }
    17. void STDestroy(ST* ps)
    18. {
    19. assert(ps);
    20. free(ps->a);
    21. ps->a = NULL;
    22. ps->size = ps->capacity = 0;
    23. }
    24. void STPush(ST* ps, int x)
    25. {
    26. assert(ps);
    27. if (ps->size == ps->capacity)
    28. {
    29. int newcapacity = (ps->capacity == 0 ? 4 : ps->capacity * 2);
    30. int* tmp = (int*)realloc(ps->a, sizeof(int) * newcapacity);
    31. if (tmp == NULL)
    32. {
    33. perror("realloc fail");
    34. exit(-1);
    35. }
    36. ps->a = tmp;
    37. ps->capacity = newcapacity;
    38. }
    39. ps->a[ps->size] = x;
    40. ps->size++;
    41. }
    42. void STPop(ST* ps)
    43. {
    44. assert(ps);
    45. assert(ps->size > 0);
    46. ps->size--;
    47. }
    48. bool STEmpty(ST* ps)
    49. {
    50. assert(ps);
    51. return ps->size == 0;
    52. }
    53. int STTop(ST* ps)
    54. {
    55. assert(ps);
    56. assert(ps->size > 0);
    57. return ps->a[ps->size - 1];
    58. }
    59. void Swap(int* x, int* y)
    60. {
    61. int tmp = *x;
    62. *x = *y;
    63. *y = tmp;
    64. }
    65. int GetMidi(int* a, int left, int right)
    66. {
    67. int mid = (left + right) / 2;
    68. if (a[left] < a[mid])
    69. {
    70. if (a[mid] < a[right])
    71. {
    72. return mid;
    73. }
    74. else if (a[left] > a[right])
    75. {
    76. return left;
    77. }
    78. else
    79. {
    80. return right;
    81. }
    82. }
    83. else// a[left] > a[mid]
    84. {
    85. if (a[left] < a[right])
    86. {
    87. return left;
    88. }
    89. else if (a[mid] > a[right])
    90. {
    91. return mid;
    92. }
    93. else
    94. {
    95. return right;
    96. }
    97. }
    98. }
    99. int PartSort(int* a, int left, int right)
    100. {
    101. int midi = GetMidi(a, left, right);
    102. Swap(&a[left], &a[midi]);
    103. int keyi = left;
    104. int prev = left;
    105. int cur = prev + 1;
    106. while (cur <= right)
    107. {
    108. if (a[cur] < a[keyi] && ++prev != cur)
    109. {
    110. Swap(&a[cur], &a[prev]);
    111. }
    112. cur++;
    113. }
    114. Swap(&a[prev], &a[keyi]);
    115. return prev;
    116. }
    117. void QuickSortNonR(int* a, int begin, int end)
    118. {
    119. ST st;//创建一个栈
    120. STInit(&st);//初始化
    121. STPush(&st, end);
    122. STPush(&st, begin);
    123. while (!STEmpty(&st))
    124. {
    125. int left = STTop(&st);
    126. STPop(&st);
    127. int right = STTop(&st);
    128. STPop(&st);
    129. int keyi = PartSort(a, left, right);
    130. // [lefy,keyi-1] keyi [keyi+1, right]
    131. if (keyi + 1 < right)
    132. {
    133. STPush(&st, right);
    134. STPush(&st, keyi + 1);
    135. }
    136. if (left < keyi - 1)
    137. {
    138. STPush(&st, keyi - 1);
    139. STPush(&st, left);
    140. }
    141. }
    142. STDestroy(&st);
    143. }
    144. int main()
    145. {
    146. int arr[] = {6,1,2,7,9,3,4,5,10,8};
    147. QuickSortNonR(arr, 0, (sizeof(arr) / sizeof(int)) - 1);
    148. for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
    149. {
    150. printf("%d ", arr[i]);
    151. }
    152. }

     三 快速排序的特性总结

    1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

    2. 时间复杂度:O(N*logN)

    3. 空间复杂度:O(logN)

    4. 稳定性:不稳定

    本节难度还是不低, 但是我觉得大家根据图解和代码慢慢吃透还是不难的,这节我画的图解很多, 对重点和难点进行了很细致的划分和讲解, 希望大家可以窥探到快速排序的妙处

    继续加油!

  • 相关阅读:
    你觉得好的代码可能并不是最优的解决方案
    企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图
    浅谈系统架构中的状态
    下载 Windows11 23H2 官方系统镜像
    四十二、《大数据项目实战之用户行为分析》多框架整合实时分析用户行为日志数据流
    (翻译) CAP 理论 FAQ
    一台机器下,多个Java版本的粗放与精细管理
    无线通信模块定点传输-点对多点的具体传输应用
    JVM——10.对象的内存布局
    Leetcode652. 寻找重复的子树
  • 原文地址:https://blog.csdn.net/yf214wxy/article/details/133828137