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

2024-06-09 11:04

本文主要是介绍【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算法和三中位数主元选择可以有效优化快速排序的性能,并避免最坏情况的出现。

这篇关于【CS.AL】八大排序算法 —— 快速排序全揭秘:从基础到优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1044991

相关文章

快速修复一个Panic的Linux内核的技巧

《快速修复一个Panic的Linux内核的技巧》Linux系统中运行了不当的mkinitcpio操作导致内核文件不能正常工作,重启的时候,内核启动中止于Panic状态,该怎么解决这个问题呢?下面我们就... 感谢China编程(www.chinasem.cn)网友 鸢一雨音 的投稿写这篇文章是有原因的。为了配置完

Python利用ElementTree实现快速解析XML文件

《Python利用ElementTree实现快速解析XML文件》ElementTree是Python标准库的一部分,而且是Python标准库中用于解析和操作XML数据的模块,下面小编就来和大家详细讲讲... 目录一、XML文件解析到底有多重要二、ElementTree快速入门1. 加载XML的两种方式2.

MySQL索引的优化之LIKE模糊查询功能实现

《MySQL索引的优化之LIKE模糊查询功能实现》:本文主要介绍MySQL索引的优化之LIKE模糊查询功能实现,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一、前缀匹配优化二、后缀匹配优化三、中间匹配优化四、覆盖索引优化五、减少查询范围六、避免通配符开头七、使用外部搜索引擎八、分

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

mysql的基础语句和外键查询及其语句详解(推荐)

《mysql的基础语句和外键查询及其语句详解(推荐)》:本文主要介绍mysql的基础语句和外键查询及其语句详解(推荐),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋... 目录一、mysql 基础语句1. 数据库操作 创建数据库2. 表操作 创建表3. CRUD 操作二、外键

Python基础语法中defaultdict的使用小结

《Python基础语法中defaultdict的使用小结》Python的defaultdict是collections模块中提供的一种特殊的字典类型,它与普通的字典(dict)有着相似的功能,本文主要... 目录示例1示例2python的defaultdict是collections模块中提供的一种特殊的字

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n