【十大排序算法】快速排序

在乱序的世界中,快速排序如同一位智慧的园丁,
以轻盈的手法,将无序的花朵们重新安排,
在每一次比较中,沐浴着理性的阳光,
终使它们在有序的花园里,开出绚烂的芬芳。

文章目录

  • 一、快速排序
  • 二、发展历史
  • 三、处理流程
  • 四、算法实现
  • 五、快速排序的特性
  • 六、小结
  • 推荐阅读

一、快速排序

快速排序是一种高效的排序算法,它采用了分治的策略。它的基本思想是选择一个基准值,将数组分为两个子数组,一个子数组中的所有元素都小于基准值,另一个子数组中的所有元素都大于基准值,然后对这两个子数组递归地进行排序。

具体来说,快速排序的步骤如下:

  1. 选择一个基准值(通常是数组的第一个元素、最后一个元素或者中间元素)。
  2. 将数组分为两个子数组,一个子数组中的所有元素都小于基准值,另一个子数组中的所有元素都大于基准值。
  3. 对这两个子数组递归地进行排序。
  4. 将排好序的子数组合并起来,即得到有序数组。

快速排序的关键在于分割操作,通过这个操作,它可以将一个大问题分解成两个规模较小的子问题,然后分别解决,最终达到整体有序的目的。

二、发展历史

快速排序是由英国计算机科学家 Tony Hoare 1960 1960 1960 年代提出的。Hoare 最初将这一算法称为 “分区交换排序”,后来更广泛地称为快速排序。他的灵感来自于合并排序和插入排序。

  1. 早期思想: Hoare 注意到合并排序在实践中性能良好,但需要额外内存空间;而插入排序则内存消耗较少,但在大规模数据下性能不佳。他希望能够结合两者的优点,创造出一种既能够节省空间又能够在平均情况下具有良好性能的排序算法。
  2. Hoare 分区法: Hoare 提出了一种称为 Hoare 分区法的分区策略,这成为了快速排序的核心部分。这个方法从数组中选择一个基准值,将数组分成两个部分,左边的部分包含比基准值小的元素,右边的部分包含比基准值大的元素。然后,递归地对这两个部分进行排序。
  3. 论文发表: Hoare 在 1961 1961 1961 年发表了关于快速排序的论文,论文中描述了这一算法的基本原理和实现方法。此后,他不断改进和优化这一算法,使得快速排序成为了一种非常高效的排序算法。
  4. 持续优化: 随着计算机科学的发展,许多学者对快速排序进行了进一步的优化和改进。例如,采用随机化的方式选择基准值,以防止最坏情况的发生;使用三数取中法来选择基准值,以提高算法的稳定性和性能等。
  5. 现代应用: 至今,快速排序仍然是一种非常流行和高效的排序算法,被广泛应用于各种编程语言和实际应用中。其简洁而优雅的设计理念以及优秀的性能使得它成为了排序算法中的经典之作。

三、处理流程

场景假设:我们需要将下列无序序列使用快速排序按从小到大进行排序。
workspace.png
快速排序的流程如下:

  1. 在原序列中选取第一个元素 3 3 3 作为 Pivot 哨兵
  2. 将序列中小于 Pivot 的元素,放在 Pivot 的左边,大于 Pivot 的元素放在 Pivot 的右边
  3. 递归处理 Pivot 左边的序列和右边的序列
  4. 当子序列为长度 1 1 1 时终止

workspace (1).png

四、算法实现

// 快速排序入口
void quickSort(int[] arr, int low, int high) {if (low < high) {// 对数组进行分区操作int pivot = partition(arr, low, high);// 递归排序左半部分quickSort(arr, low, pivot - 1);// 递归排序右半部分quickSort(arr, pivot + 1, high);}
}// 分区操作
int partition(int[] arr, int low, int high) {// 选择最后一个元素作为 pivotint pivot = arr[high];int i = low - 1; // 指向小于 pivot 的区域的边界// 遍历数组,将小于 pivot 的元素放到左侧for (int j = low; j < high; j++) {if (arr[j] < pivot) {i++;// 交换元素int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}// 将 pivot 放到正确位置int temp = arr[i + 1];arr[i + 1] = arr[high];arr[high] = temp;// 返回 pivot的位置return i + 1;
}public static void main(String[] args) {int[] arr = {5, 3, 8, 6, 2, 7, 1, 4};// 调用快速排序算法quickSort(arr, 0, arr.length - 1);// 输出排序后的数组System.out.print("Sorted array: ");for (int num : arr) {System.out.print(num + " ");}
}

算法时间复杂度分析:

情况时间复杂度计算公式公式解释
最好情况 O ( n l o g n ) O(nlogn) O(nlogn) T ( n ) = 2 T ( n 2 ) + n T(n) = 2T(\frac{n}{2}) + n T(n)=2T(2n)+n在最优情况下,每次划分都能将数组均匀地分成两部分。因此,我们有两个大小为 n 2 \frac{n}{2} 2n 的子问题,所以有 2 T ( n 2 ) 2T(\frac{n}{2}) 2T(2n)。然后,我们需要 n n n 的时间来进行划分操作。所以,总的时间复杂度就是 2 T ( n 2 ) + n 2T(\frac{n}{2}) + n 2T(2n)+n
平均情况 O ( n l o g n ) O(nlogn) O(nlogn) T ( n ) = T ( n 2 ) + T ( n 2 ) + n T(n) = T(\frac{n}{2}) + T(\frac{n}{2}) + n T(n)=T(2n)+T(2n)+n在平均情况下,我们假设每次划分都能将数组均匀地分成两部分。因此,我们有两个大小为 n 2 \frac{n}{2} 2n 的子问题,所以有 T ( n 2 ) + T ( n 2 ) T(\frac{n}{2}) + T(\frac{n}{2}) T(2n)+T(2n)。然后,我们需要 n n n 的时间来进行划分操作。所以,总的时间复杂度就是 T ( n 2 ) + T ( n 2 ) + n T(\frac{n}{2}) + T(\frac{n}{2}) + n T(2n)+T(2n)+n
最坏情况 O ( n 2 ) O(n^2) O(n2) T ( n ) = T ( n − 1 ) + n T(n) = T(n - 1) + n T(n)=T(n1)+n在最坏情况下,每次划分只能将数组划分为一份有 n − 1 n - 1 n1 个元素,另一份有 0 0 0 个元素。因此,我们有一个大小为 n − 1 n - 1 n1 子问题,所以有 T ( n − 1 ) T(n - 1) T(n1)。然后,我们需要 n n n 的时间来进行划分操作。所以,总的时间复杂度就是 T ( n − 1 ) + n T(n - 1) + n T(n1)+n

五、快速排序的特性

快速排序具有以下特性:

  1. 稳定性: 在一般情况下,快速排序是不稳定的,即相同元素在排序后可能会改变相对位置。
  2. 原地性: 快速排序是一种原地排序算法,不需要额外的辅助空间,只需要使用原始数组进行排序。
  3. 适应性: 快速排序适用于各种数据类型,并且对部分有序的数据排序效果良好。
  4. 高效性: 快速排序在平均情况下具有 O ( n l o g n ) O(nlogn) O(nlogn) 的时间复杂度,使其成为处理大规模数据的理想选择。

六、小结

快速排序是一种非常重要且高效的排序算法,适用于各种数据类型和应用场景。其原地性、高效性以及简单直观的实现使得它成为了排序算法中的经典之作。

推荐阅读

  1. Spring 三级缓存
  2. 深入了解 MyBatis 插件:定制化你的持久层框架
  3. Zookeeper 注册中心:单机部署
  4. 【JavaScript】探索 JavaScript 中的解构赋值
  5. 深入理解 JavaScript 中的 Promise、async 和 await

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

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

相关文章

Java 为什么重写equals方法时一定要重写hashCode方法

思考 hashCode和 equals 有一个协定&#xff0c;就是如果两个对象相等&#xff0c;equals判断为true,如果你只重写了equals&#xff0c;你自己定义了一些逻辑去判断两个对象的相等&#xff0c;比如id 和 name ,如果两个对象的id和name相等则相等&#xff0c;但是因为你没重写h…

profile-3d-contrib,github三维立体图的使用

图片展示: 提示: 这个profile-3d-contrib存储库有时候会出现问题,导致又有使用这个存储库svg的用户显示出现问题. 参考: https://zhuanlan.zhihu.com/p/681786778 原仓库链接&#xff1a; GitHub - yoshi389111/github-profile-3d-contrib: This GitHub Action creates a Gi…

【算法刷题 | 动态规划08】6.9(单词拆分、打家劫舍、打家劫舍||)

文章目录 21.单词拆分21.1题目21.2解法&#xff1a;动规21.2.1动规思路21.2.2代码实现 22.打家劫舍22.1题目22.2解法&#xff1a;动规22.2.1动规思路22.2.2代码实现 23.打家劫舍||23.1题目23.2解法&#xff1a;动规23.2.1动规思路23.2.2代码实现 21.单词拆分 21.1题目 给你一…

java中的异常-异常处理(try、catch、finally、throw、throws)+自定义异常

一、概述 1、java程序员在编写程序时提前编写好对异常的处理程序&#xff0c;在程序发生异常时就可以执行预先设定好的处理程序&#xff0c;处理程序执行完之后&#xff0c;可以继续向后执行后面的程序 2、异常处理程序是在程序执行出现异常时才执行的 二、5个关键字 1、tr…

在CentOS 7.9上安装和配置mitmproxy的详细步骤

以下是在CentOS 7.9上安装和配置mitmproxy的详细步骤&#xff1a; 更新系统&#xff1a;首先&#xff0c;打开终端并运行以下命令来更新您的系统&#xff1a; sudo yum update这将确保所有的包都是最新的。 安装Python和pip&#xff1a;mitmproxy需要Python 3.6或更高版本。Ce…

Redis实战篇02

1.分布式锁Redisson 简单介绍&#xff1a; 使用setnx可能会出现的极端问题&#xff1a; Redisson的简介&#xff1a; 简单的使用&#xff1a; 业务代码的改造&#xff1a; private void handleVoucherOrder(VoucherOrder voucherOrder) {Long userId voucherOrder.getUserI…

2024真机项目

项目需求&#xff1a; 1. 172.25.250.101 主机上的 Web 服务要求提供 www.exam.com 加密站点&#xff0c;该站点在任何路由可达 的主机上被访问&#xff0c;页面内容显示为 "Hello&#xff0c;Welcome to www.exam.com !"&#xff0c;并提供 content.exam.com/yum/A…

数据:人工智能的基石 | Scale AI 创始人兼 CEO 亚历山大·王的创业故事与行业洞见

引言 在人工智能领域&#xff0c;数据被誉为“新石油”&#xff0c;其重要性不言而喻。随着GPT-4的问世&#xff0c;AI技术迎来了新的浪潮。众多年轻创业者纷纷投身这一领域&#xff0c;Scale AI的创始人兼CEO亚历山大王&#xff08;Alexander Wang&#xff09;就是其中的佼佼…

什么是Java?

什么是Java&#xff1f;java是什么&#xff1f;下面我们来总结一下。 java是什么&#xff1f; java是一个静态编程语言&#xff0c;具有强大的多线程特征&#xff0c;目前java不仅采用c语言的优点&#xff0c;还去掉了一些多继承指针&#xff0c;等复杂的概念&#xff0c;我们…

Linux C/C++ 进程控制

Linux C/C 进程控制 1.信号2.进程终止3.调用可执行程序4.进程创建5.僵尸进程6.多进程与信号8.共享内存9.循环队列10. 信号量的基本概念11.多进程的生产消费者模型 1.信号 ​ 信号(signal)是软件中断&#xff0c;是进程之间相互传递消息的一种方法&#xff0c;用于通知进程发生…

回文数java算法

回文数是指从左往右读和从右往左读都一样的数字。要判断一个数字是否是回文数&#xff0c;可以将该数字转换成字符串&#xff0c;然后将字符串分割为字符数组&#xff0c;最后比较数组的对应位置的字符是否相同。 以下是一个判断回文数的Java算法&#xff1a; public static …

Git配置 安装及使用

团队开发的神 找工作必备 环境变量 配置好环境后 打开终端环境 winr cmd 我习惯在桌面打开&#xff0c;然后进入相应的文件夹 &#xff08;文件夹结构&#xff09; &#xff08;个人感觉能用cmd不用git&#xff0c;cmd更好用一些&#xff09; 进入对应的文件夹 填写自己对…

docker安装rabbitmq详解

目录 1、安装 1-1.查看rabbitmq镜像 1-2.下载Rabbitmq的镜像 1-3.创建并运行rabbitmq容器 1-4.查看启动情况 1-5.启动web客户端 1-6.访问rabbitmq的客户端 2..遇到的问题 解决方法: 1、安装 1-1.查看rabbitmq镜像 docker search rabbitmq 1-2.下载Rabbitmq的镜像 拉…

2024.6.9 五

文件区别 文本文件,又各种不同编码方式编码,导致占用空间也会不同 二进制文件,只有0和1,指定软件才能打开 操作文件步骤 先打开文件 用函数open(filename,mode,encoding) 默认打开方式是 ’ r ’ r----只读取,文件指针在文件开头,没有文件报错 w----纯覆写。如果文件存在&am…

国标GB/T 28181详解:校时流程详细说明

目录 一、定义 二、作用 1. 时间同步性 2. 事件记录的准确性 3. 跨平台、跨设备协作 4. 降低时间误差 5. 安全性提升 三、基本要求 四、命令流程 五、协议接口 六、校时效果 1、未校时的情况 2、校时后的效果 七、参考 一、定义 GB28181协议要求所有的监控设…

python后端结合uniapp与uview组件tabs,实现自定义导航按钮与小标签颜色控制

实现效果&#xff08;红框内&#xff09;&#xff1a; 后端api如下&#xff1a; task_api.route(/user/task/states_list, methods[POST, GET]) visitor_token_required def task_states(user):name_list [待接单, 设计中, 交付中, 已完成, 全部]data []color [#F04864, …

HOW - 接口防腐层(Anti-corruption layer)

目录 一、介绍1.1 主要概念1.2 实现方式1.3 示例 二、前端开发领域应用&#xff1a;接口不确定数据防腐层2.1 步骤和技术2.2 示例实现2.3 应用示例 一、介绍 在程序开发领域&#xff0c;防腐层&#xff08;Anti-Corruption Layer&#xff0c;ACL&#xff09;是一种架构模式&am…

CPP初阶:CPP的内存管理模式

目录 一.new和delete操作自定义类型 1.1C语言的内存管理 1.2CPP的内存管理方式 1.3C与CPP内存管理的差异 二.operator new和operator delete函数 三.CPP空间操作符使用深化 3.1 连续内存开辟与释放 3.2 非连续内存开辟与释放 四.new和delete的实现原理 4.1内置类型 4.2…

100道面试必会算法-32-二叉树右视图用栈实现队列

100道面试必会算法-32-二叉树右视图&用栈实现队列 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4] 输出: [1,3,4]示例 2: 输入: [1,n…

人机环境生态系统智能的流动性

一般来说&#xff0c;流动性可以理解为事物在空间或时间上的转移、变化或运动。在人机环境生态系统中&#xff0c;流动性可以涉及以下几个方面&#xff1a; 信息流动&#xff1a;数据、消息、知识等在系统中的传递和交换。这可能包括传感器收集的数据传输到处理中心&#xff0c…