初识算法 · 分治(2)

目录

前言:

数组中的第K个最大元素

题目解析

算法原理

算法编写

库存管理

题目解析

算法原理

算法编写


前言:

​本文的主题是分治,通过两道题目讲解,一道是数组中的第k个最大元素,一道是最小的k个数。

链接分别为:
215. 数组中的第K个最大元素 - 力扣(LeetCode)

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

题目分为三个部分讲解,一是题目解析,二是算法原理,三是算法编写,那么,话不多说,直接进行主题咯。


数组中的第K个最大元素

题目解析

题目的要求非常简单,让我们找出数组中的第K个大的元素,该题目是上篇文章中快速排序的Top K问题,所以该道题目的基础是颜色分类那道题目和数组分三块的思想。

那么直接进入算法原理部分咯。

算法原理

在前文我们知道,我们利用三个指针,将数组划分成了三个区域,分别是小于K的,等于K的,大于K的,那么我们要在该区域里面查找到第K个最大的元素,其实当我们划分好了区域之后,我们不妨分情况讨论,对于第三个区域,命中第K个数的可能性是最大的。

所以我们首先可以讨论C区域,对于讨论的前提是我们应该知道该区域有多少个元素,如果该区域的元素甚至没有K个,那么肯定就不用在这里找了,我们应该直接去找B区域,所以判断条件应该是C >= K,如果满足该条件,就直接在该区域找,这样,a和b区域的值就直接排掉了,就大大减少了时间复杂度。所以同样的,递归式的在该区域里面查找。

对于第二个区域,因为第二个区域所有的元素都是一样的,如果在该区域查找了,我们直接返回该区域的任何一个值就可以了。

对于第一个区域,也就是a,如果走到了该区域,就说明b和c两个区域都不满足条件,也就是说,k的值应该是大于b + c的,此时我们不妨将题目要求改变一下,它不是要找最大的第K个值吗,但是这个K又比较大,我们就可以将题目转换为,在a区域里面,找k - b - c的最大值,因为a区域里面的值都是小的,里面找K是行不通的,毕竟区间都变化了。

所以该题目我们甚至是可以直接使用快排的模板,然后分情况讨论即可。

算法编写

cpp 复制代码
class Solution 
{
public:
    int GetRandom(vector<int>& v, int left, int right) 
    {
        return v[rand() % (right - left + 1) + left];
    }
    int qsort(vector<int>& v, int left, int right, int k) 
    {
        if (left >= right) return v[left];
        int key = GetRandom(v, left, right);
        int _left = left - 1, _right = right + 1, cur = left;
        while (cur < _right) 
        {
            if (v[cur] < key)
                swap(v[++_left], v[cur++]);
            else if (v[cur] == key)
                cur++;
            else
                swap(v[--_right], v[cur]);
        }
        int b = _right - _left - 1, c = right - cur + 1;
        if (c >= k)
            return qsort(v, _right, right, k);
        else if (b + c >= k) 
            return key;
        else return qsort(v, left, _left, k - c - b);
    }
    int findKthLargest(vector<int>& nums, int k) 
    {
        srand((unsigned int)time(NULL));
        return qsort(nums, 0, nums.size() - 1, k);
    }
};

库存管理

题目解析

题目要求也是很简单,返回最小的k个数即可,对于这种题目,一共有四种题型,分别是找最大的第K个数,找最小的第K个数,找最小的K个数,找最大的K个数。

题目的基本思路都是一样的,我们直接进入算法原理部分吧,

算法原理

其实对于这道题的解法非常多的,可以直接排序,然后返回,时间复杂度是N* LogN,也可以使用大小堆,时间复杂度是N * logK,但是都没有N来的简单。

我们使用快速选择排序算法可以实现N,基本思想还是一样的,数组分为三块,随机选择基准数即可。

那么我们主要是要讨论:

以上三种情况,对于第一种情况,如果a > k的情况,那么我们直接在里面找就可以了。如果是不满足该条件,我们就应该去第二个区间里面查找,此时因为我们已经将最小的数防在了一起,直接返回就可以了。

当以上的两种情况都不满足的话,我们就应该在第三个区间找,此时我们已经找到了a + b个元素,我们只需要找剩下的k - a - b个元素就可以了。

所以只需要将上面的题目稍加改动就可以了。

算法编写

cpp 复制代码
class Solution 
{
public:
    int GetRandom(vector<int>& v, int left ,int right)
    {
        return v[rand() % (right - left + 1) + left];
    }
    void qsort(vector<int>& v, int left, int right, int k)
    {
        if(left >= right) return;
        int _left = left - 1, _right = right + 1, cur = left;
        int key = GetRandom(v, left, right);
        while(cur < _right)
        {
            if(v[cur] < key) swap(v[++_left],v[cur++]);
            else if(v[cur] == key) cur++;
            else swap(v[--_right],v[cur]);
        }
        int a = _left - left + 1, b = (_right - 1) - (_left + 1) + 1;
        if(a > k) qsort(v, left, _left, k);
        else if(a + b >= k) return;
        else qsort(v, _right, right, k - a - b); 
    }
    vector<int> inventoryManagement(vector<int>& nums, int k) 
    {
        srand((unsigned int)time(NULL));
        qsort(nums,0, nums.size() - 1, k);
        return { nums.begin(), nums.begin() + k};
    }
};

感谢阅读!​

相关推荐
纪元A梦18 小时前
贪心算法应用:K-Means++初始化详解
算法·贪心算法·kmeans
_不会dp不改名_18 小时前
leetcode_21 合并两个有序链表
算法·leetcode·链表
mark-puls18 小时前
C语言打印爱心
c语言·开发语言·算法
Python技术极客19 小时前
将 Python 应用打包成 exe 软件,仅需一行代码搞定!
算法
睡不醒的kun19 小时前
leetcode算法刷题的第三十四天
数据结构·c++·算法·leetcode·职场和发展·贪心算法·动态规划
吃着火锅x唱着歌19 小时前
LeetCode 978.最长湍流子数组
数据结构·算法·leetcode
我星期八休息19 小时前
深入理解跳表(Skip List):原理、实现与应用
开发语言·数据结构·人工智能·python·算法·list
lingran__19 小时前
速通ACM省铜第四天 赋源码(G-C-D, Unlucky!)
c++·算法
haogexiaole20 小时前
贪心算法python
算法·贪心算法
希望201720 小时前
图论基础知识
算法·图论