当前位置: 首页 > news >正文

【算法】分治 - 快速排序



快乐的流畅:个人主页


个人专栏:《算法神殿》《数据结构世界》《进击的C++》

远方有一堆篝火,在为久候之人燃烧!

文章目录

  • 引言
  • 一、颜色分类
  • 二、排序数组
  • 三、数组中的第k个数
  • 四、最小的k个数
  • 总结

引言

本节主要介绍快速排序(三路划分,随机取key),以及它的变形算法——快速选择算法

一、颜色分类


细节:快速排序中标准的partition(三路划分)

  • 设置三个指针 left,cur,right
  • 划分为三个区域[0, left - 1],[left, right],[right + 1, n-1]
  • [0, left - 1]:元素小于key
  • [left, right]:元素等于key
  • [right + 1, n-1]:元素大于key
  • left和right用来维护(等于key的)中路元素区域的左右两端,cur用来扫描数组
class Solution
{
public:void sortColors(vector<int>& nums){int left = 0, cur = 0, right = nums.size() - 1;while(cur <= right){if(nums[cur] == 0) swap(nums[left++], nums[cur++]);else if(nums[cur] == 2) swap(nums[right--], nums[cur]);else ++cur;}}
};

二、排序数组


思路:

  • 递归出口:区间只有一个元素或者不存在
  • 随机选key:利用rand函数,记得提前srand种下随机数种子
  • 三路划分:三指针维护区间
  • 分治:继续递归[begin, left - 1],[right + 1, end]两个区间
class Solution
{
public:int getKey(vector<int>& nums, int left, int right){int keyi = rand() % (right - left + 1) + left;return nums[keyi];}void quickSort(vector<int>& nums, int begin, int end){if(begin >= end) return;int key = getKey(nums, begin, end);//随机选keyint cur = begin, left = begin, right = end;while(cur <= right){if(nums[cur] < key) swap(nums[left++], nums[cur++]);else if(nums[cur] > key) swap(nums[right--], nums[cur]);else cur++;}quickSort(nums, begin, left - 1);quickSort(nums, right + 1, end);}vector<int> sortArray(vector<int>& nums){srand(0);//种下随机数种子quickSort(nums, 0, nums.size() - 1);return nums;}
};

三、数组中的第k个数


思路:TopK问题有三种解法

  1. 排序——O(NlogN)
  2. 堆——O(NlogK)
  3. 快速选择——O(N)

堆版本

细节:建大堆 + k-1次删除

class Solution
{
public:void AdjustDown(vector<int>& nums, int parent){int n = nums.size(), child = 2 * parent + 1;while(child < n){if(child + 1 < n && nums[child] < nums[child + 1]) ++child;if(nums[parent] < nums[child])//建大堆{swap(nums[parent], nums[child]);parent = child;child = 2 * parent + 1;}else break;}}int findKthLargest(vector<int>& nums, int k){int n = nums.size();for(int i=(n-1-1)/2; i>=0; --i){AdjustDown(nums, i);}while(--k)//执行k-1次{swap(nums[0], nums[nums.size() - 1]);nums.pop_back();AdjustDown(nums, 0);}return nums[0];}
};

快速选择版本

细节:

  • 从最右边开始数,k落在右区域,则继续递归找第k大
  • k落在中区域,则直接更新结果
  • k落在左区域,则继续递归找第k-b-c大
class Solution
{int ret;
public:int GetKey(vector<int>& nums, int begin, int end){int keyi = rand() % (end - begin + 1) + begin;return nums[keyi];}void qucikSelect(vector<int>& nums, int begin, int end, int k){if(begin > end) return;int key = GetKey(nums, begin, end);int left = begin, cur = begin, right = end;while(cur <= right){if(nums[cur] < key) swap(nums[left++], nums[cur++]);else if(nums[cur] > key) swap(nums[right--], nums[cur]);else cur++;}if(k <= end - right) qucikSelect(nums, right + 1, end, k);else if(k <= end - left + 1) ret = key;else qucikSelect(nums, begin, left - 1, k - (end - left + 1));}int findKthLargest(vector<int>& nums, int k){srand(0);qucikSelect(nums, 0, nums.size() - 1, k);return ret;}
};

四、最小的k个数



细节:

  • 从最左边开始数,k落在左区域,则继续递归找最小的k个元素
  • k落在中区域,则直接返回前k个元素
  • k落在右区域,则继续递归找最小的k-a-b个元素
class Solution
{
public:int getKey(vector<int>& nums, int begin, int end){int keyi = rand() % (end - begin + 1) + begin;return nums[keyi];}void quickSelect(vector<int>& nums, int begin, int end, int k){if(begin >= end) return;int key = getKey(nums, begin, end);int left = begin, cur = begin, right = end;while(cur <= right){if(nums[cur] < key) swap(nums[left++], nums[cur++]);else if(nums[cur] > key) swap(nums[right--], nums[cur]);else cur++;}if(k <= left - begin) quickSelect(nums, begin, left - 1, k);else if(k <= right + 1 - begin) return;else quickSelect(nums, right + 1, end, k - (right + 1 - begin));}vector<int> inventoryManagement(vector<int>& nums, int k){srand(0);quickSelect(nums, 0, nums.size() - 1, k);return {nums.begin(), nums.begin() + k};}
};

总结

快速排序,随机选key,保证了时间复杂度逼近O(NlogN),三路划分,是为了处理重复大量元素。

快速选择,是基于快速排序的变形算法,在解决TopK问题有着O(N)的时间复杂度,极其高效!


真诚点赞,手有余香

http://www.lryc.cn/news/352420.html

相关文章:

  • 设计模式13——桥接模式
  • 第十六讲:数据在内存中的存储
  • 【EXCEL_VBA_基础知识】15 使用ADO操作外部数据
  • 如何在Spring中配置Bean?
  • 深入学习 torch.distributions
  • Java中的判断校验非空问题
  • webman使用summernote富文本编辑器
  • jQuery里添加事件 (代码)
  • Java数组的使用
  • 如何参与github开源项目并提交PR
  • 拼多多携手中国农业大学,投建陕西佛坪山茱萸科技小院
  • 技术前沿 |【自回归视觉模型ImageGPT】
  • Manjaro linux install RedisGUI (RedisInsight)亲测2024-5-25
  • debian/control文件中常见字段的介绍
  • c++题目_农场和奶牛
  • DDD领域设计在“图生代码”中的应用实践
  • LabVIEW舱段测控系统开发
  • [leetcode]第 n个丑数
  • STM32-电灯,仿真
  • 《SpringBoot》系列文章目录
  • 牛客小白月赛94VP
  • php 亚马逊AWS-S3对象存储上传文件
  • electron-01 基础及NPM相关配置
  • Foxit PDF Editor Pro福昕PDF编辑器Pro:重塑您的文档编辑体验
  • VUE 页面生命周期基本知识点
  • windows查看mysql的版本(三种方法)
  • Redis批量删除指定前缀的key
  • 机器学习实验------Adaboost算法
  • 点云处理中阶 Octree模块
  • Nginx实现负载均衡与故障检查自动切换