【恋上数据结构】快速排序

简介: 【恋上数据结构】快速排序

我的《恋上数据结构》源码(第1季 + 第2季):https://github.com/szluyu99/Data_Structure_Note

经典的十大排序算法!
在这里插入图片描述

前言

请==务必==看一下这个:排序算法前置知识+代码环境准备

当上面的内容都准备好以后,那就开始快速排序吧!

快速排序

执行流程:

  • ① 从序列中选择一个轴点元素(pivot)
    假设每次选择 0 位置的元素为轴点元素

    • ② 利用 pivot 将序列分割成 2 个子序列

    将小于 pivot 的元素放在pivot前面(左侧)
    将大于 pivot 的元素放在pivot后面(右侧)
    等于pivot的元素放哪边都可以

    • ③ 对子序列进行 ① ② 操作

    直到不能再分割(子序列中只剩下1个元素)

在这里插入图片描述
由图可见,快速排序的本质就是:

  • 逐渐将每一个元素都转换成轴点元素

轴点构造

对于序列 { 6~a~, 8~a~, 8~b~, 2, 6~b~, 4, 9, 5, 7 } 构造轴点

  • 首先将 0 位置的元素备份,作为轴点元素

在这里插入图片描述

  • 最终目标是构造出以轴点元素为中心的序列
    左半边是小于轴点元素序列
    右半边是大于轴点元素序列
    如下图:

在这里插入图片描述

那么如何构造呢?
begin 为序列首位置,

  • 从右往左扫描,寻找比轴点元素小的元素;
    找到以后,放到 begin 位置,begin++,然后反向寻找

    • 从左往右扫描,寻找比轴点元素大的元素;

找到以后,放到 end 位置,end--,然后反向寻找;

  • 当 begin == end,说明左右都扫描完了,此时将一开始备份的轴点元素放到中间

这个过程有点迷惑性,需要先从右往左找,找到以后再从左往右,不断循环这个过程,直到把所有比轴点元素小的元素放到左半部分把所有比轴点元素大的元素放到右半部分,才算完成这个轴点的构造。
在这里插入图片描述
在这里插入图片描述

构造轴点-代码实现

代码中如何实现从左往右扫描从右往左扫描的交替呢?看一眼代码就会恍然大悟。。。

/**
 * 构造出 [begin, end) 范围的轴点元素
 * @return 轴点元素的最终位置
 */
private int pivotIndex(int begin, int end){    
    // 备份begin位置的元素
    T pivot = array[begin];
    // end指向最后一个元素
    end--;
    while(begin < end){
        while(begin < end){    // 从右往左扫描
            if(cmp(pivot, array[end]) < 0){ // 右边元素 > 轴点元素
                end--;
            }else{ // 右边元素 <= 轴点元素
                array[begin++] = array[end];    
                break;
            }
        }
        while(begin < end){ // 从左往右扫描
            if(cmp(pivot, array[begin]) > 0){ // 左边元素 < 轴点元素
                begin++;
            }else{ // 左边元素 >= 轴点元素
                array[end--] = array[begin];
                break;
            }
        }
    }
    // 将轴点元素放入最终的位置
    array[begin] = pivot;
    // 返回轴点元素的位置
    return begin; // begin==end
}

构造轴点-优化

在轴点左右元素数量比较均匀的情况下,同时也是最好情况

  • T(n) = 2 ∗ T(n/2) + O(n) = O(nlogn)

如果轴点左右元素数量极度不均匀,最坏情况

  • T(n) = T(n − 1) + O(n) = O(n^2^)

为了降低最坏情况的出现概率,一般采取的做法是:

  • 随机选择轴点元素

可以在备份轴点元素前,将 begin 位置的元素与序列中的随机元素交换一下。

// 随机选择轴点元素, 将 begin 位置的元素与序列中的随机元素交换一下
swap(begin, begin + (int)Math.random()*(end - begin));

// 备份begin位置的元素
T pivot = array[begin];
.....

思考?与轴点相等的元素

如果序列中的所有元素都与轴点元素相等,利用上面的算法实现,轴点元素可以将序列分割成 2 个均匀的子序列
在这里插入图片描述

思考:把上面的代码中,cmp 位置的判断分别改为 ≤、≥ 会起到什么效果?
在这里插入图片描述
后果:

  • 轴点元素分割出来的子序列极度不均匀
  • 导致出现最坏时间复杂度 O(n^2^)

在这里插入图片描述

快速排序完整代码

/**
 * 快速排序
 */
public class QuickSort<T extends Comparable<T>> extends Sort<T> {

    @Override
    protected void sort() {
        sort(0, array.length);
    }
    /**
     * 对 [begin, end) 范围的元素进行快速排序
     */
    private void sort(int begin, int end){
        if(end - begin < 2) return;
        
        // 确定轴点位置 O(n)
        int mid = pivotIndex(begin, end);
        // 对子序列进行快速排序
        sort(begin, mid);
        sort(mid + 1, end);
    }
    /**
     * 构造出 [begin, end) 范围的轴点元素
     * @return 轴点元素的最终位置
     */
    private int pivotIndex(int begin, int end){
        // 随机选择轴点元素
        swap(begin, begin + (int)Math.random()*(end - begin));
        // 备份begin位置的元素
        T pivot = array[begin];
        // end指向最后一个元素
        end--;
        while(begin < end){
            while(begin < end){    // 从右往左扫描
                if(cmp(pivot, array[end]) < 0){ // 右边元素 > 轴点元素
                    end--;
                }else{ // 右边元素 <= 轴点元素
                    array[begin++] = array[end];    
                    break;
                }
            }
            while(begin < end){ // 从左往右扫描
                if(cmp(pivot, array[begin]) > 0){ // 左边元素 < 轴点元素
                    begin++;
                }else{ // 左边元素 >= 轴点元素
                    array[end--] = array[begin];
                    break;
                }
            }
        }
        // 将轴点元素放入最终的位置
        array[begin] = pivot;
        // 返回轴点元素的位置
        return begin; // begin==end
    }
    
}

生成 20000 个取值在[1, 10000] 的随机数进行排序:
在这里插入图片描述

复杂度与稳定性

快速排序的复杂度与稳定性

  • 最好、平均时间复杂度:O(nlogn)
  • 最坏时间复杂度:O(n^2^)
  • 由于递归调用的缘故,空间复杂度:O(logn)
  • 属于不稳定排序
相关文章
[数据结构]-玩转八大排序(二)&&冒泡排序&&快速排序
[数据结构]-玩转八大排序(二)&&冒泡排序&&快速排序
|
5天前
|
C语言
【C语言/数据结构】排序(快速排序及多种优化|递归及非递归版本)
【C语言/数据结构】排序(快速排序及多种优化|递归及非递归版本)
6 0
|
11天前
数据结构第六课 -------迭代排序(快速排序和归并排序)
数据结构第六课 -------迭代排序(快速排序和归并排序)
|
12天前
数据结构——lesson11排序之快速排序
数据结构——lesson11排序之快速排序
|
24天前
|
算法
快速排序——“数据结构与算法”
快速排序——“数据结构与算法”
|
1月前
|
算法 数据处理 C语言
【数据结构与算法】快速排序(详解:快排的Hoare原版,挖坑法和双指针法|避免快排最坏时间复杂度的两种解决方案|小区间优化|非递归的快排)
【数据结构与算法】快速排序(详解:快排的Hoare原版,挖坑法和双指针法|避免快排最坏时间复杂度的两种解决方案|小区间优化|非递归的快排)
|
2月前
|
搜索推荐 算法 编译器
【数据结构】八大排序之快速排序算法
【数据结构】八大排序之快速排序算法
40 4
|
2月前
|
算法 索引
【数据结构与算法】:非递归实现快速排序、归并排序
上篇文章我们详细讲解了递归版本的快速排序,本篇我们来探究非递归实现快速排序和归并排序
【数据结构与算法】:非递归实现快速排序、归并排序
|
2月前
|
存储 算法 搜索推荐
【数据结构与算法】:选择排序与快速排序
欢迎来到排序的第二个部分:选择排序与快速排序!
【数据结构与算法】:选择排序与快速排序
|
2月前
|
搜索推荐
数据结构——排序算法之快速排序
数据结构——排序算法之快速排序
34 0