【CS.AL】八大排序算法 —— 快速排序全揭秘:从基础到优化

在这里插入图片描述

文章目录

    • 1. 快速排序简介
      • 1.1 定义
      • 1.2 时间复杂度
      • 1.3 相关资源
    • 2. 最优的Partition算法 🔥
      • 2.1 Introsort简介
      • 2.2 过程示例
    • 3. 非递归快速排序
      • 3.1 实现
    • 4. 递归快速排序
      • 4.1 实现
    • 5. 有问题的Partition
      • 5.1 实现
    • 6. 三中位数主元选择
      • 6.1 实现
    • 7. 总结

1. 快速排序简介

1.1 定义

快速排序:快速排序也采用分治策略,选择一个基准元素,将数组分成比基准小和比基准大的两部分,再对两部分递归地进行排序。快速排序的平均时间复杂度为O(n log n),是目前应用广泛的排序算法之一。

1.2 时间复杂度

  • 最坏情况:O(n²)
  • 平均情况:O(n log₂n)
  • 最佳情况:O(n log₂n)

1.3 相关资源

912. 排序数组 - 力扣(LeetCode)

2. 最优的Partition算法 🔥

2.1 Introsort简介

Introsort(内排序)从快速排序开始作为主要排序算法。在最坏情况下(例如,数组已经排序或接近排序),快速排序可能退化为O(n²)时间复杂度。为了避免快速排序的最坏情况,Introsort引入了一个最大递归深度。当递归深度超过这个阈值时,算法切换到堆排序或归并排序,以确保更好的最坏情况性能。

template <typename Tp>
int partition(vector<Tp>& nums, int lIdx, int rIdx) {int randomIndex = lIdx + rand() % (rIdx - lIdx + 1);std::swap(nums[randomIndex], nums[rIdx]);Tp pivot = nums[rIdx];int lBoundary = lIdx;int rBoundary = rIdx - 1;for(; ; ++lBoundary, --rBoundary){for (; lBoundary <= rBoundary && nums[lBoundary] < pivot; ++lBoundary) {}for (; lBoundary <= rBoundary && nums[rBoundary] > pivot; --rBoundary) {}if (lBoundary > rBoundary) {break;}std::swap(nums[lBoundary], nums[rBoundary]);}std::swap(nums[rIdx], nums[lBoundary]);return lBoundary;
}

2.2 过程示例

  • 假设 nums = [7, 3, 5, 1, 2, 6, 4],随机选择的pivot下标为5,即6与最右的4交换,得到 nums = [7, 3, 5, 1, 2, 4, 6]
  • 分区指针起始如图:left (lIdx) -> 7, 3, 5, 1, 2, 4 <- right (rIdx), 6(pivot)
  • 左指针移动到第一个大于或等于主元的元素(即7),右指针移动到第一个小于或等于主元的元素(为4):left (lIdx) -> 7, 3, 5, 1, 2, 4 <- right (rIdx), 6(pivot)
  • 交换左右指针处的元素:left (lIdx) -> 4, 3, 5, 1, 2, 7 <- right (rIdx), 6(pivot)
  • 继续该过程,直到左右指针相遇:4, 3, 5, 1, 2 <- right (rIdx), left (lIdx) -> 7, 6(pivot)
  • 将枢轴元素(当前位于右指针处)与左指针处的元素交换(6和7交换)。

3. 非递归快速排序

3.1 实现

template <typename Tp>
void quickSort(vector<Tp>& nums) {std::stack<std::pair<int, int>> stack;stack.push(std::make_pair(0, nums.size() - 1));while (!stack.empty()) {std::pair<int, int> current = stack.top();stack.pop();int lIdx = current.first;int rIdx = current.second;if (lIdx < rIdx) {int boundary = partition(nums, lIdx, rIdx);stack.push(std::make_pair(lIdx, boundary - 1));stack.push(std::make_pair(boundary + 1, rIdx));}}
}

4. 递归快速排序

4.1 实现

template <typename Tp>
void qSortRecursion(vector<Tp>& nums, const int& lIdx, const int& rIdx) {if (lIdx < rIdx) {int boundary = partition(nums, lIdx, rIdx);qSortRecursion(nums, lIdx, boundary - 1);qSortRecursion(nums, boundary + 1, rIdx);}
}template <typename Tp>
void quickSort(vector<Tp>& nums) {qSortRecursion(nums, 0, nums.size() - 1);
}

5. 有问题的Partition

5.1 实现

大量重复元素会超时:

template <typename Tp>
int partition(vector<Tp>& nums, int lIdx, int rIdx) {// 较为有序时, 避免超时int randIdx = lIdx + rand() % (rIdx - lIdx + 1);std::swap(nums[randIdx], nums[rIdx]);int pivot = nums[rIdx];int boundary = lIdx;for (int idx = lIdx; idx < rIdx; ++idx) {if (nums[idx] < pivot) {std::swap(nums[idx], nums[boundary]);++boundary;}}std::swap(nums[boundary], nums[rIdx]); // pivotreturn boundary;
}

通过内排序Introsort修复:

template <typename Tp>
void quickSort(vector<Tp>& nums) {double recThreshold = log10(nums.size()) / log10(2);int recDepth = 0;std::stack<std::pair<int, int>> stack;stack.push(std::make_pair(0, nums.size() - 1));while (!stack.empty()) {++recDepth;if (recDepth >= recThreshold) {heapSort(nums);break;}std::pair<int, int> current = stack.top();stack.pop();int lIdx = current.first;int rIdx = current.second;if (lIdx < rIdx) {int boundary = partition(nums, lIdx, rIdx);stack.push(std::make_pair(lIdx, boundary - 1));stack.push(std::make_pair(boundary + 1, rIdx));}}
}

6. 三中位数主元选择

6.1 实现

template <typename Tp>
int choosePivot(vector<Tp>& nums, int lIdx, int rIdx) {int mid = lIdx + (rIdx - lIdx) / 2;if (nums[lIdx] > nums[mid]) {std::swap(nums[lIdx], nums[mid]);}if (nums[mid] > nums[rIdx]) {std::swap(nums[mid], nums[rIdx]);}if (nums[lIdx] > nums[mid]) {std::swap(nums[lIdx], nums[mid]);}return mid;
}template <typename Tp>
int partition(vector<Tp>& nums, int lIdx, int rIdx) {int pivotIdx = choosePivot(nums, lIdx, rIdx);std::swap(nums[pivotIdx], nums[rIdx]);Tp pivot = nums[rIdx];int lBoundary = lIdx;int rBoundary = rIdx - 1;for(; ; ++lBoundary, --rBoundary){for (; lBoundary <= rBoundary && nums[lBoundary] < pivot; ++lBoundary) {}for (; lBoundary <= rBoundary && nums[rBoundary] > pivot; --rBoundary) {}if (lBoundary > rBoundary) {break;}std::swap(nums[lBoundary], nums[rBoundary]);}std::swap(nums[rIdx], nums[lBoundary]);return lBoundary;
}

7. 总结

快速排序作为一种现代化的排序算法,通过分治策略和递归实现,高效地解决了大多数排序问题。使用最优的Partition算法和三中位数主元选择可以有效优化快速排序的性能,并避免最坏情况的出现。

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

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

相关文章

新增FTP功能、支持添加Redis远程数据库,专业版新增网站监控和黑金主题,1Panel开源面板v1.10.10版本发布

2024年6月7日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel发布v1.10.10版本。 在这一版本中&#xff0c;1Panel新增了多项实用功能。社区版方面&#xff0c;新增了FTP功能、支持添加Redis远程数据库、支持设置压缩密码&#xff0c;并新增了清理镜像构建缓存的功能…

[ue5]建模场景学习笔记(5)——必修内容可交互的地形,交互沙(2)

1需求分析&#xff1a; 继续制作可交互沙子内容&#xff0c;前面我们已经让角色在指定区域留下痕迹&#xff0c;那么能否让区域移动起来&#xff0c;这样才能逐步满足角色走到哪里都能产生交互痕迹&#xff0c;满足更大的地图。 2.操作实现&#xff1a; 1.首先建立角色能产生…

翻译《The Old New Thing》- How do I obtain the computer manufacturer’s name?

How do I obtain the computer manufacturers name? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20081218-00/?p19783 Raymond Chen 2008年08月08日 如何获取计算机制造商的名字&#xff1f; 一位客户想要一种方法来确定计算机制造商的…

栈-20.有效的括号

20. 有效的括号 - 力扣&#xff08;LeetCode&#xff09; 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须…

C语言为什么在使⽤图形函数时要⾸先初始化图形模式?

一、问题 在TC 中开发图形程序&#xff0c;为什么要先执⾏ initgraph( ) &#xff0c;这个函数是⼲什么⽤的&#xff1f; 二、解答 在DOS 环境下&#xff0c;PC屏幕的显示分为字符模式和图形模式两种。TC 2.0 默认⼯作在字符模式下。在该模式下&#xff0c;屏幕由 25 ⾏ 80 列…

C# .NET 异步实现方式

一、异步编程模式 .NET 提供了执行异步操作的三种模式&#xff1a; 基于任务的异步模式 (TAP) &#xff0c;该模式使用单一方法表示异步操作的开始和完成。 TAP 是在 .NET Framework 4 中引入的。 这是在 .NET 中进行异步编程的推荐方法。 C# 中的 async 和 await 关键词以及 …

HTML+CSS+JS 密码灯登录表单

效果演示 实现了一个登录页面,包括一个标题、两个输入框(用户名和密码)、一个登录按钮和一个眼睛图标。点击眼睛图标可以显示或隐藏密码。页面背景有两个圆形的半透明元素,整个页面使用了flex布局,并且在水平和垂直方向上都居中对齐。登录框使用了阴影效果和圆角边框,并且…

linux centos redis-6.2.6一键安装及配置密码

linux centos redis-6.2.6一键安装及配置密码 redis基本原理一、操作阶段&#xff0c;开始安装 redis基本原理 redis作为非关系型nosql数据库&#xff0c;一般公司会作为缓存层&#xff0c;存储唯一会话id&#xff0c;以及请求削峰作用 一、数据结构 Redis支持多种数据结构&a…

【AI时代,生命修行】

今日分享&#x1f4d2;&#xff0c;AI时代&#xff0c; 生命 与 修行&#xff1a; 不要用太多时间去工作&#xff0c;尤其是在人工智能时代。如果谁还在用传统的线性的费时间的这种努力的工作方式&#xff0c;只能说太落伍了。 我只说给同频的朋友们无关的人请划走。因为很多…

Linux操作系统:Zookeeper在虚拟环境下的安装与部署

将 Zookeeper 安装到指定目录 // 将zookeeper解压到安装目录 $ tar –zxvf zookeeper-3.4.10.tar.gz –C /usr/local $ mv /usr/local/zookeeper-3.4.10.tar.gz /usr/local/zookeeper 设置 zookeeper 配置文件 // 创建 data 数据目录 $ mkdir /usr/local/zookeeper/data // …

JVM类加载机制详解(JDK源码级别)

提示&#xff1a;从JDK源码级别彻底剖析JVM类加载机制、双亲委派机制、全盘负责委托机制、打破双亲委派机制的程序、Tomcat打破双亲委派机制、tomcat自定义类加载器详解、tomcat的几个主要类加载器、手写tomcat类加载器 文章目录 前言一、loadClass的类加载大概有如下步骤二、j…

19-Nacos-服务实例的权重设置

19-Nacos-服务实例的权重设置 1.根据权重负载均衡&#xff1a; 1.服务器设备性能有差异&#xff0c;部分实例所在及其性能较高&#xff0c;有一些较差&#xff0c;我们希望性能好的机器承担更多的用户请求 Nacos提供了权重配置来控制访问频率&#xff0c;权重越大则访问频率…

Hadoop3:MapReduce源码解读之Map阶段的FileInputFormat的切片原理(2)

Job那块的断点代码截图省略&#xff0c;直接进入切片逻辑 参考&#xff1a;Hadoop3&#xff1a;MapReduce源码解读之Map阶段的Job任务提交流程&#xff08;1&#xff09; 4、FileInputFormat切片源码解析 切片入口 获取切片 获取切片最大的Size和切片最小的Size 判断文件是…

LeMeViT:具有可学习元令牌的高效ViT

本文提出使用可学习的元令牌来制定稀疏令牌&#xff0c;这有效地学习了关键信息&#xff0c;同时提高了推理速度。从技术上讲&#xff0c;主题标记首先通过交叉关注从图像标记中初始化。提出了双交叉注意&#xff08;DCA&#xff09;来促进图像令牌和元令牌之间的信息交换&…

SpringBoot2+Vue3开发课程审核流程系统

SpringBoot2Vue3开发课程审核流程系统 简介 此系统实现了课程审核全流程功能并使用了Activiti7工作流技术&#xff0c;功能包含&#xff1a;课程管理、用户管理、流程定义、课程审核&#xff08;我的申请、我的代办、我的已办&#xff09; 功能介绍 课程管理 对课程信息的管…

git凭证

默认是manager # 将凭证缓存到内存中&#xff0c;默认缓存15分钟 git config --global credential.helper cache# 将凭证存储到磁盘上的纯文本文件中 git config --global credential.helper store# 使用 Git 凭证管理器 git config --global credential.helper manager-core查…

Java面试题:通过实例说明面向对象中多态的概念,并讨论在设计类时如何利用多态提高代码的灵活性

多态&#xff08;Polymorphism&#xff09;是面向对象编程的三大特性之一&#xff0c;它允许我们使用父类类型的引用指向子类对象&#xff0c;调用方法时&#xff0c;会根据实际对象的类型来决定调用哪个类的方法。这种特性使得程序具有更好的可扩展性和可维护性。 实例说明多…

生成式人工智能 - 文本反转(Textual Inversion):一种微调稳定扩散模型的方法

一、简述 大型文本到图像稳定扩散模型已经展示了前所未有的能力,可以使用文本提示合成新场景。这些文本到图像模型提供了通过自然语言指导创作的自由。然而,它们的使用受到用户描述特定或独特场景、艺术创作或新实体产品的能力的限制。很多时候,用户被限制行使她的艺术自由来…

一进三出学生公寓电表功能介绍

学生公寓一进三出电表石家庄光大远通电气有限公司公寓网络智能支持预付费功能。可设置基础金额&#xff0c;免费使用&#xff1b;可对剩余金额进行四级报警&#xff1a;余额不足报警&#xff08;一级&#xff09;、预跳闸报警&#xff08;二级&#xff09;、欠费报警&#xff0…

内部类(超详细)

内部类 一:初始内部类 (1)什么是内部类? 类的五大成员:属性、方法、构造方法、代码块、内部类 举例:在A类的内部定义B类,B类就被称为内部类 public class Outer {// 外部类public class Inter {// 内部类} } public class Test {// 外部其他类public static void m…