[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];
    }
};

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

相关推荐
GreenTea7 小时前
深度解读 Anthropic 多智能体报告:更强的模型 ≠ 更好的协调
前端·后端·算法
码匠许师傅7 小时前
【C++ 面试真题】聊聊 C++ 的移动语义与右值引用
java·c++·面试
fqq38 小时前
力扣刷题前置Java语法
算法·leetcode·职场和发展
小小龙学IT8 小时前
DuckDB 深度实战:用 C++ 在进程内跑一个「分析型数据库」
数据库·c++
知无不研10 小时前
lambda表达式的使用(3)
开发语言·c++·lambda
半夏微凉半夏殇10 小时前
x11与weston对比,优先选哪个
leetcode·均值算法·eclipse
2501_9065651210 小时前
数学之美探究
算法
oier_Asad.Chen10 小时前
【洛谷题解/AcWing题解/OI学习笔记】洛谷P2868【USACO07DEC】Sightseeing Cows G(01分数规划求最大比率)
算法·图论·spfa·二分·负环
C++ 老炮儿的技术栈11 小时前
基于Qt实现轻量化本地音乐播放器
开发语言·c++·qt·c·播放器·音乐
leisoo809712 小时前
财报数据怎么排雷本地化Python构建财务异常预警系统
人工智能·python·算法