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

1. 分治-快排思想

之前的博客中介绍过快速排序的思想,详细请跳转此篇博客:

C语言:排序(二)

分治快排算法的思想和传统的快速排序有所不同,具体体现在:

  • 快速排序 :选取一个参照数key,将区间分为两部分,左侧元素<=key,右侧元素>key
  • 分治快排算法 :选取一个参照数key,将区间分为三部分,左侧元素<=key,中间元素==key,右侧元素>key

相比快速排序,分治快排规避了当元素全部为key时,排序效率退化为O(N^2)的情况,实现了排序效率稳定在O(NlogN)。具体的计算分析可以阅读《算法导论》概率求期望的部分。

2. 例题分析

2.1 颜色分类

颜色分类

解题思路:

题目明确要求将数组分为0,1,2有序的三块,因此选择分治快排的思想解决。

这里我们需要用三个指针left,right,i来将数组分块:

指针ileft的下一个位置出发,依次向后移动,并与left或right指向的元素进行交换。以下是关于i位置元素的分类讨论:

需要注意的是,当nums[i]为2时,在交换后不能让i++,因为i左边的区间依旧是待处理区间,交换过来的元素需要在下次循环进行二次处理。

下面是一个数组具体的排序过程:

注意点 :由于指针i是和指针的left和right的下一个位置进行交换,因此left的起始位置是-1right的起始位置是nums.size() - 1;循环的结束条件应设置为i < right

cpp 复制代码
class Solution {
public:
    void sortColors(vector<int>& nums)
    {
        int n = nums.size();
        for(int left=-1, right=n, i=0; i < right;)
        {
            if(nums[i] == 0)
            {
                swap(nums[++left], nums[i++]);
            }
            else if(nums[i] == 2)
            {
                swap(nums[--right], nums[i]); //不要i++!
            }
            else
            {
                i++;
            }
        }
    }
};

2.2 排序数组

排序数组

题目要求用O(NlogN)的时间复杂度解决,且空间复杂度尽量小,因此选择数组分三块+随机选取key元素的快速排序算法解决。

解题思路

快速排序-数组分三块 :选取一个参照数key,将区间分为三部分 ,左侧元素<=key,中间元素==key,右侧元素>key,不断递归直至数组有序。具体过程如图:

优化点 :随机选择key,让数组排序时的区间更加平衡:

cpp 复制代码
class Solution {
public:
    vector<int> sortArray(vector<int>& nums)
    {
        int n = nums.size();
        srand(time(NULL));
        QuickSort(nums, 0, n - 1);

        return nums;
    }

    void QuickSort(vector<int>& nums, int l, int r)
    {
        if(l >= r) return;

        int key = getRandom(nums, l, r);
        int left = l - 1;
        int right = r + 1;

        for(int i = l; i < right;)
        {
            if(nums[i] < key)
            {
                swap(nums[++left], nums[i++]);
            }
            else if(nums[i] > key)
            {
                swap(nums[--right], nums[i]);
            }
            else
            {
                i++;
            }
        }

        QuickSort(nums, l, left); //注意递归区间!
        QuickSort(nums, right, r);

    }

    int getRandom(vector<int>& nums, int l, int r)
    {
        int num = rand();
        return nums[num % (r - l + 1) + l];
    }
};

2.3 数组中的第K个最大元素

数组中的第K个最大元素

这道题是比较经典的TopK问题,这个问题包含以下变式:

  1. 求第K个最大 / 最小的元素
  2. 求前K个最大 / 最小的元素

对于这种问题,一般的解法是堆排序 或者快速排序 ,时间复杂度都是O(NlogN)

由于题目要求用O(N)的时间复杂度解决问题,但普通排序的时间复杂度基本都是O(NlogN),因此这里推荐用快速排序算法,并在排序的过程中去对应区间找对应的第K大元素即可。

解题思路

  • 快速排序算法 :依旧延用上一题的思路,采用数组分三块+随机选key的方式解决;
  • 找第K大的元素 :在排序过程中添加一个步骤,就是分别计算出三个区间中的元素个数,依次和K比较,判断第K大的元素落在哪个区间,再单独去这个区间找。
cpp 复制代码
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);

        //2.根据基准元素将数组分为三块
        int left = l - 1;
        int right = r + 1;
        int i = l;
        while(i < right)
        {
            if(nums[i] < key)
            {
                swap(nums[++left], nums[i++]);
            }
            else if(nums[i] > key)
            {
                swap(nums[--right], nums[i]);
            }
            else
            {
                i++;
            }
        }

        //3.分情况讨论
        int c = r - right + 1;
        int b = right - left - 1;

        if(k <= c) return QuickSort(nums, right, r, k);
        else if(k <= b + c) return key; //注意是b + c!
        else return QuickSort(nums, l, left, k - b - c); //注意是k - b - c!
    }

    int getRandom(vector<int>& nums, int l, int r)
    {
        int num = rand();
        return nums[num % (r - l + 1) + l];
    }
};

// 本期内容就到这里啦,如果对你有帮助,请三连支持!我是青云,我们下期见^_~

相关推荐
stolentime5 分钟前
AT_pakencamp_2020_day1_k Gcd of Sum题解
c++·算法
2601_9561219720 分钟前
map_计蒜客T1271 完美K倍子数组
c++·算法
编程圈子42 分钟前
电机驱动开发学习19. 霍尔 BLDC 三段式 FOC 启动算法
驱动开发·学习·算法
Hello_Damon_Nikola1 小时前
伺服电机堵转检测算法详解
单片机·嵌入式硬件·算法
罗小罗同学1 小时前
安德森癌症中心发表SCOPE IO模型,实现了对罕见肿瘤帕博利珠单抗疗效的精准预测,在患者早期即可预判药效
人工智能·算法·医学图像处理·医学ai
王维同学1 小时前
IFEO Debugger、VerifierDlls 与 SilentProcessExit 配置
c++·windows·安全·注册表
变量未定义~2 小时前
Tarjan算法-强连通分量、路径、出差
算法
geovindu2 小时前
CSharp: Iterative Algorithms
开发语言·后端·算法·c#·.net·迭代算法
小保CPP2 小时前
OpenCV C++计算多边形的最大内切圆
c++·人工智能·opencv·计算机视觉
道影子2 小时前
《黄帝内经》021章|津凝精晶 沉降为患
人工智能·深度学习·神经网络·算法·机器学习