算法:分治思想处理快排递归以及快速选择/最小K个数问题

简介: 算法:分治思想处理快排递归以及快速选择/最小K个数问题

算法原理

分治的原理就是分而治之,从原理上讲,就是把一个复杂的问题划分成子问题,再将子问题继续划分,直到可以解决

实现思路

基于分治的原理进行快速排序,区别于传统的快速排序,这里对快速排序进行改良,成为更优先的三路划分算法,可以处理一些极端场景,使快速排序的适用性更加广泛,同时引出快速选择算法,用来搭配堆排序解决topk问题

典型例题

颜色分类

本题和前面在双指针算法中做的移动0的解法类似,这里其实算法原理和快速排序优化的三路划分很相似,将数组中的区域划分为三个部分,分别为0,1,2,因此解决问题的时候要先想清楚如何解决,把数据量弄清楚

对于此题来说,算法思路就是如果遇到2,就把数据扔到最后,如果遇到0,就放到最前,那么1就会被天然的隔离到最中间的部分,这是可行的

快速排序优化

根据上面的原理,可以改进快速排序,快速排序的弊端在于,当他要进行处理很多相同数据的时候,就遇到十分低效的情况,基于这个原因,可以在实现它的过程中利用到一些上面的想法,这样的算法思路其实也叫做三路划分

因此可以使用这个方法来解题,否则会超时

class Solution 
{
public:
    vector<int> sortArray(vector<int>& nums) 
    {
        srand(time(0));
        quicksort(nums,0,nums.size()-1);
        return nums;
    }
    void quicksort(vector<int>& nums,int left,int right)
    {
        if(left>=right)
        {
            return;
        }
        int key=numsrandom(nums,left,right);
        int i=left,begin=left-1,end=right+1;
        while(i<end)
        {
            if(nums[i]<key)
            {
                swap(nums[++begin],nums[i++]);
            }
            else if(nums[i]==key)
            {
                i++;
            }
            else
            {
                swap(nums[--end],nums[i]);
            }
        }
        quicksort(nums,left,begin);
        quicksort(nums,end,right);
    }
    int numsrandom(vector<int>& nums,int left,int right)
    {
        int keyi=rand()%(right-left+1)+left;
        return nums[keyi];
    }
};

基于快速排序的三路划分原理,可以引申出新的思想:快速选择问题

数组中最大的K个数

看到这个题第一思想是使用堆排序,因为堆排序处理TopK问题是十分有效的,但是后面的限制条件,时间复杂度必须是O(N)的算法,因此这里并不能使用TopK算法

由于前面有快速排序的基础,因此这里可以引申出一个快速选择解法

首先看思路:

在快速排序的三路划分算法中,当划分结束后,整个数组会被天然的划分为下面三个部分:

假设,我们这里让蓝色区域的记作C,红色区域记作B,棕色区域记作A

那如果我们要求的这个第k个数据落在蓝色区域,那么我们只需要在C这个单位长度内寻找一次即可,如果落在红色区域,那么要找的这个数据就是key,如果落在棕色区域,则只需要在A这个单位长度寻找一次即可

由此,就引申出了快速选择算法:基于快速排序从而引申出的快速选择

class Solution 
{
public:
    int findKthLargest(vector<int>& nums, int k) 
    {
        srand(time(NULL));
        return quicksort(nums,0,nums.size()-1,k);
    }
    int quicksort(vector<int>& nums,int l,int r,int k)
    {
        if(l==r)
        {
            return nums[l];
        }
        // 1. 选取中间元素
        int key=getrandom(nums,l,r);
        int left=l-1,right=r+1,i=l;
        // 2. 三路划分
        while(i<right)
        {
            if(nums[i]<key)
            {
                swap(nums[++left],nums[i++]);
            }
            else if(nums[i]==key)
            {
                i++;
            }
            else
            {
                swap(nums[--right],nums[i]);
            }
        }
        // 3. 判断
        int c=r-right+1;
        int b=(right-1)-(left+1)+1;
        if(c>=k)
        {
            return quicksort(nums,right,r,k);
        }
        else if(b+c>=k)
        {
            return key;
        }
        else
        {
            return quicksort(nums,l,left,k-b-c);
        }
    }
    int getrandom(vector<int>& nums,int l,int r)
    {
        return nums[rand()%(r-l+1)+l];
    }
};

时间复杂度分析较为复杂,但是是严格符合题目要求的,由此其实也看出了分治的思想核心,把一个大问题转换成小问题,直到最后转换成一个我们一下就能解决的问题,不断的缩小我们需要寻找的区间,这样最终就能找到我们需要的答案

最小的K个数

class Solution 
{
public:
    vector<int> getLeastNumbers(vector<int>& nums, int k) 
    {
        srand(time(NULL));
        quicksort(nums,0,nums.size()-1,k);
        return {nums.begin(),nums.begin()+k};
    }
    void quicksort(vector<int>& nums,int l,int r,int k)
    {
        if(l==r)
        {
            return;
        }
        // 1. 选基准元素
        int key=getrandom(nums,l,r);
        int left=l-1,right=r+1,i=l;
        // 2. 三路划分
        while(i<right)
        {
            if(nums[i]<key)
            {
                swap(nums[++left],nums[i++]);
            }
            else if(nums[i]==key)
            {
                i++;
            }
            else
            {
                swap(nums[--right],nums[i]);
            }
        }
        // 3. 快速选择
        int a=left-l+1,b=(right-1)-(left+1)+1;
        if(a>k)
        {
            quicksort(nums,l,left,k);
        }
        else if(a+b>=k)
        {
            return;
        }
        else
        {
            quicksort(nums,right,r,k-a-b);
        }
    }
    int getrandom(vector<int>& nums,int l,int r)
    {
        return nums[rand()%(r-l+1)+l];
    }
};

对于这个题来说,解法多种多样,可以采用很多方法,topk,直接排序,快速选择,这里依旧选择快速选择来写

原理和前面类似,由于返回的是前k个数不一定要有序,因此三路划分后可以直接进行条件判断,满足需求就可以跳出循环

总结

分治思想用以快速排序,可以引申出快速选择这个算法,而这个算法在实际应用中有很大的作用,对于解决前k个数或第k个数都有很大的算法意义,下篇会总结分治思想用以解决归并问题

相关文章
|
1月前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
49 2
|
2月前
|
算法 搜索推荐 Shell
数据结构与算法学习十二:希尔排序、快速排序(递归、好理解)、归并排序(递归、难理解)
这篇文章介绍了希尔排序、快速排序和归并排序三种排序算法的基本概念、实现思路、代码实现及其测试结果。
33 1
|
2月前
|
算法 定位技术
数据结构与算法学习九:学习递归。递归的经典实例:打印问题、阶乘问题、递归-迷宫问题、八皇后问题
本文详细介绍了递归的概念、重要规则、形式,并展示了递归在解决打印问题、阶乘问题、迷宫问题和八皇后问题等经典实例中的应用。
53 0
|
4月前
|
算法
【算法】递归、搜索与回溯——汉诺塔
【算法】递归、搜索与回溯——汉诺塔
|
4月前
|
算法 搜索推荐
算法设计 (分治法应用实验报告)基于分治法的合并排序、快速排序、最近对问题
这篇文章是关于分治法应用的实验报告,详细介绍了如何利用分治法实现合并排序和快速排序算法,并探讨了使用分治法解决二维平面上的最近对问题的方法,包括伪代码、源代码实现及时间效率分析,并附有运行结果和小结。
|
4月前
|
算法
【算法】递归总结:循环与递归的区别?递归与深搜的关系?
【算法】递归总结:循环与递归的区别?递归与深搜的关系?
106 0
|
4月前
|
算法
【算法】递归、搜索与回溯——简介
【算法】递归、搜索与回溯——简介
|
17天前
|
算法
基于WOA算法的SVDD参数寻优matlab仿真
该程序利用鲸鱼优化算法(WOA)对支持向量数据描述(SVDD)模型的参数进行优化,以提高数据分类的准确性。通过MATLAB2022A实现,展示了不同信噪比(SNR)下模型的分类误差。WOA通过模拟鲸鱼捕食行为,动态调整SVDD参数,如惩罚因子C和核函数参数γ,以寻找最优参数组合,增强模型的鲁棒性和泛化能力。
|
23天前
|
机器学习/深度学习 算法 Serverless
基于WOA-SVM的乳腺癌数据分类识别算法matlab仿真,对比BP神经网络和SVM
本项目利用鲸鱼优化算法(WOA)优化支持向量机(SVM)参数,针对乳腺癌早期诊断问题,通过MATLAB 2022a实现。核心代码包括参数初始化、目标函数计算、位置更新等步骤,并附有详细中文注释及操作视频。实验结果显示,WOA-SVM在提高分类精度和泛化能力方面表现出色,为乳腺癌的早期诊断提供了有效的技术支持。
|
3天前
|
供应链 算法 调度
排队算法的matlab仿真,带GUI界面
该程序使用MATLAB 2022A版本实现排队算法的仿真,并带有GUI界面。程序支持单队列单服务台、单队列多服务台和多队列多服务台三种排队方式。核心函数`func_mms2`通过模拟到达时间和服务时间,计算阻塞率和利用率。排队论研究系统中顾客和服务台的交互行为,广泛应用于通信网络、生产调度和服务行业等领域,旨在优化系统性能,减少等待时间,提高资源利用率。
下一篇
DataWorks