快速排序算法

embedded/2024/12/28 11:01:27/

一、快速排序简介

**快速排序(Quick Sort)**是一种基于分治法的高效排序算法,由C. A. R. Hoare于1960年提出。它的平均时间复杂度为O(n log n),在实际应用中,由于其优秀的性能和较高的效率,被认为是排序算法中的佼佼者。


二、算法思想

快速排序的核心思想是:

  1. 选择主元(Pivot):从待排序序列中选择一个基准元素,称为主元。
  2. 分区操作:将序列划分为两个子序列,使得左子序列中所有元素都小于主元,右子序列中所有元素都大于或等于主元。
  3. 递归排序:对左右子序列分别递归地进行快速排序。

三、算法步骤

  1. 初始序列:给定一个待排序的数组或序列。
  2. 选择主元:通常选择序列的第一个、最后一个、中间位置或随机位置的元素作为主元。
  3. 分区(Partition)
    • 设置两个指针,分别指向序列的起始位置和结束位置。
    • 从左到右移动左指针,找到第一个大于等于主元的元素。
    • 从右到左移动右指针,找到第一个小于等于主元的元素。
    • 交换左、右指针所指向的元素。
    • 重复上述过程,直到左指针超过右指针。
  4. 交换主元:将主元放到正确的位置(通常是右指针所在的位置)。
  5. 递归调用:对主元左右两侧的子序列重复上述过程,直到序列有序。

四、算法实现

以下是快速排序的Java实现:

java">public class QuickSortExample {public static void quickSort(int[] arr, int left, int right) {if (left < right) {// 分区操作,返回主元的位置int pivotIndex = partition(arr, left, right);// 对左子序列进行快速排序quickSort(arr, left, pivotIndex - 1);// 对右子序列进行快速排序quickSort(arr, pivotIndex + 1, right);}}private static int partition(int[] arr, int left, int right) {// 选择最右边的元素作为主元int pivot = arr[right];int i = left - 1;for (int j = left; j < right; j++) {// 将小于主元的元素放到左边if (arr[j] <= pivot) {i++;swap(arr, i, j);}}// 将主元放到正确的位置swap(arr, i + 1, right);return i + 1; // 返回主元的位置}private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}// 测试快速排序public static void main(String[] args) {int[] arr = { 9, 3, 7, 5, 6, 4, 8, 2 };quickSort(arr, 0, arr.length - 1);System.out.println(Arrays.toString(arr));}
}

运行结果:

[2, 3, 4, 5, 6, 7, 8, 9]

五、动画演示

为了更直观地理解快速排序,可以想象以下过程:

  1. 初始序列[9, 3, 7, 5, 6, 4, 8, 2]
  2. 选择主元:选择2(最后一个元素)作为主元。
  3. 分区结果:经过分区,序列变为[2, 3, 7, 5, 6, 4, 8, 9]2的位置确定。
  4. 递归排序:对左子序列[2]和右子序列[3, 7, 5, 6, 4, 8, 9]进行递归排序。

依此类推,最终序列有序。


六、性能分析

  • 时间复杂度
    • 平均时间复杂度:O(n log n)
    • 最坏时间复杂度:O(n²),当每次选取的主元都是序列中的最大或最小值时,可能退化为冒泡排序。
  • 空间复杂度:O(log n),递归调用栈的深度。
  • 特点
    • 不稳定排序:快速排序不是稳定的排序算法,可能改变相等元素的相对位置。
    • 原地排序:不需要额外的辅助空间。

七、优化策略

  1. 三数取中:选择主元时,取左、中、右三个元素的中间值,减少最坏情况出现的概率。
  2. 随机化主元:随机选择主元,避免序列有序导致的最坏情况。
  3. 尾递归优化:减少递归调用的深度,优化空间复杂度。
  4. 小规模优化:对于小规模的子序列,可以改用插入排序,提高效率。

八、力扣(LeetCode)相关题目

在LeetCode上,有多道题目涉及到快速排序或其应用。以下列举一些典型的题目:

1. 215. 数组中的第K个最大元素

  • 题目描述:找到数组中第K大的元素。
  • 解题思路
    • 快速选择算法(Quick Select):快速排序的变种,只对一侧进行递归,效率更高。
    • 实现:利用快速排序的分区思想,在分区后确定主元的位置与目标位置的关系,决定递归方向。

示例代码:

java">class Solution {public int findKthLargest(int[] nums, int k) {return quickSelect(nums, 0, nums.length - 1, nums.length - k);}private int quickSelect(int[] nums, int left, int right, int index) {int pivotIndex = partition(nums, left, right);if (pivotIndex == index) {return nums[pivotIndex];} else if (pivotIndex < index) {return quickSelect(nums, pivotIndex + 1, right, index);} else {return quickSelect(nums, left, pivotIndex - 1, index);}}private int partition(int[] nums, int left, int right) {int pivot = nums[right], i = left - 1;for (int j = left; j < right; j++) {if (nums[j] <= pivot) {swap(nums, ++i, j);}}swap(nums, ++i, right);return i;}private void swap(int[] nums, int i, int j) {int temp= nums[i];nums[i] = nums[j];nums[j] = temp;}
}

2. 912. 排序数组

  • 题目描述:对数组进行排序。
  • 解题思路
    • 经典快速排序:直接实现快速排序算法,对数组进行排序。

3. 973. 最接近原点的 K 个点

  • 题目描述:在平面上找到离原点最近的K个点。
  • 解题思路
    • 快速选择算法:利用快速选择找到距离最小的K个点。
    • 实现:根据距离对点进行分区,类似于快速排序的思想。

4. 347. 前 K 个高频元素

  • 题目描述:找到数组中出现频率最高的K个元素。
  • 解题思路
    • 基于快速选择的优化:对频率进行排序,使用快速选择找到前K个高频元素。

5. 75. 颜色分类

  • 题目描述:只有0、1、2三种颜色的数组,要求进行排序。
  • 解题思路
    • 三路快排:快速排序的变种,将数组分为三部分,更高效地完成排序。

示例代码:

java">class Solution {public void sortColors(int[] nums) {int zero = -1, one = 0, two = nums.length;while (one < two) {if (nums[one] == 0) {swap(nums, ++zero, one++);} else if (nums[one] == 2) {swap(nums, one, --two);} else {one++;}}}private void swap(int[] nums, int i, int j) {int temp= nums[i];nums[i] = nums[j];nums[j] = temp;}
}

九、总结

  • 理解分治思想:快速排序的核心是分治,将复杂问题分解为小问题。
  • 掌握分区算法:分区操作是快速排序的关键,熟练掌握有助于解决类似的问题。
  • 应用变种算法:快速排序有多种变种,如快速选择、三路快排,针对不同问题有效。
  • 注意最坏情况:在实现快速排序时,要注意最坏情况下的优化,避免算法性能下降。

十、练习与提高

为了更深入地理解快速排序,建议在LeetCode上尝试以下步骤:

  1. 手动实现快速排序:不要直接使用库函数,亲自编码,实现从选择主元、分区到递归排序的全过程。
  2. 解决相关问题:尝试做与快速排序相关的题目,加深对算法的理解。
  3. 优化算法:在基本实现的基础上,尝试加入三数取中、随机主元等优化策略。
  4. 比较不同算法:实现不同的排序算法,如归并排序、堆排序,比较它们的性能和适用场景。

十一、参考资料

  • 算法导论:深入了解排序算法的理论基础。
  • LeetCode题解:查看他人的解题思路,学习不同的实现方式。
  • 在线可视化工具:使用排序算法的可视化网站,帮助理解算法的执行过程。

希望以上内容能够帮助您深入理解快速排序算法及其在力扣题目中的应用。如有疑问,欢迎继续提问!


http://www.ppmy.cn/embedded/149423.html

相关文章

LLMs之o3:《Deliberative Alignment: Reasoning Enables Safer Language Models》翻译与解读

LLMs之o3&#xff1a;《Deliberative Alignment: Reasoning Enables Safer Language Models》翻译与解读 导读&#xff1a;2024年12月&#xff0c;这篇论文提出了一种名为“审慎式对齐 (Deliberative Alignment)”的新方法&#xff0c;旨在提高大型语言模型 (LLM) 的安全性。论…

【实验记录】动手实现一个简单的神经网络实验(一)

最近上了“神经网络与深度学习”这门课&#xff0c;有一个自己动手实现调整神经网络模型的实验感觉还挺有记录意义&#xff0c;可以帮我巩固之前学习到的理论知识&#xff0c;所以就打算记录一下。 实验大概是使用LeNet&#xff08;卷积神经网络&#xff09;对MINIST数据集做图…

python实战案例笔记:统计出数据中路劲下没有文件的文件夹

数据样例&#xff1a;&#x1f447;有如下excel数据 需求&#xff1a;有如下excel&#xff0c;a.xls&#xff0c;统计出路劲下没有文件的路劲 详细实现代码&#xff1a; import os from openpyxl import Workbook from datetime import datetimedef get_empty_dirs(paths):# …

小米加速AI布局,搭建GPU万卡集群,近屿智能带您走近AI大模型

小米公司近期宣布正在加速构建GPU万卡集群&#xff0c;这一举措标志着小米在AI大模型领域的战略升级。据内部人士透露&#xff0c;该计划已在雷军的领导下秘密推进数月。雷军指出&#xff0c;在AI硬件的发展中&#xff0c;手机才是核心&#xff0c;小米在这一领域的全面投入是战…

Git在软件开发中的核心作用:如何利用Git进行版本控制和团队协作?

在当今数字化时代&#xff0c;软件开发项目日益复杂&#xff0c;团队协作的紧密程度和效率对于项目的成功交付起着至关重要的作用。而Git&#xff0c;作为一款强大的分布式版本控制系统&#xff0c;已经成为软件开发领域不可或缺的工具。它不仅能够帮助开发者高效地管理代码版本…

【软件工程】十万字知识点梳理 | 期末复习专用

原创文章,禁止转载。 文章目录 图CRC卡片用例图类图状态图活动图泳道图软件质量因素自顶向下集成自底向上集成人员与工作量之间的关系时序图关键路径软件结构基本路径测试判定表数据流图(DFD)体系结构设计问题数据字典挣值分析等价划分程序流程图PAD | N-S燃尽图甘特图对象模…

MySQL Workbench菜单汉化为中文

默认情况下&#xff0c;安装完成的MySQL Workbench的菜单为英文&#xff0c;今天介绍一个简单易操作的方法&#xff0c;将MySQL Workbench菜单汉化为中文。 一、查找MySQL Workbench菜单标记文件main_menu.xml 1. 默认情况下&#xff0c;MySQL Workbench的安装路径为&#xff…

VSCode使用deepseek-v3

2024年12月27日&#xff0c;deekseep-v3发布了。顶级benchmark和实用体验&#xff0c;显著降低的训练成本&#xff0c;注册后免费送token额度。很诱人。 生成 deepseek-v3 API https://www.deepseek.com/ https://platform.deepseek.com/usage 创建一个新的 api key VSCo…