• 快速排序模拟实现


    快速排序,时间复杂度为O(NlogN),属于排序中相对快的那一列,以下是快排的模拟实现:

    法一:左右指针交换法

    1. void swap(int* x, int* y)
    2. {
    3. int tmp = *x;
    4. *x = *y;
    5. *y = tmp;
    6. }//交换函数
    7. int getmid(int* a, int left, int right)
    8. {
    9. int mid = (left + right) / 2;
    10. if (a[left] < a[right])
    11. {
    12. if (a[mid] > a[right])
    13. {
    14. return right;
    15. }
    16. else
    17. {
    18. if (a[mid] > a[left])
    19. {
    20. return mid;
    21. }
    22. else
    23. {
    24. return left;
    25. }
    26. }
    27. }
    28. else //left大于right
    29. {
    30. if (a[mid] < a[right])
    31. {
    32. return right;
    33. }
    34. else
    35. {
    36. if (a[mid] < a[left])
    37. {
    38. return mid;
    39. }
    40. else
    41. {
    42. return left;
    43. }
    44. }
    45. }
    46. }//三数取中
    47. int PartSort1(int* a, int left, int right)
    48. {
    49. int mid = getmid(a, left, right);
    50. int keyi = left;
    51. while (left < right)
    52. {
    53. while (left < right && a[right]>=a[keyi])
    54. {
    55. right--;
    56. }
    57. while (left < right && a[left] <=a[keyi])
    58. {
    59. left++;
    60. }
    61. swap(&a[left], &a[right]);
    62. }
    63. swap(&a[keyi], &a[left]);
    64. return left;
    65. }
    66. void sort(int* arr, int left, int right)
    67. {
    68. if (left < right)
    69. {
    70. int mid = PartSort1(arr, left, right);
    71. sort(arr, left, mid - 1);
    72. sort(arr, mid + 1, right);
    73. }
    74. }

    本思路是以left下标所指的元素为分界线,left指针向右移动找比分界元素大的,right指针向左移动找比分解元素小的,然后两者交换。通过这样循环,使得左侧的元素都是比分界元素小的,右侧元素都是比分界元素大的,且此时分界元素已经放在了排好序后的正确位置上。再对分界元素左侧、右侧调用同样的方法,最后就能实现排序。三数取中是一种优化方式,使得left下标所指向的元素更加贴近当前left到righ范围内的中间值。最后之所以用left指向位置和keyi指向位置进行交换,是因为left此时所指元素一定小于等于keyi所指向的元素。下面会证明为什么left此时所指元素一定小于等于keyi所指向的元素:

    当循环停止以后,循环前的最后一次移动分两种情况,一种是right指针左移动碰到left,一种是left指针右移动碰到right。对于第一种情况,此时left指针所指的位置是前面left和right所指元素交换后的,所以此时left所指元素一定是小于keyi的。对于第二种情况,此时right指针已经结束移动,所指向的位置也是小于keyi的元素。所以left此时所指元素一定小于等于keyi所指向的元素。当然,如果先移动left指针再移动right指针,就不能符合上述结论。

    法二:挖坑法

    1. // 挖坑法
    2. int PartSort2(int* a, int left, int right)
    3. {
    4. int mid = getmid(a, left, right);
    5. swap(&a[left], &a[mid]);
    6. //三数取中
    7. int key = a[left];
    8. int hole = left;
    9. while (left < right)
    10. {
    11. while (left < right && a[right] >= key)
    12. {
    13. right--;
    14. }
    15. a[hole] = a[right];
    16. hole = right;
    17. while (left < right && a[left] <= key)
    18. {
    19. left++;
    20. }
    21. a[hole] = a[left];
    22. hole = left;
    23. }
    24. a[hole] = key;
    25. return hole;
    26. }
    27. void sort(int* arr, int left, int right)
    28. {
    29. if (left < right)
    30. {
    31. int mid = PartSort2(arr, left, right);
    32. sort(arr, left, mid - 1);
    33. sort(arr, mid + 1, right);
    34. }
    35. }

    该方法的思路是先把left所指的位置视为坑,保存最左的元素到可以中 ,此后利用right指针和left指针移动找元素填坑和创建新坑。此时整个数组中一直保持着有一个下标指向坑,最后再将最开始保存的key放入坑中,形成完整的数组。left和right指针的移动规则仍然是left找比key大的,right找比key小的。

    法三:前后指针法

    1. int PartSort3(int* a, int left, int right)
    2. {
    3. int prev = left;
    4. int cur = left+1;
    5. while (cur <= right)
    6. {
    7. while(cur <= right&&a[cur] <= a[left])
    8. {
    9. prev++;
    10. swap(&a[cur], &a[prev]);
    11. cur++;
    12. }
    13. if (cur > right)
    14. {
    15. break;
    16. }
    17. while (cur <= right && a[cur] > a[left])
    18. {
    19. cur++;
    20. }
    21. if (cur > right)
    22. {
    23. break;
    24. }
    25. }
    26. swap(&a[prev], &a[left]);
    27. return prev;
    28. }
    29. void sort(int* arr, int left, int right)
    30. {
    31. if (left < right)
    32. {
    33. int mid = PartSort2(arr, left, right);
    34. sort(arr, left, mid - 1);
    35. sort(arr, mid + 1, right);
    36. }
    37. }

    思路是让prev的下一位元素是比a[left]大的元素,cur所指的元素是比a[left]小的元素,然后交换,使得小于a[left]的元素往左移动,比a[left]大的元素不断往右移动。

  • 相关阅读:
    前端项目实战182-ant design Cascader实现自定义字段
    泛型基础使用
    MySql分库分表
    BUUCTF Reverse/firmware
    含有PEG 间隔基和一个末端伯胺基团(CAS:1006592-62-6)化学试剂
    嵌入式C语言经典笔试题
    linux篇【6】:进程等待
    深理解@State 变量 SwiftUI
    坚持五件事,带你走出迷茫困境
    Java基础抽象类详解
  • 原文地址:https://blog.csdn.net/yyssas/article/details/133160477