• 快排三种递归及其优化,非递归和三路划分


    个人主页:Lei宝啊 

    愿所有美好如期而遇


    目录

    快排简介:

    快排的三种递归实现:

    Hoare:

    挖坑:

    双指针:

    小区间优化:

    三数取中优化:

    快排非递归实现:

    快排的三路划分实现:


    快排简介:

    快速排序,参见: qsort详解及其模拟实现


    快排的三种递归实现:

    Hoare:

    此法乃Hoare大佬所创,我等一个不注意便就掉入陷阱,大坑于代码处自有注释,诸君慢品:

    1. //Hoare版本 right传数组下标
    2. void QuickSort_Binary(int* arr, int left, int right)
    3. {
    4. if (left >= right)
    5. return;
    6. //选定一个数keyi,最好是不大也不小,上面加个三数取中
    7. int keyi = left;
    8. //快排开始的区间,都是闭区间
    9. int begin = left;
    10. int end = right;
    11. while (begin < end)
    12. {
    13. //一坑在=,若无此,循环不止
    14. //二坑在begin < end,若无此,有越界之忧,end或减减不止
    15. //三坑在要从右先行,以此保证begin与end相遇时
    16. // 二者所指处值小于keyi所指值
    17. while (begin < end && arr[end] >= arr[keyi])
    18. {
    19. end--;
    20. }
    21. while (begin < end && arr[begin] <= arr[keyi])
    22. {
    23. begin++;
    24. }
    25. Swap(&arr[end], &arr[begin]);
    26. }
    27. Swap(&arr[begin], &arr[keyi]);
    28. QuickSort_Binary(arr, left, begin - 1);
    29. QuickSort_Binary(arr, begin + 1, right);
    30. }

    挖坑:

    此法则无关左右矣

    1. //挖坑法 传数组下标
    2. void QuickSort_Binary(int* arr, int left, int right)
    3. {
    4. if (left >= right)
    5. return;
    6. int hole = left;
    7. int temp = arr[left];
    8. //定义这两变量主要是为了区分后面递归时的区间
    9. int begin = left;
    10. int end = right;
    11. while(begin < end)
    12. {
    13. while(begin < end && arr[end] >= temp)
    14. {
    15. end--;
    16. }
    17. //交换爽啊,赋值的话循环结束后,还要把temp的值赋给hole位置
    18. Swap(&arr[hole], &arr[end]);
    19. hole = end;
    20. while (begin < end && arr[begin] <= temp)
    21. {
    22. begin++;
    23. }
    24. Swap(&arr[hole], &arr[begin]);
    25. hole = begin;
    26. }
    27. QuickSort_Binary(arr, left, begin - 1);
    28. QuickSort_Binary(arr, begin + 1, right);
    29. }

    双指针

    1. //双指针法 传数组下标
    2. void QuickSort_Binary(int* arr, int left, int right)
    3. {
    4. if (left >= right)
    5. return;
    6. int temp = arr[left];
    7. int prev = left;
    8. int cur = left;
    9. while (cur <= right)
    10. {
    11. while (arr[cur] < temp && ++prev != cur)
    12. {
    13. Swap(&arr[prev], &arr[cur]);
    14. }
    15. cur++;
    16. }
    17. //想法大致都是keyi位置的值不动,从下一个位置开始,最后交换keyi位置和停止位置
    18. //停止位置的值一定比keyi位置的值要小
    19. Swap(&arr[left], &arr[prev]);
    20. QuickSort_Binary(arr, left, prev - 1);
    21. QuickSort_Binary(arr, prev + 1, right);
    22. }

    小区间优化:

    我们可以发现的是,递归像一座金字塔,越是到下面,递归次数越多,而我们通过计算得知,一颗满二叉树节点数为2^n-1,最后一层节点数为2^(n-1),也就是说,最后三层节点数占到总数的近87.5%,

    也就是说,剩余的节点小于15就不要递归了,可以使用插入排序,这个还是比较好的,插入排序参见:插入排序与希尔排序

    以Hoare大佬的排序为例:

    1. //Hoare版本 right传数组下标
    2. void QuickSort_Binary(int* arr, int left, int right)
    3. {
    4. if (left >= right)
    5. return;
    6. if(right-left+1 >= 15)
    7. {
    8. //选定一个数keyi,最好是不大也不小,上面加个三数取中
    9. int keyi = left;
    10. //快排开始的区间,都是闭区间
    11. int begin = left;
    12. int end = right;
    13. while (begin < end)
    14. {
    15. //一坑在=,若无此,循环不止
    16. //二坑在begin < end,若无此,有越界之忧,end或减减不止
    17. //三坑在要从右先行,以此保证begin与end相遇时
    18. // 二者所指处值小于keyi所指值
    19. while (begin < end && arr[end] >= arr[keyi])
    20. {
    21. end--;
    22. }
    23. while (begin < end && arr[begin] <= arr[keyi])
    24. {
    25. begin++;
    26. }
    27. Swap(&arr[end], &arr[begin]);
    28. }
    29. Swap(&arr[begin], &arr[keyi]);
    30. QuickSort_Binary(arr, left, begin - 1);
    31. QuickSort_Binary(arr, begin + 1, right);
    32. }
    33. else
    34. {
    35. InsertSort(arr,right-left+1);
    36. }
    37. }

    三数取中优化:

    再一个,如果说一个序列已然有序,我们再使用快排就很难受,此时时间复杂度直达O(N^2),所以如果我们加上三数取中,就不会出现最坏情况,但是力扣老贼针对快排,快排的三数取中我们仍要修改,改为随机数取中。

    1. int GetMidNum(int* a, int left, int right)
    2. {
    3. int mid = left + (rand() % (right - left));
    4. if (a[left] > a[mid])
    5. {
    6. if (a[mid] > a[right])
    7. {
    8. return mid;
    9. }
    10. else if (a[left] > a[right])
    11. {
    12. return right;
    13. }
    14. else
    15. {
    16. return left;
    17. }
    18. }
    19. else
    20. {
    21. if (a[left] > a[right])
    22. {
    23. return left;
    24. }
    25. else if (a[mid] > a[right])
    26. {
    27. return right;
    28. }
    29. else
    30. {
    31. return mid;
    32. }
    33. }
    34. }

    这样我们返回这个中间值坐标后,这样做:

    1. int mid = GetMidNum(arr, left, right);
    2. Swap(&arr[left], &arr[mid]);

     

    快排非递归实现:

    快排掌握递归并不够,虽然说他的空间复杂度不高,尽管我们有了上述优化,但是仍然难以保证他不会爆栈,所以掌握非递归还是很有必要的。

    快速排序的非递归类似于二叉树的前序遍历,我们在这里需要借助于栈,当然队列也可,但是这样的话就类似于二叉树的层序遍历了。

    栈和队列参考:栈和队列的实现

    二叉树的前序遍历参考:二叉树的几个递归问题

    二叉树的层序参考:二叉树的层序遍历及判断完全二叉树

    1. void QuickSortNonR(int* a, int left, int right)
    2. {
    3. Stack stack;
    4. Init(&stack);
    5. Push(&stack, right - 1);
    6. Push(&stack, left);
    7. while (!Empty(&stack))
    8. {
    9. int begin = GetTop(&stack);
    10. Pop(&stack);
    11. int end = GetTop(&stack);
    12. Pop(&stack);
    13. int mid = SortWay_two(a, begin, end);
    14. if (mid + 1 < end)
    15. {
    16. Push(&stack, end);
    17. Push(&stack, mid + 1);
    18. }
    19. if (begin < mid)
    20. {
    21. Push(&stack, mid);
    22. Push(&stack, begin);
    23. }
    24. }
    25. }

    这里注意栈的特性是先进后出。 

    快排的三路划分实现:

    在力扣的针对下,有大佬推出了这个算法,使得快排终于能够通过。

    我们的快排是大等于或小等于,而三路划分是小的在左,相等于keyi的在中间,大的在右,使得我们直接递归相等数的左边和右边就可。

    1. //快排三路划分
    2. void QuickSort_ThrDiv(int *arr,int left,int right)
    3. {
    4. if (left >= right)
    5. return;
    6. srand((unsigned int)time(NULL));
    7. int mid = GetMidNum(arr, left, right);
    8. Swap(&arr[left], &arr[mid]);
    9. int begin = left;
    10. int end = right;
    11. int keyi = arr[left];
    12. int cur = left + 1;
    13. while (cur <= right)
    14. {
    15. if (arr[cur] < keyi)
    16. {
    17. Swap(&arr[cur], &arr[left]);
    18. left++;
    19. cur++;
    20. }
    21. else if (arr[cur] > keyi)
    22. {
    23. Swap(&arr[cur], &arr[right]);
    24. right--;
    25. }
    26. else
    27. {
    28. cur++;
    29. }
    30. }
    31. QuickSort_ThrDiv(arr, begin, left - 1);
    32. QuickSort_ThrDiv(arr, right + 1, end);
    33. }

    今晚的风,吹得好浪漫~

  • 相关阅读:
    [Java反序列化]—CommonsCollections4
    第5套.py
    springboot引入redisson分布式锁及原理
    百度智能云千帆 ModelBuilder 技术实践系列:通过 SDK 快速构建并发布垂域模型
    nodejs项目实例博客管理系统
    蓝桥杯嵌入式基础模块——ADC的使用(新板)STM32G431(HAL库开发)
    HarmonyOS NEXT应用开发—Grid和List内拖拽交换子组件位置
    EN 14351-1门窗及配件—CE认证
    群体遗传学-选择消除分析
    【Java语言IntelliJ IDEA 2021.1.3 x64安装包】IntelliJ IDEA软件到期解决办法以及json文件示例
  • 原文地址:https://blog.csdn.net/m0_74824254/article/details/133469131