• 快速排序--QuickSort()--递归版本


    一、快速排序(递归版本)

    1.快速排序模板(初始版本)

    1. void QuickSort(int* a, int begin, int end)
    2. {
    3. // 区间不存在,或者只有一个值则不需要在处理
    4. if (begin >= end)
    5. {
    6. return;
    7. }
    8. int keyi = PartSort1(a, begin, end);
    9. // [begin, keyi-1] keyi [keyi+1, end]
    10. QuickSort(a, begin, keyi - 1);
    11. QuickSort(a, keyi+1, end);
    12. }

    存在问题、特点:

    1.递归次数过多

    2.有序等的极端情况会使其递归过深,导致栈溢出

    3.没优化前,快排的效率和key的选取有关,选越靠近最大,或者越靠近最小的数,效率越慢;越靠近中位数,效率越快。

    2.快速排序模板(优化版本)

    1. void QuickSort(int* a, int begin, int end)
    2. {
    3. //callCount++;
    4. //printf("%p\n", &callCount);
    5. // 区间不存在,或者只有一个值则不需要在处理
    6. if (begin >= end)
    7. {
    8. return;
    9. }
    10. if (end - begin > 10)
    11. {
    12. int keyi = PartSort1(a, begin, end);
    13. // [begin, keyi-1] keyi [keyi+1, end]
    14. QuickSort(a, begin, keyi - 1);
    15. QuickSort(a, keyi + 1, end);
    16. }
    17. else
    18. {
    19. InsertSort(a+begin, end - begin + 1);//当区间过小时,可以直接用插入排序,较为高效
    20. }
    21. }

    图示:

    优点:

    假设总共有n个元素需要排的话,递归层次越深,所需要排序的次数也就越多,例:第一次要排两次,第二次要排四次,...,最后一次要2^h次,---(.h从0开始,树有h-1层,则树的总节点数为 n=2^(h+1) -1.得h=log(n+1) -1)---能看得出最后一次就占了全部排序次数的50%。 所以该优化方法,可以大大的减少排序的次数和递归的次数。

    3.快速排序的三个方法partsort()

    (1)hoare版本        时间复杂度:O(N*logN)

    1. int PartSort1(int* a, int begin, int end)
    2. {
    3. int left = begin, right = end;
    4. int keyi = left;
    5. while (left < right)
    6. {
    7. // 右边先走,找小
    8. while (left < right && a[right] >= a[keyi])
    9. {
    10. --right;
    11. }
    12. // 左边再走,找大
    13. while (left < right && a[left] <= a[keyi])
    14. {
    15. ++left;
    16. }
    17. Swap(&a[left], &a[right]);
    18. }
    19. Swap(&a[keyi], &a[left]);
    20. keyi = left;
    21. return keyi;
    22. }

    (2)挖洞法

    1. int PartSort2(int* a, int begin, int end)
    2. {
    3. int key = a[begin];
    4. int piti = begin;
    5. while (begin < end)
    6. {
    7. // 右边找小,填到左边的坑里面去。这个位置形成新的坑
    8. while (begin < end && a[end] >= key)
    9. {
    10. --end;
    11. }
    12. a[piti] = a[end];
    13. piti = end;
    14. // 左边找大,填到右边的坑里面去。这个位置形成新的坑
    15. while (begin < end && a[begin] <= key)
    16. {
    17. ++begin;
    18. }
    19. a[piti] = a[begin];
    20. piti = begin;
    21. }
    22. a[piti] = key;
    23. return piti;
    24. }

    (3)前后指针法 (进一步优化了模板--加入了getmid三数取中原则--减少了对有序序列排序时递归过深等的极端情况)

     

    1. int GetMidIndex(int* a, int begin, int end)
    2. {
    3. int mid = (begin + end) / 2;
    4. if (a[begin] < a[mid])
    5. {
    6. if (a[mid] < a[end])
    7. {
    8. return mid;
    9. }
    10. else if (a[begin] < a[end])
    11. {
    12. return end;
    13. }
    14. else
    15. {
    16. return begin;
    17. }
    18. }
    19. else if(a[begin] >= a[mid])
    20. {
    21. if (a[mid] > a[end])
    22. {
    23. return mid;
    24. }
    25. else if (a[begin] < a[end])
    26. {
    27. return begin;
    28. }
    29. else
    30. {
    31. return end;
    32. }
    33. }
    34. }
    35. int PartSort3(int* a, int begin, int end)
    36. {
    37. int prev = begin;
    38. int cur = begin + 1;
    39. int keyi = begin;
    40. // 加入三数取中的优化--用于随机选midi放到keyi,使keyi既不是最大也不是最小
    41. int midi = GetMidIndex(a, begin, end);
    42. Swap(&a[keyi], &a[midi]);
    43. while (cur <= end)
    44. {
    45. // cur位置的之小于keyi位置值
    46. if (a[cur] < a[keyi] && ++prev != cur)
    47. Swap(&a[prev], &a[cur]);
    48. ++cur;
    49. }
    50. Swap(&a[prev], &a[keyi]);
    51. keyi = prev;
    52. return keyi;
    53. }

     4.疑惑解答

    (1)hoare法 和 挖洞法 为什么 左边做key 一定要右边先走?

    答:

    1.R先走,R停下来,L去遇到R

    可以保证:相遇位置就是R停下的位置,R的位置是比key要小的,确保后续和key交换时,是小的交换去左边了,大的在右边.

    2.R先走,若R没有找到比key要小的值,R直接去到L

    可以保证:相遇位置是L上一轮停下来的位置(要么就是key,要么就是比key要小的值。)

    也确保了用R遇到的交换到左边的是key或者比key大的值.L交换到右边的是key或者比key要小的值

    结论:左边做key,右边先走;右边做key,左边先走。

  • 相关阅读:
    苹果签名有多少种类之TF签名(TestFlight签名)是什么?优势是什么?什么场合需要应用到?
    会议论文分析-CCS21-ML增强的符号执行方法
    JSP useBean动作
    win部署CRM
    AMD(锐龙)处理器解决安装 AndroidStudio 虚拟机失败问题
    【sim-storage-client】SpringBoot集成Minio与本地存储
    后量子时代,未来密码该何去何从?
    Python Web框架Django
    监控基本概念
    Java基于JSP实验室预约管理系统
  • 原文地址:https://blog.csdn.net/anyway33/article/details/126544644