• 超级哇塞的快排,你值得学会!


    今天我们要讲的是快速排序,非常的哇塞!

    简单了解一下快排------

    下面介绍快排的三个方法,未优化和优化后的快排

    First----hoare版本
     

    也就是说先确立的一个下标为key的值为对比值,普遍可以认为左边第一个值就key值,然后,从右边发出,寻找比key值小的数字,当R停下时,L才动,然后找到比key大的值,然后进行L、R的交换,最终他们一定会相遇,相遇的下标对应的值就将key赋值给他,然后再分而治之,

    [left,key-1]          key      [key+1,right]    就是一样一个递归

    数据结构一定要学会画图来思考问题,让问题更加地清晰明了!

     

     

     完整代码:

    1. //hoare版本
    2. void QuickSort(int* arr, int begin, int end)
    3. {
    4. //进来判断一下
    5. if (begin >= end)
    6. {
    7. return;
    8. }
    9. int left = begin;
    10. int right = end;
    11. int key = left;
    12. while (left < right)
    13. {
    14. //先走右边
    15. while (left < right && arr[right] >= arr[key])
    16. {
    17. --right;
    18. }
    19. while (left < right && arr[left] <= arr[key])
    20. {
    21. ++left;
    22. }
    23. //交换
    24. Swap(&arr[left], &arr[right]);
    25. }
    26. //最后相遇了
    27. Swap(&arr[key], &arr[left]);
    28. key = left;//更新key的位置
    29. QuickSort(arr, begin, key - 1);
    30. QuickSort(arr, key + 1, end);
    31. }
    32. void PrintSort(int* arr, int len)
    33. {
    34. for (int i = 0; i < len; i++)
    35. {
    36. printf("%d ", arr[i]);
    37. }
    38. }
    39. int main()
    40. {
    41. int arr[] = { 3,1,2,4,9,5,6};
    42. int len = sizeof(arr) / sizeof(arr[0]);
    43. QuickSort(arr,0, len-1);
    44. PrintSort(arr, len);
    45. }

    下面介绍另外一种方法,被大神们优化一下, 比较好理解!

    2、挖坑法-------顾名思义就是有一个坑

     

    完整代码:

    //挖坑法
    void QuickSort(int* arr, int begin, int end)
    {
        if (begin >= end)
        {
            return;
        }
        int left = begin;
        int right = end;
        int key = arr[left];
        int piti =left;//坑位
        while (left < right)//控制范围
        {
            while (left < right && arr[right] >= key)
            {
                --right;
            }
            arr[piti] = arr[right];
            piti = right;
            while (left < right && arr[left] <= key)
            {
                ++left;
            }
            arr[piti] = arr[left];
            piti = left;
        }
        //还有最后相遇的一个坑位
        arr[piti] = key;
        QuickSort(arr, begin, piti - 1);
        QuickSort(arr, piti + 1, end);
    }


    void PrintSort(int* arr, int len)
    {
        for (int i = 0; i < len; i++)
        {
            printf("%d ", arr[i]);
        }
    }
    int main()
    {
        int arr[] = { 3,1,2,4,9,5,6};
        int len = sizeof(arr) / sizeof(arr[0]);
        QuickSort(arr,0, len-1);    
        
        PrintSort(arr, len);
    }

    第三种方法:前后指针版本

     

    完整代码:

    1. //前后指针版本
    2. void QuickSort(int* arr, int begin, int end)
    3. {
    4. if (begin >= end)
    5. {
    6. return;
    7. }
    8. int prev = begin;
    9. int cur = begin + 1;
    10. int key = begin;
    11. while (cur <= end)
    12. {
    13. if (arr[cur] < arr[key] && ++prev != cur)
    14. {
    15. Swap(&arr[cur], &arr[prev]);
    16. }
    17. ++cur;
    18. }
    19. //最后只剩下一个prev
    20. Swap(&arr[prev], &arr[begin]);
    21. key = prev;
    22. QuickSort(arr, begin, key - 1);
    23. QuickSort(arr, key + 1, end);
    24. }
    25. void PrintSort(int* arr, int len)
    26. {
    27. for (int i = 0; i < len; i++)
    28. {
    29. printf("%d ", arr[i]);
    30. }
    31. }
    32. int main()
    33. {
    34. int arr[] = { 3,1,2,4,9,5,6};
    35. int len = sizeof(arr) / sizeof(arr[0]);
    36. QuickSort(arr,0, len-1);
    37. PrintSort(arr, len);
    38. }

     相信你学会这三种快排的方法不算难事,但是那都是没进行过优化的,接下来让我们来优化一下吧!前面三种方法的时间复杂度都是O(N),在效率上并没有提升太多

    优化方法—:三数取中(在前后指针版本上面改善)

    影响效率的障碍就是key,要不每次key都是最小,要        不key每次都是最大,那么效率肯定就上不去,如果我们让key是区域内的middle 那么效率更上一层了,所以三数取中。

    1. //前后指针版本
    2. void QuickSort(int* arr, int begin, int end)
    3. {
    4. if (begin >= end)
    5. {
    6. return;
    7. }
    8. int prev = begin;
    9. int cur = begin + 1;
    10. int key = begin;
    11. //三数取中
    12. int mid = GetMidIndex(arr, begin, end);
    13. Swap(&arr[key], &arr[mid]);
    14. while (cur <= end)
    15. {
    16. if (arr[cur] < arr[key] && ++prev != cur)
    17. {
    18. Swap(&arr[cur], &arr[prev]);
    19. }
    20. ++cur;
    21. }
    22. //最后只剩下一个prev
    23. Swap(&arr[prev], &arr[begin]);
    24. key = prev;
    25. QuickSort(arr, begin, key - 1);
    26. QuickSort(arr, key + 1, end);
    27. }

    优化方法二:递归到小的子区间时,可以考虑使用插入排序

    因为在数据有序或者接近有序的时候,插入排序的效率非常可观,至于为什么不用希尔,堆,是因为在大数据下用他们比较快,但是接近有序的时间复杂度插入排序更胜一筹!

     

     

    这些数据划分区域就知道最后接近有序的递归次数就占了一半,所以说非常的庞大。

    区域优化代码:
     

     这里的插入排序的arr如果不加begin的话,那么每次都是最前面的20个数进行排序,那么无意义,arr+begin就是不同区域内的begin----end

    这个优化就非常的爽了!

    这些快排都是运用了递归的方式,递归也是一定的劣势,所以我们也必须要掌握非递归的方式

    非递归我们运用的是栈完成快排

    栈的性质是先进后出,我们可以完美地运用这个性质!

    思路:

     这完全就像是树的工作方式,先完成左树的排序,然后再进行右树的排序,穷尽到最后一个区域,最后再销毁一下栈就可以实现非递归的快排了

    更形象一点:当然用队列也是一样的,运用好队列的性质就行好,控制好

     

    因为这是纯C所以要先手搓一个栈,到C++就不用这么麻烦了,

    非递归完整代码:

    1. //栈
    2. typedef struct Stack
    3. {
    4. int* arr;
    5. int top;
    6. int capacity;
    7. }Stack;
    8. void InitStack(Stack* ps)
    9. {
    10. ps->arr = NULL;
    11. ps->top = ps->capacity = 0;
    12. }
    13. void StackPush(Stack* ps,int x)
    14. {
    15. //检测是否满了
    16. if (ps->top == ps->capacity)
    17. {
    18. int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
    19. int tmp = (int *)realloc(ps->arr,sizeof(int )*newcapacity);
    20. ps->arr = tmp;
    21. ps->capacity = newcapacity;
    22. }
    23. ps->arr[ps->top] = x;
    24. ps->top++;
    25. }
    26. void StackPop(Stack* ps)
    27. {
    28. ps->top--;
    29. }
    30. void DestoryStack(Stack* ps)
    31. {
    32. ps->arr = NULL;
    33. ps->top = ps->capacity = 0;
    34. }
    35. int StackTop(Stack* ps)
    36. {
    37. return ps->arr[ps->top-1];
    38. }
    39. bool StackEmpty(Stack* ps)
    40. {
    41. return ps->top==0;
    42. }
    43. //栈来实现非递归的快排
    44. void QuickSort(int* arr, int begin, int end)
    45. {
    46. Stack st;
    47. InitStack(&st);
    48. StackPush(&st, end);
    49. StackPush(&st, begin);
    50. while (!StackEmpty(&st))
    51. {
    52. int left = StackTop(&st);
    53. StackPop(&st);
    54. int right = StackTop(&st);
    55. StackPop(&st);
    56. int keyi = QuickSort2(arr, left, right);
    57. if (left < keyi - 1)
    58. {
    59. StackPush(&st, keyi - 1);
    60. StackPush(&st, left);
    61. }
    62. if (keyi + 1 < right)
    63. {
    64. StackPush(&st, right);
    65. StackPush(&st, keyi + 1);
    66. }
    67. }
    68. DestoryStack(&st);
    69. }
    70. void PrintSort(int* arr, int len)
    71. {
    72. for (int i = 0; i < len; i++)
    73. {
    74. printf("%d ", arr[i]);
    75. }
    76. }
    77. int main()
    78. {
    79. int arr[] = { 3,1,2,4,9,5,6};
    80. int len = sizeof(arr) / sizeof(arr[0]);
    81. QuickSort(arr,0,len-1);
    82. PrintSort(arr, len);
    83. }

    伙伴们!快来试一下吧!!

    如有错误,请指出!

  • 相关阅读:
    day15_集合
    SQL存储引擎
    函数及其应用(2)——湖南工商大学
    WPF使用MVVM(三)-事件转命令
    CentOS基Docker容器时区配置解决方案
    随便记记第二周
    小米6安装Ubuntu Touch系统也不是很难嘛
    硬件设计基础----三极管
    leetCode 第44天 | 518. 零钱兑换 II 377. 组合总和 Ⅳ 动态规划
    AgileConfig 1.8.0 已适配 .NET8
  • 原文地址:https://blog.csdn.net/qq_61342044/article/details/125579065