leetcode - 分治-三路划分快速排序总结

Java 快速排序:分治思想的最强代表 本文详细解析了快速排序的核心思想和实现细节,重点介绍Lomuto分区法。通过ASCII图解演示了数组[10,7,8,9,1,5]的完整排序过程: 每次选取最右元素为pivot,通过双指针i/j划分"小于区"和"大于区" 当arr[j]<pivot时交换元素到i的右侧 递归处理左右子数组直至完成排序 强调"不动"操作的含义是维持大于区元素位置,仅移动小于区元素 文章还指出快排最坏时间复杂度O(n²)的成因及优化方法,并列出5个常见实现错误点,包括递归终止条件和边界处理。最后推荐LeetCode 912和215 阅读详情

系列文章目录

  1. leetcode - 双指针问题_leetcode双指针题目-优快云博客
  2. leetcode - 滑动窗口问题集_leetcode 滑动窗口-优快云博客
  3. 高效掌握二分查找:从基础到进阶-优快云博客
  4. leetcode - 前缀和_前缀和的题目-优快云博客
  5. 动态规划 - 斐波那契数列模型-优快云博客
  6. 位运算 #常见位运算总结 #题解-优快云博客
  7. 模拟 - #介绍 #题解-优快云博客

目录

系列文章目录

前言

一、分治思想

二、快排

三、题解

1、题1 颜色分类:

解法:三指针

参考代码:

2、题2 排序数组 :

解法一:快速排序

参考代码:

3、题3 数组中的第K个最大元素:

解法:

参考代码:

4、题4 库存管理 III:

解法一:排序

解法二:堆排序

解法三:快速排序算法

参考代码:

总结


前言

路漫漫其修远兮,吾将上下而求索;


大家可以先自己尝试做一下~

75. 颜色分类 - 力扣(LeetCode)

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

215. 数组中的第K个最大元素 - 力扣(LeetCode)

LCR 159. 库存管理 III - 力扣(LeetCode)


一、分治思想

分治,即分而治之;

将大问题拆解为相同或者相似的子问题,然后在这个子问题额基础上继续进行划分,直到划分到某一个位置为止(该子问题可以快速解决);

其中,排序之中,快排、归并排序就是典型的分治思想;

二、快排

想要更加详细地了解快排可以参考:数据结构与算法 - 快速排序 #hoare版本 #挖坑法 #lomuto前后指针 #三数取中 #三路划分 #introsort自省排序_lomuto法-优快云博客

当待排序列中有大量重复数据的时候,三路划分的思想类似于hoare 版本和lomuto 前后指针的结合;其核心思想是将数组中的数据划分为三段:小于基准值的数据、等于基准值的数据、大于基准值的数据,故而叫做三路划分算法;

数据结构与算法 - 快速排序 #hoare版本 #挖坑法 #lomuto前后指针 #三数取中 #三路划分 #introsort自省排序_lomuto法-优快云博客

当待排序列中有大量重复数据的时候,三路划分的思想类似于hoare 版本和lomuto 前后指针的结合;其核心思想是将数组中的数据划分为三段:小于基准值的数据、等于基准值的数据、大于基准值的数据,故而叫做三路划分算法;

使用三指针:left 、cur、 right

让left 指向等于key 区间的左下标,让right 指向等于 key区间的右下标;然后让cur 遍历整个数组;

三路划分版快排代码如下:

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>

using namespace std;

//三路划分快排
void QuickSort(vector<int>& a, int left, int right)
{
	//确保范围合法
	if (left >= right) return;

	//提前保存一下范围
	int begin = left, end = right;

	//随机值取基准值
	int keyi = left + rand() % (right - left + 1);
	swap(a[keyi], a[left]);
	keyi = left;

	int key = a[left];//基准值
	int cur = left + 1;
	while(cur <= right)
	{
		if (a[cur] < key)
		{
			swap(a[left], a[cur]);
			cur++;
			left++;
		}
		else if (a[cur] > key)
		{
			swap(a[right], a[cur]);
			//因为不知道a[right] 中数据地大小,所以right--,而 cur 不动
			right--;
		}
		else//与 key 相等
		{
			cur++;
		}
	}
	//[begin,left-1] 小于key 的区间
	//[left , right] 等于key 的区间
	//[right ,end] 大于key 的区间
	//继续递归
	QuickSort(a, begin, left - 1);
	QuickSort(a, right+1, end);
}

int main()
{
	//设置随机种子
	srand((unsigned int)time(NULL));
	vector<int> nums = { 10,2,90,8,76,23,12,11,5,3,2 };
	QuickSort(nums, 0, nums.size() - 1);
	for (auto e : nums)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

三、题解

1、题1 颜色分类:

75. 颜色分类 - 力扣(LeetCode)

本题的核心在于将数组分为三块;

解法:三指针

其实就是三路划分版本的快排;

设置三个变量: left 、cur、right

  • cur : 遍历数组中的数据
  • left : 标记 1 区域的最左侧
  • right :标记 1 区域的最右侧

而当 cur > right 的时候代表cur 遍历完了所有的数据,循环结束;故循环条件为 cur<=right;

那么区间 [0,left-1] 中全为0,区间 [left,right] 中全为1, 区间 [right+1,n-1] 中全为2;

参考代码:
    void sortColors(vector<int>& nums) 
    {
        //三路划分
        int left = 0, right = nums.size()-1, cur = 0;
        int key = 1;
        while(cur <= right)
        {
            if(nums[cur] < key) swap(nums[left++], nums[cur++]);
            else if(nums[cur] > key) swap(nums[right--] , nums[cur]);
            else cur++;
        }
    }

2、题2 排序数组 :

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

解法一:快速排序

其实使用排序算法就可以解决,而三路划分版本的快速排序是当数据大量重复时进行的优化;

我们再来过一遍三路划分的快速排序;

首先第一步是寻找一个基准值 key , 我们目标是将数组nums 划分为三个部分,一部分小于 key , 一部分等于 key , 另一部分大于key ;这样划分之后,便不用管等于key 的区域,因为中间的这部分区域中的数据一定是到了最终合适的位置上;

三指针+分类讨论:

三指针: left cur  right 

  • cur: 遍历数组
  • left : 指向等于 key 区间的最左区间
  • right : 指向等于 key 区间的最右区间

取基准值也有讲究,我们可以采用三数取中的方式,也可以是采用取随机值的方式;此处采用取随机值来取随机值;

参考代码:

    void QuickSort(vector<int>& nums, int left , int right)
    {
        //确保处理的空间合理
        if(left>=right) return;

        //保存范围
        int begin = left , end = right;
        int cur = left +1;
        
        //随机数取基准值
        int keyi = left + rand()%(right-left+1);
        swap(nums[left],nums[keyi]);

        int key = nums[left];
        left++;

        while(cur <= right)
        {
            if(nums[cur] < key)  swap(nums[cur++] , nums[left++]);
            else if(nums[cur] > key) swap(nums[cur] , nums[right--]);
            else cur++;
        }

        //begin , left-1 left,right right+1, end
        QuickSort(nums,begin , left-1);
        QuickSort(nums, right+1,end);
    }
    vector<int> sortArray(vector<int>& nums) 
    {
        //设置随机数种子
        srand((unsigned int)time(NULL));
        QuickSort(nums, 0, nums.size()-1);
        return nums;
    }

3、题3 数组中的第K个最大元素:

215. 数组中的第K个最大元素 - 力扣(LeetCode)

解法:

首要目标将数组中的数据进行排序就可以了;

第 k最大的元素,可以使用堆排序,也可以使用快排;

使用三路划分的快排,如果能确定第 k 大的数据落在哪一个区域,那么另外两个区域就不用考虑了;仅需在该区域中找第 k 大的数据即可;那我们可以借助于数据个数进行划分,专门去某一个区间查找数据就可以了;

需要注意的是,当 c>=k 就意味着,k 位于区域[right+1,r] 之中,只需要对这个区域中的数据进行排序即可,排完序之后,第 k 大的数据就是目标所求的数据;当 b+c >= k 的时候,条件 c>=k 放在前面就说明,k 并不大于等于c ,而是处于区间[left,right] 之中,而 [left,right] 中的数据全是等于基准值的,此时只要返回key 即可;而至于当上述两个情况均不满足的时候,就说明 k 小于等于 a ,只要去 [l,left-1] 中进行查找,但是由于数据排的是升序,那么就得找到区间[l,left-1] 排完序之后第 (k-b-c )大的数据才行;

参考代码:
    int QSort(vector<int>&nums, int l , int r, int k)
    {
        if(l == r) return nums[l];

        //随机数取基准值
        int keyi = l + rand()%(r-l+1);
        swap(nums[keyi] , nums[l]);
        int key = nums[l];

        //根据基准值元素将数据划分为三块
        int left = l, right = r, cur = l;
        while(cur <= right)
        {
            if(nums[cur] < key) swap(nums[left++] , nums[cur++]);
            else if(nums[cur] > key) swap(nums[cur],nums[right--]);
            else cur++;
        }
        //分情况讨论下一次去那个区间
        //l,left-1 left,right right+1,r
        int a = left - 1 - l +1 , b = right - left +1 , c = r - (right+1) +1;
        if( c >= k) return QSort(nums, right+1, r , k);
        else if( b+ c >= k) return key;
        else return QSort(nums, l,left-1, k-b-c);
    }
    int findKthLargest(vector<int>& nums, int k) 
    {
        //三路划分版本的快排
        srand((unsigned int)time(NULL));//设置随机数种子
        return QSort(nums,0,nums.size()-1 , k);
    }

4、题4 库存管理 III:

LCR 159. 库存管理 III - 力扣(LeetCode)

解法一:排序

调用sort 然后取得最小的前cnt 的个数;这种解法的时间复杂度为 O(NlogN);

解法二:堆排序

topK问题,直接使用堆排序;该解法的时间复杂度为O(NlogK);

解法三:快速排序算法

即三路划分版的快速排序算法;与上一题的思路相似,这种解法最优,时间复杂度为:O(N)

参考代码:
    void QSort(vector<int>&nums , int l, int r , int k)
    {
        if( l>= r) return;

        //随机数取基准值
        int keyi = l + rand()%(r-l+1);
        swap(nums[l] , nums[keyi]);
        int key = nums[l];

        //三路划分 - 三指针
        int cur = l+1 , left = l , right = r;
        while(cur <= right)
        {
            if(nums[cur] < key) swap(nums[cur++] , nums[left++]);
            else if(nums[cur] > key) swap(nums[cur] , nums[right--]);
            else cur++;
        }

        //继续进行排序的区间- 分情况讨论
        //l,left-1 left,right right+1,r
        int a = left -1 -l+1, b = right-left +1, c = r - right;
        if(k<=a) QSort(nums, l , left-1,k);
        else if( k<= a +b) return;
        else QSort(nums, right+1, r , k-a-b); 
    }
    vector<int> inventoryManagement(vector<int>& stock, int cnt) 
    {
        //设置随机数种子
        srand((unsigned int)time(NULL));
        QSort(stock, 0, stock.size()-1,cnt);
        return {stock.begin(), stock.begin()+cnt};
    }

总结

三路划分算法:

当待排序列中有大量重复数据的时候,三路划分的思想类似于hoare 版本和lomuto 前后指针的结合;其核心思想是将数组中的数据划分为三段:小于基准值的数据、等于基准值的数据、大于基准值的数据,故而叫做三路划分算法;

用Matlab和CVX搞定原子范数最小化:一个信号处理小白的避坑实践 本文详细介绍了如何使用Matlab和CVX实现原子范数最小化(Atomic norm minimization),特别适合信号处理初学者。从CVX安装配置、核心代码解析到实战中的典型问题与解决方案,提供了全面的避坑指南。通过具体案例展示了原子范数在频率估计和DOA估计中的优势,并给出性能优化策略和可视化方法。 阅读详情

相关推荐

企业和个人 AI 编程工具怎么选?2025 年 12 款主流工具(Claude/Cursor 等)核心能力 + 收费对比

2025 年最火的 12 款 AI 编程工具,像 Claude、Cursor 这些热门款,我把它们的核心能力、收费标准全扒得明明白白,看完这篇你不用再瞎琢磨,直接就能挑到对的!再说说 Cursor,美国 Cursor Labs 做的,还拿过红杉资本投资,2022 年推出后,2024 年收入直接涨了 6400%,势头很猛。总之,不管你是企业技术管理者、程序员,还是刚入门的 AI 小白,按自己的需求(语言、合规、预算)挑,保准能找到最适合的 AI 编程工具!最后给大家划重点,不同需求直接对号入座:?

hundred98的博客 2801

史上最清晰的三路快速排序

更多精彩文章,请关注公众号【TanLiuYi00】。 排序算法不论是在刷题还是面试都经常遇到,掌握它能提升自己的算法功力从而增加自己面试通过的几率。 本文主要介绍一下三路快排,并以微软的一道面试题leetcode75. 颜色分类作为例题来讲解,供大家参考,希望对大家有所帮助。 三路快排 使用快速排序的思想给带有大量的重复键值的数组进行排序,一种经典的实现方式就是三路快排(Quick Sort 3 Ways)。 主要思想 将整个数组分成三部分,即小于 v、等于 v 和大于v。分割后...

2006

数据分析实战:如何用皮尔逊相关系数避免伪相关陷阱(附Python代码)

本文通过实战案例与Python代码,深入解析了皮尔逊相关系数在数据分析中的应用与常见陷阱。文章重点揭示了如何识别与避免由混杂变量导致的伪相关,并介绍了通过可视化、敏感性分析、分层分析与偏相关分析等方法来检验相关性的可靠性,帮助分析师建立严谨的相关性思维。

weixin_29051245的博客 411

LeetCode--快速排序

则把当前节点的数据赋值到 right 节点, 然后停止左边遍历, 开始右边遍历。则把当前节点的数据赋值到 left 节点, 然后停止右边遍历, 开始左边遍历。对左数组递归调用执行 1,2,3 步骤。对右数组递归调用执行 1,2,3 步骤。

安全新司机 1290

ros_control硬件接口实战:从零构建机器人控制框架

本文详细介绍了ros_control硬件接口在机器人控制框架中的核心作用与实践方法。通过解析硬件接口类型、实现标准接口、构建主控制循环及自定义功能扩展,帮助开发者从零构建高效的机器人控制系统,提升硬件与ROS的协同效率。

lstm7chronicler的博客 500

构建高可用CephFS NFS网关:NFS-Ganesha与RADOS集群的深度整合

本文详细介绍了如何构建高可用的CephFS NFS网关,通过NFS-Ganesha与RADOS集群的深度整合,实现高效文件共享。文章涵盖核心架构、部署实战、性能调优及避坑指南,帮助用户解决Ceph客户端兼容性问题,提升存储访问效率。特别强调高可用设计,确保服务稳定性,适用于金融、电商等高要求场景。

weixin_26976635的博客 254

深入解析三路快排:一种高效的排序算法

在数据结构和算法的世界中,快排(Quick Sort)无疑是最受欢迎的排序算法之一。今天,探讨一种优化的快排变体——三路快排(3-Way Quick Sort),它在处理具有重复元素的数组时展现出了令人惊叹的效率。

qq_41591215的博客 2166

SolidWorks 小车底盘 装配体

通过制作小车底盘了解solidworks装配体的部分知识

及时总结 2228

比较排序——三路快速排序

三路快排

m0_58679504的博客 1036

BES2700的PMU

BES2700ZP-BES2700芯片的PMU架构分析摘要:本文档分析了BES2700芯片的电源管理单元(PMU)架构。实际应用中仅需为VBAT引脚供电,其余电源均由芯片内部PMU提供。文档包含架构示意图、问题列表和参考链接,涉及PMU工作原理、外部供电要求等关键技术点。待解决问题包括具体供电参数和内部电源转换机制等。通过添加日志信息和代码片段,为开发人员提供了调试PMU功能的实用方法。

云淡风轻 117

基于STM32L4XX 、HAL库的TMP144YFFR数字温度传感器驱动应用C语言程序设计

define TMP144_ADDRESS 0x00 // 默认为全局地址,用于单器件或MDA命令。#define TMP144_CMD_READ_TEMP 0x00 // 读取温度寄存器命令。if (temp_raw & 0x800) { // 判断第11位(符号位)

PP_Cottage的博客 91

Leetcode刷题笔记——排序算法篇

本文给大家介绍了面试中常考的关于排序问题的算法题,本文重点介绍了计数排序、快速排序、堆排序(重点)、插入排序、归并排序(重点)、桶排序、还有自定义排序规则

qq_42898642的博客 2669

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

分治算法——75. 颜色分类

75.颜色分类

Dirty_artist的博客 1429

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

分治(快排)(2)

力扣快排讲解

2601_95366422的博客 605

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

【算法】分治-快速排序

本文包含快速选择算法的实现,包括为什么要进行将数组进行分成三块,以及为什么要进行随机选择基础值;另外还详细解释快速选择算法是如何基于快排实现的。

2302_80475369的博客 789

算法思想总结:分治思想

1,快速排序本身相当于一个前序遍历,最好的时间复杂度是NlogN 最差的时间复杂度是N^2 ,最坏的情况是出现在(1)以最左侧或最右侧为基准值的时候,凑巧又接近有序(2)大量重复元素。为了解决这个问题衍生出了优化思路:三组划分+随机取key。并且这种方式还可以解决top-k问题,并且时间复杂度是o(N)比堆排序还优秀,我们称之为快速选择算法。 2,归并排序的本质就是将问题划分成无数个合并两个有序数组的子问题。是一个典型的后序遍历,时间复杂度是NlogN.我们发现他有一个特点就是:在归并之前,两个数组是有序

weixin_51142926的博客 1851

分治算法:从快排到归并排序的实战指南

欢迎来到丘山望岳的小栈,今天分享分治算法,从熟络的快排归并讲起,话不多说,我们现在发车。

2501_94762791的博客 247

分治—快排与归并算法的学习与习题

如果是升序,假设此时cur1的位置元素比cur2要大,因为是升序,只知道cur2以及左侧元素均小于cur1的元素,但是此时cur2进行移动之后,新的元素是不是一定比cur1位置的小呢,这是未知的,也就是不能一次性确定一串满足条件的元素个数进行累加,也就是这次判断对累加结果没有作用。注意这道题要返回的是一个数组,每个位置对应多少个结果,要一一返回出来,这就需要多一个辅助数组,用于绑定每一个元素的下标,只要每次合并数组时,对下标数组也进行一样的操作,就可以通过元素获取到它本来的下标。

2401_87804979的博客 436

【优选算法】分治:快速排序与归并排序

本文介绍了基于三指针分区的快速排序算法及其应用。主要内容包括: 颜色划分问题(LC75):使用三指针将数组划分为0、1、2三个区域 快速排序优化(LC912):通过随机基准值和三路分区提高效率,避免相同元素导致的性能下降 快速选择算法(LC215):在快速排序基础上实现O(n)时间找到第K大元素 最小K个数问题(LCR159):通过快速选择算法高效获取最小K个元素 算法核心思想都是利用三指针将数组分为小于、等于、大于基准值的三个区域,通过递归处理子区间实现排序或选择功能。代码实现均采用Java语言,包含随机

记录了从0学习Java技术栈的日常 776

分治算法——912. 排序数组

912.排序数组(快排三路划分、随机选key)

Dirty_artist的博客 1637

快速排序算法学习记录

快速排序算法学习记录

2401_87023016的博客 677

算法通关村第十关——快速排序算法

快速排序的是将分治法运用到排序问题的典型例子。基本思想:是通过随机标记一个pivot元素将含有n个元素的序列划分为左右两个子序列left和right,其中left的元素都比pivot小,right的元素都比pivot大,然后再次对left和right分别执行快速排序,这样将左右两个子序列排列完后,整个序列也就是有序的了。但是当数组中包含时,会出现性能下降的现象,此时我们可以使用三路快速排序算法。

分享自己学习过程获得的一些知识 389

[LC优选算法#5] 分治:快排 | 颜色分类 | 排序数组 | 第K大元素

本文介绍了基于分治思想的快速排序算法及其应用。主要内容包括:1) 分治快排与普通快排的区别,分治快排将数组分为三部分,解决了全为key值时效率退化问题;2) 通过颜色分类、排序数组和求第K大元素三个例题,详细讲解分治快排的实现和优化技巧,如随机选择基准元素;3) 特别分析了TopK问题的解法,如何在排序过程中通过区间元素数量比较快速定位目标元素。文章提供了清晰的算法图解和代码实现,展示了分治快排算法在稳定O(NlogN)时间复杂度上的优势。

Tairitsu_青云 410

力扣--分治(快速排序)算法题II:数组中的第K个最大元素(Top K问题),LCR159.库存管理III

本文介绍了两种使用快速选择算法解决TopK问题的方案。第一种是查找数组中第K大的元素,通过三路划分和随机基准元素,根据K值递归处理不同区间。第二种是查找前K小的元素集合,同样采用三路划分策略,根据小于和等于基准元素的个数与K值的关系,决定递归处理左区间、直接返回或处理右区间。两种方法都利用了快速选择算法的高效性,时间复杂度均为O(n),适用于大规模数据的TopK查询。文章提供了完整的C++实现代码,包括随机基准选择和三路划分的具体实现。

分享C,C++,数据结构,Linux,算法内容 734

【算法】分治 · 三路划分的快排

快速排序算法有很多版本,其中较为常见的有 hoare 法、挖坑法、前后指针法。这些版本已经在往期博客中做过详细的梳理(【算法】八种常见排序算法-优快云博客),本篇则侧重于三路划分版本。三路划分,顾名思义就是把一个顺序数据结构(一般为数组)分成三个部分,在该数据结构中随机选定一个基准值作为 key,然后按元素的值把数组分成小于 key 的部分、等于 key 的部分、大于 key 的部分。此时,这个 key 是已经排好序的了,接下来只需继续对其他区间进行划分,直到整个数组完成排序。

NeeEk0的博客 1165

Offer必备算法09_分治快排_四道力扣OJ(快排三路划分)

Offer必备算法09_分治快排_四道力扣OJ详解(快排三路划分)(力扣75颜色分类+力扣912排序数组+力扣215数组中的第K个最大元素+力扣LCR 159. 库存管理 III(剑指 Offer . 最小的k个数))

参考书籍:《C语言程序设计》《数据结构》《C++ Primer》《Effective C++》《STL源码剖析》《现代操作系统》《UNIX环境高级编程》《图解TCP/IP》《图解HTTP》《Linux高性能服务器编程》《剑指Offer》《算法导论》 1748
上一篇: 模拟 - #介绍 #题解
下一篇: c++ - 关于 string 的练习题
秦少游在淮海
博客等级 码龄3年 1071粉丝 88原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值