当前位置: 首页 > news >正文

【算法】快速排序 详解

在这里插入图片描述

快速排序 详解

  • 快速排序
    • 1. 挖坑法
    • 2. 左右指针法 (Hoare 法)
    • 3. 前后指针法
    • 4. 快排非递归
  • 代码优化

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

稳定性: 假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中, r[i] = r[j], 且 r[i] 在 r[j] 之前,而在排序后的序列中, r[i] 仍在 r[j] 之前,则称这种排序算法是稳定的;否则称为不稳定的。
(注意稳定排序可以实现为不稳定的形式, 而不稳定的排序实现不了稳定的形式)

在这里插入图片描述

内部排序: 数据元素全部放在内存中的排序。

外部排序: 数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

快速排序

快速排序是 Hoare 于 1962 年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

1. 挖坑法

  • 选择基准元素:从待排序的数组中选择一个元素作为基准(pivot)。通常情况下,可以选择第一个元素、最后一个元素、中间元素或者随机选择一个元素作为基准。

  • 分割数组:从数组的两端开始,分别设置两个指针,一个从左边(low指针)开始,一个从右边(high指针)开始,分别向中间移动,直到它们相遇。在移动过程中,通过比较元素与基准的大小关系,找到一个大于基准的元素和一个小于基准的元素,并将它们互换位置。

  • 继续分割:重复步骤2,直到low指针和high指针相遇。在此过程中,将小于基准的元素移到基准的左侧,将大于基准的元素移到基准的右侧,形成三个区域。

  • 递归排序:对左侧小于基准的区域和右侧大于基准的区域,分别递归地应用快速排序算法。

在这里插入图片描述

    public static void quickSort(int[] arr) {int len = arr.length;partition(arr, 0, len-1);}public static void partition(int[] arr, int left, int right) {if (left >= right) {return;}int pivot = left;int value = arr[left];// 从两边开始遍历int begin = left;int end = right;while (begin < end) {// 注意一定要带上 ==, 不然死循环while (begin < end && arr[end] >= value) {end--;}// 右边找到小于 value 的值arr[pivot] = arr[end];// end 变为 坑pivot = end;while (begin < end && arr[begin] <= value) {begin++;}// 左边找到大于 value 的值arr[pivot] = arr[begin];// begin 变为坑pivot = begin;}// value 找到自己的正确位置arr[pivot] = value;// 递归左边和右边partition(arr, left, pivot-1);partition(arr, pivot+1, right);}

2. 左右指针法 (Hoare 法)

从两边开始, 左边找到比基准值大的, 右边找比基准值小的, 找到后, 两边交换, 一直到左右两个指针相遇, 相遇位置即为基准值的正确位置, 然后递归确定左右两边的区间元素的位置。

	public static void quickSort(int[] arr) {int len = arr.length;partition(arr, 0, len-1);}public static void partition(int[] arr, int left, int right) {if (left >= right) {return;}int value = arr[left];int begin = left;int end = right;while (begin < end) {// 注意一定要带上 ==, 不然死循环while (begin < end && arr[end] >= value) {end--;}while (begin < end && arr[begin] <= value) {begin++;}swap(arr, begin, end);}swap(arr, left, begin);partition(arr, left, begin-1);partition(arr, begin+1, right);}public static void swap (int[] arr, int index1, int index2) {int temp = arr[index1];arr[index1] = arr[index2];arr[index2] = temp;}

3. 前后指针法

后面的指针指向小于基准值的最后一个, 前面的指针一直往后找, 找到小于基准值的就与后面指针的下一个位置交换, 后面的指针 ++, 直到前面的指针遍历完, 最后后面的指针的位置, 就是基准值的正确位置。然后再递归确定左右区间的元素的位置。

    public static void quickSort(int[] arr) {int len = arr.length;partition(arr, 0, len-1);}public static void partition(int[] arr, int left, int right) {if (left >= right) {return;}int value = arr[left];// 前后指针int end = left;int front = left + 1;while (front <= right) {if (arr[front] < value) {swap(arr, end+1, front);end++;}front++;}swap(arr, left, end);// 递归左边和右边partition(arr, left, end-1);partition(arr, end+1, right);}public static void swap (int[] arr, int index1, int index2) {int temp = arr[index1];arr[index1] = arr[index2];arr[index2] = temp;}

4. 快排非递归

使用栈记录要排序的区间

    public static void quickSortNonR(int[] arr) {int len = arr.length;Stack<Integer> stack = new Stack<>();stack.push(arr.length-1);stack.push(0);while (!stack.isEmpty()) {int left = stack.pop();int right = stack.pop();if (left >= right) {continue;}int pivot = left;int value = arr[left];// 从两边开始遍历int begin = left;int end = right;while (begin < end) {// 注意一定要带上 ==, 不然死循环while (begin < end && arr[end] >= value) {end--;}// 右边找到小于 value 的值arr[pivot] = arr[end];// end 变为 坑pivot = end;while (begin < end && arr[begin] <= value) {begin++;}// 左边找到大于 value 的值arr[pivot] = arr[begin];// begin 变为坑pivot = begin;}// value 找到自己的正确位置arr[pivot] = value;// 右区间stack.push(right);stack.push(pivot+1);// 左区间stack.push(pivot-1);stack.push(left);}}

代码优化

优化一:
三数取中:
当我们找基准值时, 基准值越靠近是中位数,每次都近似于将一个大的区间分成两个相等的区间, 那么时间复杂度越低, 越靠近是 O(N*logN), 最坏的情况下, 每次确定一个元素, 即分成的两个区间其中一个只有一个元素, 那么如果每次都是这样的话, 最终的时间复杂度为 O(N*N), 所以选择基准值时, 越靠近中位数越好。

这里面以前后指针为例使用了 三数取中

public static void quickSort(int[] arr) {int len = arr.length;partition(arr, 0, len-1);}/***  前后指针*/public static void partition(int[] arr, int left, int right) {if (left >= right) {return;}// 三数取中int midIndex = getMidIndex(arr, left, right);swap(arr, left, midIndex);int value = arr[left];// 前后指针int end = left;int front = left + 1;while (front <= right) {if (arr[front] < value) {swap(arr, end+1, front);end++;}front++;}swap(arr, left, end);// 递归左边和右边partition(arr, left, end-1);partition(arr, end+1, right);}public static int getMidIndex(int[] arr, int left, int right) {int midIndex = ((right - left) >> 1) + left;if (arr[left] <= arr[midIndex]) {if (arr[midIndex] <= arr[right]) {return midIndex;} else {if (arr[left] >= arr[right]) {return left;} else {return right;}}} else {if (arr[midIndex] >= arr[right]) {return midIndex;} else {if (arr[left] >= arr[right]) {return right;} else {return left;}}}}public static void swap (int[] arr, int index1, int index2) {int temp = arr[index1];arr[index1] = arr[index2];arr[index2] = temp;}

优化二:
小区间优化, 当区间中元素数量比较少时,区间中元素本身就接近有序, 使用直接插入排序能提高效率 (直接插入排序)
假如说有 100W 个数据, 就会调用 100W 次 partition 函数, 但是光最后两层就会调用近 80W 次, 所以使用小区间优化提高效率。

直接插入排序

    public static void insertSort(int[] arr) {int len = arr.length;for (int i = 1; i < len; i++) {// 从已经有序的位置从后往前开始比较int key = arr[i];int end = i-1;while (end >= 0 && arr[end] > key) {// 数据往后挪arr[end+1] = arr[end];end--;}// 找到了合适位置, 就插入进去arr[end+1] = key;}}

在快排中使用小区间优化

/***   直接插入排序*/public static void insertSort(int[] arr, int left, int right) {for (int i = left+1; i <= right; i++) {// 从已经有序的位置从后往前开始比较int key = arr[i];int end = i-1;while (end >= 0 && arr[end] > key) {// 数据往后挪arr[end+1] = arr[end];end--;}// 找到了合适位置, 就插入进去arr[end+1] = key;}}public static void quickSort(int[] arr) {int len = arr.length;partition(arr, 0, len-1);}public static void partition(int[] arr, int left, int right) {if (left >= right) {return;}// 小区间优化, 当数组中元素个数 <= 100 个时使用直接插入排序// 主要是减少了递归的次数if (right - left + 1 <= 100) {insertSort(arr, left, right);return;}// 三数取中int midIndex = getMidIndex(arr, left, right);swap(arr, left, midIndex);int value = arr[left];// 前后指针int end = left;int front = left + 1;while (front <= right) {if (arr[front] < value) {swap(arr, end+1, front);end++;}front++;}swap(arr, left, end);// 递归左边和右边partition(arr, left, end-1);partition(arr, end+1, right);}

总结:

  • 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  • 时间复杂度: O (N*logN)
  • 空间复杂度: O(logN)
  • 是不稳定排序
  • 对数据比较敏感: 当数据本身就是有序时, 情况最坏, 因为每次只能确定一个数据, 时间复杂度为 O(N*N)
    在这里插入图片描述

好啦, 以上就是对快速排序的讲解, 希望能帮到你 !
评论区欢迎指正 !

相关文章:

【算法】快速排序 详解

快速排序 详解 快速排序1. 挖坑法2. 左右指针法 &#xff08;Hoare 法&#xff09;3. 前后指针法4. 快排非递归 代码优化 排序&#xff1a; 排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&…...

架构师spring boot 面试题

spring boot 微服务有哪些特点&#xff1f; Spring Boot 微服务具有以下特点&#xff1a; 独立性&#xff1a;每个微服务都是独立的部署单元&#xff0c;有自己的代码库和数据库。这使得微服务可以独立开发、测试、部署和扩展。 分布式&#xff1a;微服务架构将一个大型应用程…...

电商系统架构设计系列(十一):在电商的交易类系统中,如何正确地使用 Redis 这样的缓存系统呢?需要考虑哪些问题?

上篇文章中&#xff0c;我给你留了一个思考题&#xff1a;在电商的交易类系统中&#xff0c;如何正确地使用 Redis 这样的缓存系统呢&#xff1f;需要考虑哪些问题&#xff1f; 这篇文章&#xff0c;我们来聊聊。 引言 我们知道&#xff0c;大部分面向公众用户的互联网系统&a…...

MySQL数据库和表的操作

数据库基础 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 文件保存数据有以下几个缺点&#xff1a; 1、文件的安全性问题 2、文件不利于数据查询和管理 3、文件不利于存储海量数据 4、文件在程序中控制不方便 数据库存储介质&#xff1a; 磁盘 内存 为了解决上…...

DAY-01--分布式微服务基础概念

一、项目简介 了解整体项目包含后端、前端、周边维护。整个项目的框架知识。 二、分布式基础概念 1、微服务 将应用程序 基于业务 拆分为 多个小服务&#xff0c;各小服务单独部署运行&#xff0c;采用http通信。 2、集群&分布式&节点 集群是个物理形态&#xff0c;…...

记:一次关于paddlenlp、python、版本之间的兼容性问题

兼容版本 Python 3.10.8 absl-py1.4.0 accelerate0.19.0 addict2.4.0 aiofiles23.1.0 aiohttp3.8.3 aiosignal1.3.1 alembic1.10.4 aliyun-python-sdk-core2.13.36 aliyun-python-sdk-kms2.16.0 altair4.2.2 altgraph0.17.3 aniso86019.0.1 antlr4-python3-runtime4.9.3 anyi…...

MyBatis配置及单表操作

文章目录 一. MyBatis概述二. MyBatis项目的创建1. 准备一个数据表2. 创建项目 三. MyBatis的使用1. 基本使用2. SpringBoot单元测试 四. 使用MyBatis实现单表操作1. 查询2. 修改3. 删除4. 新增 五. 基于注解完成SQL 一. MyBatis概述 MyBatis 是一款优秀的持久层框架&#xff…...

python基础教程:深浅copy的详细用法

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 1.先看赋值运算 l1 [1,2,3,[barry,alex]] l2 l1l1[0] 111 print(l1) # [111, 2, 3, [barry, alex]] print(l2) # [111, 2, 3, [barry, alex]]l1[3][0] wusir print(l1) # [111, 2, 3, [wusir, alex]] print(l2)…...

【算法篇】动态规划(二)

文章目录 分割回文字符串编辑距离不同的子序列动态规划解题思路 分割回文字符串 class Solution { public:bool isPal(string& s,int begin,int end){while(begin<end){if(s[begin]!s[end]){return false;}begin;end--;}return true;}int minCut(string s) {int lens.si…...

数据库 SQL高级查询语句:聚合查询,多表查询,连接查询

目录 创建学生表聚合查询聚合函数直接查询设置别名查询设置条件查询 常用的聚合函数 分组查询单个字段Group by报错分组查询多字段分组查询 多表查询直接查询重命名查询Students表新建一列CourseID 连接&#xff08;JOIN&#xff09;查询INNER JOINRIGHT JOIN, LEFT JOINFULL J…...

pytorch-构建卷积神经网络

构建卷积神经网络 卷积网络中的输入和层与传统神经网络有些区别&#xff0c;需重新设计&#xff0c;训练模块基本一致 import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F from torchvision import datasets,transforms impor…...

点云从入门到精通技术详解100篇-点云滤波算法及单木信息提取(续)

目录 3.3 点云滤波算法原理概述 3.3.1 坡度滤波算法 3.3.2 基于不规则三角网滤波 3.3.3 数学形态学滤波...

Gartner发布中国科技报告:数据编织和大模型技术崭露头角

近日&#xff0c;全球知名科技研究和咨询机构Gartner发布了关于中国数据分析与人工智能技术的最新报告。报告指出&#xff0c;中国正迎来数据分析与人工智能领域的蓬勃发展&#xff0c;预计到2026年&#xff0c;将有超过30%的白领工作岗位重新定义&#xff0c;生成式人工智能技…...

java八股文面试[数据库]——explain

使用 EXPLAIN 关键字可以模拟优化器来执行SQL查询语句&#xff0c;从而知道MySQL是如何处理我们的SQL语句的。分析出查询语句或是表结构的性能瓶颈。 MySQL查询过程 通过explain我们可以获得以下信息&#xff1a; 表的读取顺序 数据读取操作的操作类型 哪些索引可以被使用 …...

Kafka3.0.0版本——增加副本因子

目录 一、服务器信息二、启动zookeeper和kafka集群2.1、先启动zookeeper集群2.2、再启动kafka集群 三、增加副本因子3.1、增加副本因子的概述3.2、增加副本因子的示例3.2.1、创建topic(主题)3.2.2、手动增加副本存储 一、服务器信息 四台服务器 原始服务器名称原始服务器ip节点…...

升级iOS 17出现白苹果、不断重启等系统问题怎么办?

iOS 17发布后了&#xff0c;很多果粉都迫不及待的将iphone/ipad升级到最新iOS17系统&#xff0c;体验新系统功能。 但部分果粉因硬件、软件的各种情况&#xff0c;导致升级系统后出现故障&#xff0c;比如白苹果、不断重启、卡在系统升级界面等等问题。 如果遇到了这些系统问题…...

6. `Java` 并发基础之`ReentrantReadLock`

前言&#xff1a;随着多线程程序的普及&#xff0c;线程同步的问题变得越来越常见。Java中提供了多种同步机制来确保线程安全&#xff0c;其中之一就是ReentrantLock。ReentrantLock是Java中比较常用的一种同步机制&#xff0c;它提供了一系列比synchronized更加灵活和可控的操…...

float浮动布局大战position定位布局

华子目录 布局方式普通文档流布局浮动布局&#xff08;浮动主要针对与black&#xff0c;inline元素&#xff09;float属性浮动用途浮动元素父级高度塌陷 position属性定位篇相对定位&#xff08;relative为属性值&#xff0c;配合left属性&#xff0c;和top属性使用&#xff09…...

算法 数据结构 递归插入排序 java插入排序 递归求解插入排序算法 如何用递归写插入排序 插入排序动图 插入排序优化 数据结构(十)

1. 插入排序&#xff08;insertion-sort&#xff09;&#xff1a; 是一种简单直观的排序算法。它的工作原理是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入 算法稳定性: 对于两个相同的数&#xff0c;经过…...

OpenCV(二十二):均值滤波、方框滤波和高斯滤波

目录 1.均值滤波 2.方框滤波 3.高斯滤波 1.均值滤波 OpenCV中的均值滤波&#xff08;Mean Filter&#xff09;是一种简单的滤波技术&#xff0c;用于平滑图像并减少噪声。它的原理非常简单&#xff1a;对于每个像素&#xff0c;将其与其周围邻域内像素的平均值作为新的像素值…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具&#xff0c;支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议&#xff0c;涵盖接口测试、性能测试、数字体验监测等测试类型…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...