【数据结构】排序算法——快速排序

谁借莪1个温暖的怀抱¢ 2022-05-25 11:13 306阅读 0赞

  快速排排序是效率非常高的排序算法之一。
  它的基本思想是:首先选择一个基准值,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都小于基准值,另一部分所有数据都大于基准值,并且经过一趟排序,所选择基准值已经换到了在它应该在的正确位置。然后再通过此方法堆这两部分数据分别进行快速排序,整个排序过程可以递归实现。但是具体的将待排序的数据分为两个部分的方法,却有很多:
  
举一个例子:
































数组下标 0 1 2 3 4 5 6 7 8 9
待排序数据 7 1 4 8 2 0 9 6 3 5

我们取最后一个元素5为基准值key
第一种方法:

  1. 定义两个指针,begin和end分别指向第一个元素和最后一个元素,基准值key=arr[end];
  2. begin从前向后移动,当遇到大于key的时候停下来,end从后向前移动,当遇到小于key的时候停下,交换begin和end对应的元素;
  3. 重复上一步,直至begin与end相遇,begin对应的元素与key值进行交换。
    这里写图片描述
    此方法的实现代码如下:

    size_t Pation1(int *arr, int left, int right)
    {

    1. size_t begin = left;
    2. size_t end = right - 1;
    3. int key = arr[end];
    4. while (begin < end)
    5. {
    6. while (begin < end && arr[begin] <= key)
    7. begin++;
    8. while (begin < end && arr[end] >= key)
    9. end--;
    10. if (begin < end)
    11. swap(arr[begin], arr[end]);
    12. }
    13. if (begin != right-1)// 1 2 4 5 6
    14. swap(arr[begin], arr[right-1]);
    15. return begin;

    }
    void QuickSort(int *arr, int left, int right)
    {

    1. if (left < right)
    2. {
    3. size_t div = Pation1(arr, left, right);
    4. QuickSort(arr, left, div);
    5. QuickSort(arr, div + 1, right);
    6. }

    }

第二种方法:挖坑法

  1. 定义两个指针,begin和end分别指向第一个元素和最后一个元素,基准值key=arr[end];
  2. begin从前向后移动,当遇到大于key的时候停下来,将begin位置的数据放置end处,begin变为坑;
  3. end从后开始向前移动,当遇到小于key的时候停下来,将end处的数据放置坑(begin)处,end变为坑,begin开始移动;
  4. 重复2、3两步,直至begin与end相遇,最后的一个坑放key。
    这里写图片描述

此方法的实现代码如下:

  1. size_t Pation2(int *arr, size_t left, size_t right)
  2. {
  3. size_t begin = left;
  4. size_t end = right - 1;
  5. int key = arr[end];
  6. while (begin < end)
  7. {
  8. while (begin < end && arr[begin] <= key)
  9. begin++;
  10. if (begin < end)
  11. arr[end--] = arr[begin];
  12. while (begin < end && arr[end] >= key)
  13. end--;
  14. if (begin < end)
  15. arr[begin++] = arr[end];
  16. }
  17. if (begin != right-1)
  18. arr[begin] = key;//1 2 3 4 5 6
  19. return begin;
  20. }
  21. void QuickSort(int *arr, int left, int right)
  22. {
  23. if (left < right)
  24. {
  25. size_t div = Pation2(arr, left, right);
  26. QuickSort(arr, left, div);
  27. QuickSort(arr, div + 1, right);
  28. }
  29. }

第三种方法:追赶法

  1. 定义两个指针,cur和pre分别指向第一个元素和-1,基准值key=arr[end];
  2. cur从前向后移动,当遇到大于key的时候,cur++;当遇到小于key的时候,++pre后,如果pre与cur不在同一位置,则交换两个数据;
  3. 重复第2步,直至cur不小于right边界,再++pre,如果pre不与right在同一个位置,那么交换两个数据。

如下图所示:
这里写图片描述
这里写图片描述
此方法的实现代码如下:

  1. size_t Pation3(int *arr, size_t left, size_t right)
  2. {
  3. int index = GetMid(arr, left, right);
  4. if (index != right - 1)
  5. swap(arr[index], arr[right - 1]);
  6. int key = arr[right - 1];
  7. int cur = left;
  8. int pre = cur - 1;
  9. while (cur < right)
  10. {
  11. if (arr[cur] < key && ++pre != cur)
  12. swap(arr[pre], arr[cur]);
  13. cur++;
  14. }
  15. if (++pre != right)
  16. swap(arr[pre], arr[right-1]);
  17. return pre;
  18. }
  19. void QuickSort(int *arr, int left, int right)
  20. {
  21. if (left < right)
  22. {
  23. size_t div = Pation3(arr, left, right);
  24. QuickSort(arr, left, div);
  25. QuickSort(arr, div + 1, right);
  26. }
  27. }

以上实现的快速排序是递归的,递归也是可以转换成循环,我们利用循环+栈来实现,非递归的快排。

  1. #include <stack>
  2. void QuickSort(int *arr, size_t size)
  3. {
  4. int left = 0;
  5. int right = size;
  6. stack<int> s;
  7. s.push(right);
  8. s.push(left);
  9. while (!s.empty())
  10. {
  11. left = s.top();
  12. s.pop();
  13. right = s.top();
  14. s.pop();
  15. if (left < right)
  16. {
  17. size_t div = Pation3(arr, left, right);
  18. s.push(div);//保存基准值左边数据的边界
  19. s.push(left);
  20. s.push(right);//保存基准值右边的数据的边界
  21. s.push(div + 1);
  22. }
  23. }
  24. }

优化

  快速排序时间复杂度一般为O(N*logN),最坏为O(N*N),快速排序的性能取决于递归树的深度,在最优的情况下,递归树深度为logN,最坏的情况下,递归深度为N。所以,基准值key的选择至关重要,如果选择的不合适,那么递归深度将会变大,从而造成效率下降。
  对此,我们的解决方法是:三数取中法,顾名思义,就是在待排序数组中的首元素、中间元素、最后一个元素,这三个数据中选择中间大小的数据。
  
代码如下:

  1. size_t GetMid(int *arr, size_t left, size_t right)
  2. {
  3. size_t mid = left + ((right - left) >> 1);
  4. if (arr[left] < arr[right-1])
  5. {
  6. if (arr[left] > arr[mid])
  7. return left;
  8. else if (arr[right - 1] < arr[mid])
  9. return right - 1;
  10. else
  11. return mid;
  12. }
  13. else
  14. {
  15. if (arr[left] < arr[mid])
  16. return left;
  17. else if (arr[right - 1] > arr[mid])
  18. return right - 1;
  19. else
  20. return mid;
  21. }
  22. }

发表评论

表情:
评论列表 (有 0 条评论,306人围观)

还没有评论,来说两句吧...

相关阅读

    相关 数据结构-排序-快速排序

    快速排序 快速排序法又称分割交换排序法,是目前公认的速度最快的排序法,该方法是现在数据中找到一个虚拟的中间值,然后将大于这个中间值的放到右边,小于中间值的放到左边,然后将