七大排序算法——快速排序,通俗易懂的思路讲解与图解(完整Java代码)

文章目录

  • 一、排序的概念
    • 排序的概念
    • 排序的稳定性
    • 七大排序算法
  • 二、快速排序
    • 核心思想
    • Hoare法
    • 挖坑法
    • 前后指针法(选学)
  • 三、性能分析
  • 四、算法优化
    • 优化基准的选取
    • 优化少量数据时的排序方案
    • 优化后的完整代码
  • 五、七大排序算法


一、排序的概念

排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

排序的稳定性

在这里插入图片描述
上述待排序的数中,有两个5。 将前面的5标记一个a, 将后面的5标记一个b。

通过算法进行排序后,这一组数就有序了, 但是要看两个相同的5的位置是否有改变。
5a仍在5b前面,那么这个排序算法就是稳定的
5a跑到了5b后面,那么这个排序算法就是不稳定的

一个稳定的排序算法可以做到不稳定,
不稳定的排序算法一定做不到稳定。


至于为什么要讨论这个稳定性, 是为了以后应用到实际场景上。 比如,一场数学考试, 假设a用了30分钟做完了,并得了满分。
假设b用了一个小时做完了,并得了满分。 此时a与b都是得了满分,但是用的时间不一样,所以两个人的排名又会有所不同。


七大排序算法

在这里插入图片描述


二、快速排序

核心思想

基本思想任取待排序元素序列中的某元素作为基准值,将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

    public static void quickSort(int[] array) {quick(array,0, array.length-1);}private static void quick(int[] array,int left,int right) {// 此处的一定要是大于等于// 如果往下递归出现了piovt == 0的情况,// 那么再递归进去,left == 0,right == -1,// 此时就要结束递归if(left >= right) {return;}// partition方法功能:将选取的基准值放到合适的位置,并返回基准值下标int piovt = partition(array,left,right);// 通过递归的方式,对左右子序列分别找寻基准值并放到合适位置,从而达到排序目的quick(array, left, piovt-1);quick(array,piovt+1,right);}

共有三种方法可以实现该思想。


Hoare法

图解

有一组待排序数列,我们进行升序排序。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

代码

    public static void quickSort(int[] array) {quick(array,0, array.length-1);}private static void quick(int[] array,int left,int right) {if(left >= right) {return;}int piovt = partition1(array,left,right);quick(array, left, piovt-1);quick(array,piovt+1,right);}//Hoare法private static int partition(int[] array,int left,int right) {// 基准值int tmp = array[left];// 基准下标int index = left;while (left < right) {// 让right找比tmp小的数while (right > left && array[right] >= tmp) {right--;}// 让left找比tmp大的数while (left < right && array[left] <= tmp) {left++;}// 让left与right这两个数进行交换swap(array,left,right);}// 将基准值放到合适的位置swap(array,index,right);// 返回基准下标return right;}

挖坑法

挖坑发是后人对Hoare法的另一种实现方式。

图解

有一组待排序数列,我们进行升序排序。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意left与right的区别,别看错了就搞不懂了。

代码

    public static void quickSort(int[] array) {quick(array,0, array.length-1);}private static void quick(int[] array,int left,int right) {if(left >= right) {return;}int piovt = partition(array,left,right);quick(array, left, piovt-1);quick(array,piovt+1,right);}//挖坑法private static int partition(int[] array,int left,int right) {// 在left下标挖一个坑int tmp = array[left];while (left < right) {// 让right下标去找比tmp小的数while (right > left && array[right] >= tmp) {right--;}// 填left下标的坑,此时right下标变成一个坑了array[left] = array[right];// 让left下标去找比tmp大的数while (left < right && array[left] <= tmp) {left++;}// 填right下标的坑,此时left下标变成一个坑了array[right] = array[left];}// 将基准值放到合适的位置array[left] = tmp;// 返回基准下标return left;}

前后指针法(选学)

图解

有一组待排序数列,我们进行升序排序。
在这里插入图片描述


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码

	public static void quickSort(int[] array) {quick(array,0, array.length-1);}private static void quick(int[] array,int left,int right) {if(left >= right) {return;}int piovt = partition(array,left,right);quick(array, left, piovt-1);quick(array,piovt+1,right);}// 前后指针法private static int partition(int[] array, int left, int right) {int prev = left ;int cur = left+1;while (cur <= right) {// 此处当第一个判断语句不满足时,// 不会触发第二个判断语句,// 会跳出if语句// 因此不是每次if进行判断时,都会执行++previf(array[cur] < array[left] && array[++prev] != array[cur]) {swap(array,cur,prev);}cur++;}swap(array,prev,left);return prev;}private static void swap(int[] array,int a,int b) {int tmp = array[a];array[a] = array[b];array[b] = tmp;}

三、性能分析

最好情况

每次找到的基准都能平均将数据分为两组
在这里插入图片描述
那么此时一共有log(n)层,每层都有n个数据,这n个数据都要遍历一遍,因此
时间复杂度:O(n*log(n)),
此时会先遍历左树,因此空间复杂度为树的高度即层数
空间复杂度:log(n).

不相邻间元素发送了交换
稳定性:不稳定

最坏情况

需要排序的数列本身就是一个顺序或逆序的数列,此时找到的基准就会每次都分成极端的两组,一组一个数没有,另一组有n-1个数(这两组的边界一个是基准-1,一个是基准+1)

在这里插入图片描述
此时就会有n层,每层也有n个数据
此时
时间复杂度:O(n2)
空间复杂度为树的高度即层数
空间复杂度:n

不相邻间元素发送了交换
稳定性:不稳定


四、算法优化

快速排序在最坏情况下,时间复杂度竟然达到了O(n2),这哪里快速啊,所以下面就要进行优化了。

优化基准的选取

共有两种方案: 1️⃣随机选取基准法,这要是倒霉起来,依然有可能会次次都随机选到最极端最坏的情况,所以这个不用。 2️⃣三数取中法,这个可以保证不会让你选到最极端最坏的情况。

三数取中法:在上面的算法中,我们的基准选取的都是left下标,
而三数取中指的是在left,right,mid( (left + right)/2 )这三个下标在中选取一个中间值作为基准,不是最大也不是最小,就保证了不会出现极端情况。
出现了以上的最坏情况,也就是让快速排序变成了二分查找。

    private static int minThree(int[]array,int left,int right) {//三数取中法,优化递归实现的快速排序//使得最坏情况时,快速排序变为二分查找int mid = (left+right)/2;if(array[right] > array[left]) {int tmp = left;left = right;right = tmp;}if(array[mid] > array[left]) {return left;}if(array[mid] > array[right]) {return mid;}return right;}

优化少量数据时的排序方案

数据量大时
就像二叉树一样,每一组数据往下走一层都会被分成两组,而到了最后几层,则会因为数据量的庞大而被分成许多组进行递归,此时的递归开销就会很大,很有可能导致~~栈溢出~~,

因此我们可以设定一个数量闸口,当每组的数据小的到了这个闸口,就采用比较简单的直接插入排序。

而且在快速排序的不断递归下,数据一定是越来越有序的,直接插入排序的效率也会更高。

数据小时

此时即便是一开始就用直接插入排序,时间也会相差无几。


优化后的完整代码

public class QuickSort {/*** 快速排序* 时间复杂度:代码未优化时:最好情况(满二叉树或完全二叉树):O(n*logn),    最坏情况(顺序和逆序时):O(n^2)* 空间复杂度:代码未优化时:最好情况(满二叉树或完全二叉树):O(logn),    最坏情况(顺序和逆序时):O(n)* 稳定性:不稳定* @param array*/public static void quickSort(int[] array) {quick(array,0, array.length-1);}private static void quick(int[] array,int left,int right) {if(left >= right) {return;}// 设置数量闸口,// 数量小,使用直接插入排序if(right - left + 1 < 14) {InsertSort(array);return;}// 将三数取中法取得的中间值换到left处swap(array,minThree(array,left,right),left);int piovt = partition(array,left,right);quick(array, left, piovt-1);quick(array,piovt+1,right);}//挖坑法private static int partition(int[] array,int left,int right) {// 在left下标挖一个坑int tmp = array[left];while (left < right) {// 让right下标去找比tmp小的数while (right > left && array[right] >= tmp) {right--;}// 填left下标的坑,此时right下标变成一个坑了array[left] = array[right];// 让left下标去找比tmp大的数while (left < right && array[left] <= tmp) {left++;}// 填right下标的坑,此时left下标变成一个坑了array[right] = array[left];}// 将基准值放到合适的位置array[left] = tmp;// 返回基准下标return left;}//Hoare法private static int partition3(int[] array,int left,int right) {// 基准值int tmp = array[left];// 基准下标int index = left;while (left < right) {// 让right找比tmp小的数while (right > left && array[right] >= tmp) {right--;}// 让left找比tmp大的数while (left < right && array[left] <= tmp) {left++;}// 让left与right这两个数进行交换swap(array,left,right);}// 将基准值放到合适的位置swap(array,index,right);// 返回基准下标return right;}//前后指针法private static int partition2(int[] array, int left, int right) {int prev = left ;int cur = left+1;while (cur <= right) {if(array[cur] < array[left] && array[++prev] != array[cur]) {swap(array,cur,prev);}cur++;}swap(array,prev,left);return prev;}private static int minThree(int[]array,int left,int right) {//三数取中法,优化递归实现的快速排序//使得最坏情况时,快速排序变为二分查找int mid = (left+right)/2;if(array[right] > array[left]) {int tmp = left;left = right;right = tmp;}if(array[mid] > array[left]) {return left;}if(array[mid] > array[right]) {return mid;}return right;}private static void swap(int[] array,int a,int b) {int tmp = array[a];array[a] = array[b];array[b] = tmp;}}

五、七大排序算法

在这里插入图片描述

想学哪个点哪个
归并排序讲解
快速排序讲解
直接插入排序讲解
希尔排序讲解
直接选择排序讲解
堆排序讲解
冒泡排序讲解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/21492.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DNS协议解析原理

0. 前言 为了保证网址的正常访问&#xff0c;域名解析协议&#xff08;DNS&#xff09;其实在背后做出了很多努力&#xff0c;本文将透彻讲解 DNS 协议的原理&#xff0c;了解我们每天都在接触的网址到底是怎么工作的。 1. 什么是 DNS 协议 在学习 DNS 协议之前&#xff0c;我…

前端uni-app自定义精美全端复制文本插件,支持全端文本复制插件 可设置复制按钮颜色

随着技术的发展&#xff0c;开发的复杂度也越来越高&#xff0c;传统开发方式将一个系统做成了整块应用&#xff0c;经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改&#xff0c;造成牵一发而动全身。 通过组件化开发&#xff0c;可以有效实现…

一场内容生产的革命 :从PGC、UGC到AIGC

1 概念解读 1.1 什么是PGC&#xff1f; PGC 是指专业生成内容&#xff08;Professional Generated Content&#xff09;&#xff0c;它指的是由专业的内容创作者或团队进行创作、编辑和发布的内容。PGC创作方式起源于传统媒体时代&#xff0c;如报纸、杂志、电视和电影等&…

【沐风老师】3DMAX灯光放置插件LightPlacer使用方法教程

3DMAX灯光放置插件LightPlacer使用方法 3DMAX灯光放置插件LightPlacer&#xff0c;用于3dMax放置和管理灯光的插件&#xff0c;可以在3dMax中一键制作所需的灯光&#xff0c;且通过插件创建出来的灯光属性可以在该面板下进行直接修改&#xff0c;并不需要切换至堆栈。该插件的有…

城市餐饮油烟的监测与治理

摘要&#xff1a;为控制餐饮油烟污染&#xff0c;改善城市大气污染和生态环境&#xff0c;针对城市餐饮油烟污染现状&#xff0c;提出相应的治理政策。加快餐饮油烟污染立法进度&#xff0c;推进相关法律法规修订&#xff0c;加大油烟污染执法力度&#xff1b;维护清洗油烟净化…

MySQL(备份还原索引视图入门)

文章目录 第一节 备份和还原1、题目2、题目作答 第二节 索引1.题目2.题目作答 第三节 视图1 题目2 题目作答 第一节 备份和还原 1、题目 CREATE DATABASE beifen;use beifen;CREATE TABLE books(bk_id INT NOT NULL PRIMARY KEY,bk_title VARCHAR(50) NOT NULL,copyright YEA…

MOVEit再现新漏洞,多个版本受影响

今年6月&#xff0c;文件共享工具MOVEit Transfer曾曝出SQL 注入漏洞&#xff0c;能让远程攻击者访问其数据库并执行任意代码。最近&#xff0c;MOVEit Transfer 母公司Progress Software又披露了三个新漏洞。 这三个漏洞分别是 CVE-2023-36932、CVE-2023-36933 和 CVE-2023-36…

07_flash全擦除实验

07_flash全擦除实验 1. SPI 协议1.1 SPI 协议1.2 SPI 物理层1.3 SPI 协议层1.3.1 SPI 通讯模式时序图1.3.2 CPHA0 时的 SPI 通讯模式1.3.3 CPHA1 时的 SPI 通讯模式 2. 实验目标3. SPI-Flash 芯片3.1 硬件资源3.2 板载 Flash 原理图3.3 操作时序3.3.1 全擦除时序3.3.2 写使能时…

CDN技术(Content Delivery Network,内容分发网络)分布式网络架构(CND与P2P(Peer-to-Peer)区别)

文章目录 CDN是什么&#xff1f;CDN的优势CDN的应用1. 静态内容加速2. 动态内容加速3. 视频流媒体4. 软件分发5. 游戏加速6. 移动应用加速 CDN收费吗&#xff1f;CND与P2P区别什么是静态内容和动态内容&#xff1f; CDN是什么&#xff1f; CDN&#xff08;Content Delivery Ne…

html相关面试题

html相关面试题 1.html和css中的图片加载与渲染规则是什么样的&#xff1f;2.title与h1的区别、b与strong的区别、i与em的区别&#xff1f;title 和 h1 的区别b 和 strong 的区别i 和 em 的区别最后 3.script 标签为什么建议放在 body 标签的底部&#xff08;defer、async&…

WebDAV之π-Disk派盘 + PDF Expert

PDF Expert 支持WebDAV方式连接π-Disk派盘。 PDF Expert是一款macOS上的办公软件,它具有专业的PDF编辑功能,可以快速从邮件、网页支持PDF打开,支持用户进行阅读、批注等功能,用户可以直接在PDF上进行编辑文字图片,表单文档、创建笔记、添加书单等自定义使用,大大提高工…

【从零开始学习JAVA | 第二十八篇】不可变集合

目录 目录 前言&#xff1a; 不可变集合&#xff1a; 常见的不可变集合&#xff1a; 1.创建list的不可变集合&#xff1a; 2.创建map的不可变集合&#xff1a; 应用场景&#xff1a; 总结&#xff1a; 前言&#xff1a; 本文我们将为大家介绍JAVA中的不可变集合&#x…