内部排序之快速排序法

文章目录

  • 内部排序之快速排序法
    • 主要思想
    • 过程演示
    • JAVA代码
    • 算法分析
      • 时间复杂度分析
        • 最好时间复杂度
        • 最坏时间复杂度
        • 平均时间复杂度
      • 空间复杂度

对冒泡排序的一种优化

主要思想

快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。

通过一趟排序将待排记录分割成独立的两个部分,一部分的关键字比另一部分的关键字小,则可以分别对两部分记录继续进行排序。

  1. 选取任务选取的一个记录,作为枢轴或(支点)(pivot)
  2. 重新排列记录,所有关键字比pivot值小的摆放在pivot前面,所有关键字比pivot值大的摆在pivot的后面(相同的数可以到任一边)。在这个分区退出之后,该pivot就处于数列的中间位置。这个称为分区(partition)操作;
  3. 重复执行步骤2。把小于pivot关键字的子数列和大于pivot关键字的子数列排序。

过程演示

JAVA代码

package sort;public class QuickSort {private static int count = 1;public static void main(String[] args) {int[] o = {49, 38, 45, 27, 46, 13, 27, 8};System.out.print("排序前: ");for (int t : o) {System.out.print(t);System.out.print(" ");}System.out.println();// 算法部分int left = 0;int right = o.length - 1;quickSort(o, left, right);System.out.print("排序后: ");for (int t : o) {System.out.print(t);System.out.print(" ");}System.out.println();}private static void quickSort(int[] arr, int left, int right) {if (left < right) {int partitionIndex = partition(arr, left, right);quickSort(arr, left, partitionIndex - 1);quickSort(arr, partitionIndex + 1, right);} else {count++;}}private static int partition(int[] arr, int left, int right) {// left位置的记录就是pivot,index代表比pivot小的下标int index = left + 1;System.out.print("第" + count + "趟pivot=" + arr[index - 1] + ",排序后: ");for (int i = index; i <= right; i++) {if (arr[i] < arr[left]) {swap(arr, i, index);index++;}}swap(arr, left, index - 1);for (int t : arr) {System.out.print(t);System.out.print(" ");}System.out.println();count++;return index - 1;}private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}
}

执行结果

排序前: 49 38 45 27 46 13 27 8
第1趟pivot=49,排序后: 8 38 45 27 46 13 27 49
第2趟pivot=8,排序后: 8 38 45 27 46 13 27 49
第4趟pivot=38,排序后: 8 27 27 13 38 45 46 49
第5趟pivot=27,排序后: 8 13 27 27 38 45 46 49
第8趟pivot=45,排序后: 8 13 27 27 38 45 46 49
排序后: 8 13 27 27 38 45 46 49

算法分析

时间复杂度分析

经过上述一趟快速排序,我们只确定了一个元素的最终位置,我们最终需要经过 n n n趟快速排序才能将一个含有 n n n个数据元素的序列排好序,下面我们来分析其时间复杂度.

设 n n n 为待排序数组中的元素个数, T ( n ) T(n) T(n)为算法需要的时间复杂度则
T ( n ) = { D ( 1 ) n ≤ 1 D ( n ) + T ( I 1 ) + T ( I 2 ) n > 1 T(n)= \begin{cases} \vcenter D(1) &{n \leq 1} \\ D(n)+T(I_1)+T(I_2) &{n >1 } \end{cases} T(n)={D​(1)D(n)+T(I1​)+T(I2​)​n≤1n>1​
其中 D ( n ) = n − 1 D(n)=n−1 D(n)=n−1 ,是一趟快排需要的比较次数,一趟快排结束后将数组分成两部分 I 1 I_1 I1​ 和 I 2 I_2 I2​。

最好时间复杂度

最好的情况是,每一次划分都正好将数组分成长度相等的两半
T ( n ) = { D ( 1 ) n ≤ 1 D ( n ) + T ( n 2 ) + T ( n 2 ) n > 1 T(n)= \begin{cases} D(1) & {n \leq 1} \\ D(n)+T(\frac{n}{2})+T(\frac{n}{2}) &{n >1 } \end{cases} T(n)={D(1)D(n)+T(2n​)+T(2n​)​n≤1n>1​

∴ T ( n ) = D ( n ) + 2 T ( n 2 ) = D ( n ) + 2 D ( n 2 ) + 2 T ( n 4 ) . . . = D ( n ) + 2 D ( n 2 ) + 4 D ( n 4 ) + . . . + 2 k D ( n 2 k ) = n − 1 + 2 ( n 2 − 1 ) + 4 ( n 4 − 1 ) + . . . + 2 k ( n 2 k − 1 ) = ( n − 1 ) + ( n − 2 ) + ( n − 4 ) + . . . + ( n − 2 k ) ⟹ ∑ k = 1 n n − 2 k = k n − 2 k + 1 ∵ k = l o g 2 n ∴ 原式 = n l o g 2 n − 2 n + 1 ⟹ O ( n l o g 2 n ) \begin{aligned} \therefore T(n)&=D(n)+2T(\frac{n}{2}) \\ &=D(n)+2D(\frac{n}{2})+2T(\frac{n}{4}) \\ &.\\ &.\\ &.\\ &=D(n)+2D(\frac{n}{2})+4D(\frac{n}{4})+...+2^kD(\frac{n}{2^k}) \\ &=n-1+2(\frac{n}{2}-1)+4(\frac{n}{4}-1)+...+2^k(\frac{n}{2^k}-1) \\ &=(n-1)+(n-2)+(n-4)+...+(n-2^k) \\ &\implies \displaystyle \sum_{k=1}^{n} n-2^k=kn-2^k+1 \\ &\because k=log_2^n\\ &\therefore 原式=nlog_2^n-2n+1 \implies \boxed{O(nlog_2^n)}\\ \end{aligned} ∴T(n)​=D(n)+2T(2n​)=D(n)+2D(2n​)+2T(4n​)...=D(n)+2D(2n​)+4D(4n​)+...+2kD(2kn​)=n−1+2(2n​−1)+4(4n​−1)+...+2k(2kn​−1)=(n−1)+(n−2)+(n−4)+...+(n−2k)⟹k=1∑n​n−2k=kn−2k+1∵k=log2n​∴原式=nlog2n​−2n+1⟹O(nlog2n​)​​

最坏时间复杂度

最坏情况下,每一次划分都将数组分成了 0 0 0和 n − 1 n-1 n−1两部分
T ( n ) = { D ( 1 ) n ≤ 1 D ( n ) + T ( n − 1 ) + T ( 0 ) n > 1 T(n)= \begin{cases} \vcenter D(1) &{n \leq 1} \\ D(n)+T(n-1)+T(0) &{n >1 } \end{cases} T(n)={D​(1)D(n)+T(n−1)+T(0)​n≤1n>1​

∴ T ( n ) = D ( n ) + 2 T ( n − 1 ) = D ( n ) + D ( n − 1 ) + T ( n − 2 ) . . . = D ( n ) + D ( n − 1 ) + D ( n − 2 ) + . . . + D ( 1 ) = ( n − 1 ) + ( n − 2 ) + ( n − 3 ) + . . . + 0 ⟹ ∑ k = 0 n k = n ( n − 1 ) 2 原式 = n ( n − 1 ) 2 ⟹ O ( n 2 ) \begin{aligned} \therefore T(n)&=D(n)+2T(n-1) \\ &=D(n)+D(n-1)+T(n-2) \\ &.\\ &.\\ &.\\ &=D(n)+D(n-1)+D(n-2)+...+D(1) \\ &=(n-1)+(n-2)+(n-3)+...+0 \\ &\implies \displaystyle \sum_{k=0}^{n}k=\frac{n(n-1)}{2} \\ 原式&=\frac{n(n-1)}{2} \implies \boxed{O(n^2)}\\ \end{aligned} ∴T(n)原式​=D(n)+2T(n−1)=D(n)+D(n−1)+T(n−2)...=D(n)+D(n−1)+D(n−2)+...+D(1)=(n−1)+(n−2)+(n−3)+...+0⟹k=0∑n​k=2n(n−1)​=2n(n−1)​⟹O(n2)​​

平均时间复杂度

任意一种划分情况出现的概率都相等
A l l = { I 1 = 0 I 2 = n − 1 I 1 = 1 I 2 = n − 2 . . . I 1 = n − 1 I 2 = 0 All= \begin{cases} I_1=0 & I_2=n-1 \\ I_1=1 & I_2=n-2 \\ . \\ . \\ . \\ I_1=n-1 & I_2=0 \\ \end{cases} All=⎩ ⎨ ⎧​I1​=0I1​=1...I1​=n−1​I2​=n−1I2​=n−2I2​=0​

∴ T a v g ( n ) = D ( n ) + 1 n ∗ ∑ i = 0 n − 1 T a v g ( i ) + T a v g ( n − i ) = D ( n ) + 2 n ∗ ∑ i = 0 n − 1 T a v g ( i ) T a v g ( n − 1 ) = D ( n − 1 ) + 2 n − 1 ∗ ∑ i = 0 n − 2 T a v g ( i ) n T a v g ( n ) − ( n − 1 ) T a v g ( n − 1 ) = n D ( n ) + 2 ∑ i = 0 n − 1 T a v g ( i ) − ( n − 1 ) D ( n − 1 ) − 2 ∗ ∑ i = 0 n − 2 T a v g ( i ) = n D ( n ) − ( n − 1 ) D ( n − 1 ) + 2 T a v g ( n − 1 ) = n ( n − 1 ) − ( n − 1 ) ( n − 2 ) + 2 T a v g ( n − 1 ) = 2 ( n − 1 ) + 2 T a v g ( n − 1 ) 移项得 n T a v g ( n ) = 2 ( n + 1 ) + 2 T a v g ( n − 1 ) + ( n − 1 ) 2 T a v g ( n − 1 ) = 2 ( n + 1 ) + ( n + 1 ) 2 T a v g ( n − 1 ) 等式两边同时除以 n ( n + 1 ) 得 T a v g ( n ) n + 1 = T a v g ( n − 1 ) n + 2 ( n − 1 ) n ( n + 1 ) 令 B ( n ) = T a v g ( n ) n + 1 得 B ( n ) = B ( n − 1 ) + 2 ( n − 1 ) n ( n + 1 ) = B ( n − 2 ) + 2 ( n − 2 ) n ( n − 1 ) + 2 ( n − 1 ) n ( n + 1 ) . . . = B ( 1 ) + ∑ i = 1 n 2 ( i − 1 ) i ( i + 1 ) = B ( 1 ) + ∑ i = 1 n 2 ( i + 1 ) − 4 i ( i + 1 ) = ∑ i = 1 n [ 2 i − 4 i ( i + 1 ) ] ∵ ∑ i = 1 n 4 i ( i + 1 ) = 4 ∑ i = 1 n [ i + 1 − i i ( i + 1 ) ] = 4 ∑ i = 1 n [ 1 i − 1 i ( i + 1 ) ] = 4 [ 1 − 1 n + 1 ] = 4 n n + 1 ∵ ∑ i = 1 n 1 i ≈ 0.577216+ln(n) ∴ B ( n ) = 2 ∗ 0.577216 + 2 ∗ l n ( n ) + 4 n n + 1 代回 B ( n ) = T a v g ( n ) n + 1 T a v g ( n ) = 2 ( n + 1 ) l n ( n ) − 4 n + 2 ∗ 0.577216 ( n + 1 ) ⟹ O ( n l o g 2 n ) \begin{aligned} \therefore T_{avg}(n)&=D(n)+\frac{1}{n}*\displaystyle \sum_{i=0}^{n-1}T_{avg}(i)+T_{avg}(n-i) \\ &=D(n)+\frac{2}{n}*\displaystyle \sum_{i=0}^{n-1}T_{avg}(i) \\ T_{avg}(n-1)&=D(n-1)+\frac{2}{n-1}*\displaystyle \sum_{i=0}^{n-2}T_{avg}(i)\\ nT_{avg}(n)-(n-1)T_{avg}(n-1)&= nD(n)+2\displaystyle \sum_{i=0}^{n-1}T_{avg}(i)-(n-1)D(n-1)-2*\displaystyle \sum_{i=0}^{n-2}T_{avg}(i) \\ &=nD(n)-(n-1)D(n-1)+2T_{avg}(n-1) \\ &=n(n-1)-(n-1)(n-2)+2T_{avg}(n-1) \\ &=2(n-1)+2T_{avg}(n-1) \\ 移项得 \\ nT_{avg}(n) &= 2(n+1)+2T_{avg}(n-1)+(n-1)2T_{avg}(n-1) \\ &= 2(n+1)+(n+1)2T_{avg}(n-1) \\ 等式两边同时除以n(n+1)得 \\ \frac{T_{avg}(n)}{n+1}&=\frac{T_{avg}(n-1)}{n}+\frac{2(n-1)}{n(n+1)} \\ 令B(n)=\frac{T_{avg}(n)}{n+1} 得\\ B(n)&=B(n-1)+\frac{2(n-1)}{n(n+1)} \\ &=\boxed{B(n-2)+\frac{2(n-2)}{n(n-1)}}+\frac{2(n-1)}{n(n+1)} \\ .\\ .\\ .\\ &=B(1)+\displaystyle \sum_{i=1}^{n}\frac{2(i-1)}{i(i+1)} \\ &=B(1)+\displaystyle \sum_{i=1}^{n}\frac{2(i+1)-4}{i(i+1)} \\ &=\displaystyle \sum_{i=1}^{n}[\frac{2}{i}-\frac{4}{i(i+1)}] \\ \because \displaystyle \sum_{i=1}^{n}\frac{4}{i(i+1)}&= 4\displaystyle \sum_{i=1}^{n}[\frac{i+1-i}{i(i+1)}] \\ &= 4\displaystyle \sum_{i=1}^{n}[\frac{1}{i}-\frac{1}{i(i+1)}] \\ &= 4[1-\frac{1}{n+1}]=\boxed{4\frac{n}{n+1}}\\ \because \displaystyle \sum_{i=1}^{n}\frac{1}{i}&\approx \colorbox{aqua}{0.577216+ln(n)} \\ \therefore B(n)&=2*0.577216+2*ln(n)+4\frac{n}{n+1} \\ 代回B(n)=\frac{T_{avg}(n)}{n+1} \\ T_{avg}(n)&=2(n+1)ln(n)-4n+2*0.577216(n+1) \implies \boxed{O(nlog_2^n)} \\ \end{aligned} ∴Tavg​(n)Tavg​(n−1)nTavg​(n)−(n−1)Tavg​(n−1)移项得nTavg​(n)等式两边同时除以n(n+1)得n+1Tavg​(n)​令B(n)=n+1Tavg​(n)​得B(n)...∵i=1∑n​i(i+1)4​∵i=1∑n​i1​∴B(n)代回B(n)=n+1Tavg​(n)​Tavg​(n)​=D(n)+n1​∗i=0∑n−1​Tavg​(i)+Tavg​(n−i)=D(n)+n2​∗i=0∑n−1​Tavg​(i)=D(n−1)+n−12​∗i=0∑n−2​Tavg​(i)=nD(n)+2i=0∑n−1​Tavg​(i)−(n−1)D(n−1)−2∗i=0∑n−2​Tavg​(i)=nD(n)−(n−1)D(n−1)+2Tavg​(n−1)=n(n−1)−(n−1)(n−2)+2Tavg​(n−1)=2(n−1)+2Tavg​(n−1)=2(n+1)+2Tavg​(n−1)+(n−1)2Tavg​(n−1)=2(n+1)+(n+1)2Tavg​(n−1)=nTavg​(n−1)​+n(n+1)2(n−1)​=B(n−1)+n(n+1)2(n−1)​=B(n−2)+n(n−1)2(n−2)​​+n(n+1)2(n−1)​=B(1)+i=1∑n​i(i+1)2(i−1)​=B(1)+i=1∑n​i(i+1)2(i+1)−4​=i=1∑n​[i2​−i(i+1)4​]=4i=1∑n​[i(i+1)i+1−i​]=4i=1∑n​[i1​−i(i+1)1​]=4[1−n+11​]=4n+1n​​≈0.577216+ln(n)​=2∗0.577216+2∗ln(n)+4n+1n​=2(n+1)ln(n)−4n+2∗0.577216(n+1)⟹O(nlog2n​)​​

空间复杂度

快速排序的空间复杂度取决于分划基准的选择,每次都选在中间, O ( l o g 2 n ) O(log_2^n) O(log2n​)若基本有序,退化为冒泡,栈的深度 O ( n ) O(n) O(n)。

O ( l o g 2 n ) O(log_2^n) O(log2n​)就是递归的深度,递归的时候使用的栈空间稍微优化一点的快排,比如取首尾中三个数据,取其中间的值作为划分标准,不会出现退化到冒泡的可能。

计算参考:https://zhuanlan.zhihu.com/p/341201904

【重温基础算法】内部排序之快速排序法相关推荐

  1. AcWing 算法基础课第一节基础算法1排序、二分

    1.该系列为ACWing中算法基础课,已购买正版,课程作者为yxc 2.y总培训真的是业界良心,大家有时间可以报一下 3.为啥写在这儿,问就是oneNote的内存不够了QAQ ACwing C++ 算 ...

  2. 算法 - 内部排序方法总结

    分享一个大牛的人工智能教程.零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请点击http://www.captainbed.net 各种排序方法的性能比较 排序方法 最好时间复杂度 平 ...

  3. C语言排序算法 选择排序 插入排序 快速排序 qsort实现快排 堆排序

    常见排序算法 选择排序 选择排序(Selection sort)是一种简单直观的排序算法. 它的工作原理如下. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素 ...

  4. 数据结构与算法-基础算法篇-排序(归并排序、快速排序)

    3. 归并排序.快速排序 1. 分治思想 分治,顾明思意,就是分而治之,将一个大问题分解成小的子问题来解决,小的子问题解决了,大问题也就解决了. 分治与递归的区别:分治算法一般都用递归来实现的.分治是 ...

  5. 保研机试——1基础算法(排序、哈希、模拟(日期、图形、查找、进制、字符串)、递归与分治、贪心)

    写在前面的话:笔者在大三上学期(2022.9.20)对刷算法题基本为0基础,通过博客记录自己的学习过程,本人的学习计划为: 1.大三上学期:首先看<王道计算机机试考研指南>,着重看看保研机 ...

  6. 数据结构排序算法 内部排序(冒泡、鸡尾酒、选择、简单插入、二分插入、快排、希尔、归并、堆排)C语言实现

    文章目录 排序 冒泡排序 鸡尾酒排序 选择排序: 简单插入排序: 二分插入排序 快速排序: 希尔排序: 归并排序: 堆排序: 排序 点击以下图片查看大图: 冒泡排序 1.比较相邻的元素,如果前一个比后 ...

  7. 算法——计数排序与快速排序

    计数排序是一种算法复杂度 O(n) 的排序方法,适合于小范围集合的排序.比如100万学生参加高考,我们想对这100万学生的数学成绩(假设分数为0到100)做个排序.我们如何设计一个 最高效的排序算法. ...

  8. 【重温经典算法之二】快速排序

    快速排序的思想与归并排序思想类似,都是采用分治法的思想.将一个数组A[l...r]使用快速排序可以分解为三个主要的步骤: 通过随机算法获得数组A中的一个下标k,将A[k]与A[r]交换. 将数组分解成 ...

  9. 常用排序之快速排序法

    通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列. ...

最新文章

  1. 数据结构(05)— 线性单链表实战
  2. CISCO-生成树-安全保护配置
  3. android context.java_Android / Java类范围和Context
  4. wamp环境搭建到mysql就不成功_Wamp环境搭建常见错误问题解决
  5. 作者:​郭海红(1987-),女,中国医学科学院医学信息研究所助理研究员。...
  6. Silverlight+WCF 新手实例 象棋 棋子定位与象棋类(四)
  7. 调整地面材质_【C4DtoA 13】Arnold渲染器(材质七): Shadow Matte Shader
  8. plc和pc串口通讯接线_PC与PLC的串口通信及编程实现
  9. 20191019:(leetcode习题)第K个语法符号
  10. Azure SQL作業
  11. 24.docker port
  12. python编译型语言和解释型语言的区别_编译型语言和解释型语言的区别?
  13. python 进化树_进化树专题(七)| 进化树与不完全谱系分选
  14. 关闭自动降频 linux,iPhone如何关闭降频?iPhone手动关闭降频方法[多图]
  15. JavaScript:异步简介与Promise实践拓展
  16. mysql 定时任务 每月15号执行
  17. 关于巴伦——Marchand巴伦
  18. 1480_人月神话阅读笔记_开篇
  19. 深度学习笔记之稀疏自编码器
  20. git提交的时候出现异常“bad object HEAD”的另一种解决方案

热门文章

  1. Unity UWA内存优化总结列表
  2. 最短路标号法python_例题 最短路的标号法 -
  3. 浅谈NFT抵押借贷的三种模式:点对点、资金池和中心化模式
  4. python使用smtplib和email发送腾讯企业邮箱邮件
  5. 蒲慕明院士PNAS最新研究:神经元数量在共同激活诱导大脑神经元兴奋性增强的重要性...
  6. 基于SpringBoot、RabbitMQ的Android消息推送平台搭建
  7. Flutter 笔记 | Flutter 核心原理(一)架构和生命周期
  8. c字符串、string对象、字符串字面值的区别
  9. 关于iOS内购常见的避审方法
  10. 用友T6库存管理中,入库单保存后在单据列表里找不到此单据