排序算法——关于快速排序的详解

目录

1.基本思想

2.基本原理

2.1划分思想

2.2排序过程

(1)选择基准值

(2)分割过程(Partition)

(3)递归排序

(4)合并过程

2.3具体实例

2.4实现代码

2.5关键要点

3.性能分析

3.1空间效率

3.2时间效率

3.3稳定性


1.基本思想

快速排序(Quick Sort)是一种常用的排序算法,它采用分治法的思想,通过递归地将数据分解成小于基准值和大于基准值的两部分,然后对这两部分进行排序,最终将它们合并起来。

2.基本原理

2.1划分思想

在待排序表Ll...n中任取一个元素pivot作为枢轴(或称基准,通常取首元素),通过一趟排序将待排序表划分为独立的两部分Ll...k-1和Lk+l...n,使得Ll...k-l中的所有元素小于pivot;Lk+l...n中的所有元素大于或等于pivot,则pivot放在了其最终位置L(k)上,这个过程称为一次划分。

2.2排序过程

(1)选择基准值

从待排序数组中选择一个元素作为基准值。通常选择第一个元素,但也可以选择随机元素或数组中间的元素。

(2)分割过程(Partition)

将数组按照基准值进行分割,将小于等于基准值的元素放在基准值的左侧,大于基准值的元素放在右侧。同时,基准值所在的位置被确定,这个位置之前的元素都小于等于基准值,之后的元素都大于基准值。这一过程可以使用双指针法来实现。

(3)递归排序

对基准值左侧和右侧的子数组分别进行递归排序。即对左侧子数组和右侧子数组分别重复步骤1和步骤2。

(4)合并过程

递归排序完成后,整个数组已经被拆分成若干有序的子数组,只需简单地将这些子数组合并即可得到最终的有序数组。

2.3具体实例

一趟快速排序的过程是一个交替搜索交换的过程,下面通过实例来介绍

*来自2024版王道数据结构考研复习指导

设两个指针i和j,初值分别为low和high,取第一个元素49为枢轴赋值到变量pivot。

对算法的最好理解方式是手动地模拟一遍这些算法。

2.4实现代码

cpp 复制代码
void Quicksort(ElemType A[], int low, int high) {
    if (low < high) {
        //递归跳出的条件
        //Partition()就是划分操作,将表A [low...high]划分为满足上述条件的两个子表
        int pivotpos = Partition(A, low, high);
        //划分
        Quicksort(A, low, pivotpos - 1);
        //依次对两个子表进行递归排序
        Quicksort(A, pivotpos + 1, high);
    }
}
cpp 复制代码
int Partition(ElemType A[], int low, int high) {
    //一趟划分
    ElemType pivot = A[low]; //将当前表中第一个元素设为枢轴,对表进行划分
    while (low < high) {
        //循环跳出条件
        while (low < high && A[high] >= pivot)
            --high;
        A[low] = A[high];
        //将比枢轴小的元素移动到左端
        while (low < high && A[low] < pivot)
            ++low;
        A[high] = A[low];
        //将比枢轴大的元素移动到右端
    }
    A[low] = pivot;
    //枢轴元素存放到最终位置
    return low;
    //返回存放枢轴的最终位置
}

2.5关键要点

(1)基准值的选择:选择待排序数组中的一个元素作为基准值。通常情况下选择第一个元素,但也可以采用其他策略,如随机选择。

(2)分割过程:将数组分割成两个子数组,一个包含小于等于基准值的元素,另一个包含大于基准值的元素。这个过程通常被称为分区(partition)。

(3)递归排序:对分割得到的两个子数组递归地应用快速排序算法。这是分治策略的关键,通过不断递归排序,最终实现整个数组的排序。

(4)合并过程:将排好序的子数组与基准值合并起来,形成最终的有序数组。

(5)终止条件:当子数组的长度为1或0时,不再进行递归,因为长度为1或0的数组被认为是有序的。

(6)不稳定性:快速排序是一种不稳定的排序算法,即相等元素的相对位置可能在排序前后发生变化。

(7)空间复杂度:快速排序是原地排序算法,不需要额外的存储空间,只需要在递归调用时保持一些辅助变量。

(8)平均时间复杂度:快速排序的平均时间复杂度为O(n log n),其中n是数组的长度。最坏情况下为O(n^2),但在实际应用中,快速排序通常表现良好。

3.性能分析

3.1空间效率

由于快速排序是递归的,需要借助一个递归工作栈来保存每层递归调用的必要信息,其容量与递归调用的最大深度一致。最好情况下为O(log2n);最坏情况下,因为要进行n-1次递归调用,所以栈的深度为O(n);平均情况下,栈的深度为O(log2n)。

3.2时间效率

快速排序的运行时间与划分是否对称有关,快速排序的最坏情况发生在两个区域分别包含n-1个元素和0个元素时,这种最大限度的不对称性若发生在每层递归上,即对应于初始排序表基本有序或基本逆序时,就得到最坏情况下的时间复杂度为0(n^2)。

快速排序是所有内部排序算法中平均性能最优的排序算法

3.3稳定性

在划分算法中,若右端区间有两个关键字相同,且均小于基准值的记录,则在交换到左端区间后,它们的相对位置会发生变化,即快速排序是一种不稳定的排序方法。例如,表L={3,2,2},经过一趟排序后L={2,2,3},最终排序序列也是L={2,2,3),显然,2与2的相对次序己发生了变化。

相关推荐
云絮.9 小时前
数据库操作
数据库·mysql·算法·oracle
小林ixn9 小时前
LeetCode 206. 反转链表(迭代 + 递归详解)
算法·leetcode·链表
凡人叶枫9 小时前
Effective C++ 条款17:以独立语句将 newed 对象置入智能指针
java·linux·开发语言·c++·算法
菜鸟‍11 小时前
LeetCode 1 27 和 704 || 两数之和 移除元素 二分查找
算法·leetcode·职场和发展
退休倒计时12 小时前
【每日一题】LeetCode 142. 环形链表 II TypeScript
算法·leetcode·链表·typescript
popcorn_min12 小时前
Digits 手写数字识别:随机森林多分类 + 像素级特征热力图
算法·随机森林·分类
liulilittle13 小时前
拥塞控制:排水终止的两种决策:OR 与 AND
网络·tcp/ip·计算机网络·算法·信息与通信·tcp·通信
weixin_3077791313 小时前
从脚本执行到智能体协作:AI辅助测试能力的范式重构
运维·开发语言·人工智能·算法·测试用例
量化君也13 小时前
从回测到全自动实盘交易,全天候策略需要经历哪些改造?
大数据·人工智能·python·算法·金融
fox_lht14 小时前
第十五章 函数式语言:迭代器和闭包
开发语言·后端·学习·算法·rust