【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

相关文章

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

springboot security快速使用示例详解

《springbootsecurity快速使用示例详解》:本文主要介绍springbootsecurity快速使用示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录创www.chinasem.cn建spring boot项目生成脚手架配置依赖接口示例代码项目结构启用s

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

一文详解SpringBoot响应压缩功能的配置与优化

《一文详解SpringBoot响应压缩功能的配置与优化》SpringBoot的响应压缩功能基于智能协商机制,需同时满足很多条件,本文主要为大家详细介绍了SpringBoot响应压缩功能的配置与优化,需... 目录一、核心工作机制1.1 自动协商触发条件1.2 压缩处理流程二、配置方案详解2.1 基础YAML

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

C#基础之委托详解(Delegate)

《C#基础之委托详解(Delegate)》:本文主要介绍C#基础之委托(Delegate),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 委托定义2. 委托实例化3. 多播委托(Multicast Delegates)4. 委托的用途事件处理回调函数LINQ