【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]); // pivot
    return 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算法和三中位数主元选择可以有效优化快速排序的性能,并避免最坏情况的出现。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Typescript 中 tsconfig.json 无法写入文件,因为它会覆盖输入文件

先来看看问题 在开发ts项目的时候&#xff0c;包错提示无法写入文件 tsconfig.json无法写入文件"url"因为它会覆盖输入文件 这是tsconfig.json文件配置问题&#xff0c;需要加入下面的配置就好了&#xff1a; {"compilerOptions": {"outDir": …

python爬虫入门教程(一)

上一篇文章讲了爬虫的工作原理&#xff0c;这篇文章以后就要重点开始讲编程序了。 简单爬虫的的两个步骤&#xff1a; 使用HTTPRequest工具模拟HTTP请求&#xff0c;接收到返回的文本。用于请求的包有: requests、urllib等。 对接收的文本进行筛选,获取想要的内容。用户筛选文…

操作系统期末复习整理知识点

操作系统的概念&#xff1a;①控制和管理整个计算机系统的硬件和软件资源&#xff0c;并合理地组织调度计算机的工作和资源的分配&#xff1b;②提供给用户和其他软件方便的接口和环境&#xff1b;③是计算机中最基本的系统软件 功能和目标&#xff1a; ①操作系统作为系统资源…

专业场景化ChatGPT论文润色提示词指令,更精准、更有效辅助学术论文撰写

大家好&#xff0c;感谢关注。我是七哥&#xff0c;一个在高校里不务正业&#xff0c;折腾学术科研AI实操的学术人。可以添加我&#xff08;yida985&#xff09;交流学术写作或ChatGPT等AI领域相关问题&#xff0c;多多交流&#xff0c;相互成就&#xff0c;共同进步。 在学术写…

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

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

Spring Boot通过自定义注解和Redis+Lua脚本实现接口限流

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

【CS.AI】AI引领编程新时代:深度探索GitHub Copilot

文章目录 引言0. TOP TAKEAWAYS 重要要点1. Copilot的基本功能2. 技术原理3. 优势与局限优势局限 4. 使用体验4.1 初次使用4.2 在 JetBrains 全家桶中使用 GitHub Copilot1. 安装插件2. 配置插件3. 使用 GitHub Copilot 4.3 日常开发4.4 体验与反馈 5. 对开发者生态系统的影响5…

深度学习500问——Chapter10:迁移学习(1)

文章目录 11.1 迁移学习基础知识 11.1.1 什么是迁移学习 11.1.2 为什么需要迁移学习 11.1.3 迁移学习的基本问题有哪些 11.1.4 迁移学习有哪些常用概念 11.1.5 迁移学习与传统机器学习有什么区别 11.1.6 迁移学习的核心及度量准则 11.1.7 迁移学习与其他概念的区别 11.1.8 什么…

【设计模式】行为型设计模式之 状态模式,带你探究有限状态机FSM的三种实现方式

什么是有限状态机 Finite state Machine FSM 简称状态机&#xff1a;状态机由三部分组成&#xff0c;状态(State) 事件(Event) 和动作(Action)组成。 其中事件也被称为转移条件&#xff0c;事件触发状态的转移和动作的执行。不过动作不是必须的&#xff0c;也可能只存在状态转…

人工智能与能源约束的矛盾能否化解

以下文章来源&#xff1a;澎湃新闻 人工智能技术在台前展示的是比特世界的算力、算法和数据&#xff0c;但其“轻盈的灵魂”背后则是土地、能源和水等物理世界“沉重的肉身”。根据本文三种情境的模拟测算&#xff0c;未来人工智能发展需要可持续的巨量能源支撑&#xff0c;能源…

DS:树与二叉树的相关概念

欢迎来到Harper.Lee的学习世界&#xff01;博主主页传送门&#xff1a;Harper.Lee的博客主页想要一起进步的uu可以来后台找我哦&#xff01; 一、树的概念及其结构 1.1 树的概念亲缘关系 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限节点…

社区物资交易互助平台的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;基础数据管理&#xff0c;论坛管理&#xff0c;公告信息管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;论坛&#xff0c;求助留言板&#xff0c;公…

OpenCV 双目相机标定

文章目录 一、简介1.1单目相机标定1.2双目相机标定二、实现代码三、实现效果参考资料一、简介 1.1单目相机标定 与单目相机标定类似,双目标定的目的也是要找到从世界坐标转换为图像坐标所用到的投影P矩阵各个系数(即相机的内参与外参)。具体过程如下所述: 1、首先我们需要…

王学岗鸿蒙开发(北向)——————(四、五、六)ArkUi声明式组件

普通组件 1,注意&#xff0c;如上图&#xff0c;build只能有一个根节点 2,Entry表示程序的入口 Component表示自定义的组件 Preview表示可以预览 3&#xff0c;图片存放的地方 4&#xff0c; Image组件最好只给宽度&#xff0c;给了高度又给宽度容易失真。 build() {Row() {/…

论文降痕指南:如何有效降低AIGC率

随着 AI 技术迅猛发展&#xff0c;各种AI辅助论文写作的工具层出不穷&#xff01; 为了防止有人利用AI工具进行论文代写&#xff0c;在最新的学位法中已经明确规定“已经获得学位者&#xff0c;在获得该学位过程中如有人工智能代写等学术不端行为&#xff0c;经学位评定委员会…

使用 ESP32 和 PlatformIO (arduino框架)实现 Over-the-Air(OTA)固件更新

使用 ESP32 和 PlatformIO 实现 Over-the-Air&#xff08;OTA&#xff09;固件更新 摘要&#xff1a; 本文将介绍如何在 ESP32 上使用 PlatformIO 环境实现 OTA&#xff08;Over-the-Air&#xff09;固件更新。OTA 更新使得在设备部署在远程位置时&#xff0c;无需物理接触设…

Python 基于阿里云的OSS对象存储服务实现本地文件上云框架

Python 基于阿里云的OSS对象存储服务实现将文件上云框架 文章目录 Python 基于阿里云的OSS对象存储服务实现将文件上云框架一、前言二、阿里云配置1、获取用户AKEY和AKeySecret2、创建Bucket 三、Python 阿里云oss上云框架1、安装oss2依赖库2、阿里云oss python 一、前言 未来…

Mysql 中的case-when

什么是 case-when case-when 是一种 sql 语句中的语法结构,结构如下&#xff1a; case 字段名 when 值 then 字段名|值 ... else 字段名|值 end case when 主要用于数据的 行列转换&#xff08;把一列数据转换为多列&#xff09; 前置条件&#xff1a; -- 表…

基于统一二维电子气密度表达式的通用MIS-HEMT紧凑模型

来源&#xff1a;A Compact Model for Generic MIS-HEMTs Based on the Unified 2DEG Density Expression&#xff08;TED 14年&#xff09; 摘要 本文提出了一种针对二维电子气&#xff08;ns&#xff09;密度和费米能级&#xff08;E_f&#xff09;的解析表达式&#xff0c…

【Pytorch】计算机视觉项目——卷积神经网络TinyVGG模型图像分类(如何使用自定义数据集)

目录 一、前言二、工作流程回顾三、详细步骤流程1. 环境配置2. 数据准备数据集下载数据存储结构&路径查看图片 3. 数据转换4. 自定义数据集&#xff08;Custom Dataset &#xff09;4.1 方法一&#xff1a;使用ImageFolder加载数据集信息查看张量转图片创建DataLoader 4.2 …