求数组第k小/大的序列和

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求数组第k小/大的序列和相关的知识,希望对你有一定的参考价值。

求数组第 k k k小/大的序列和

弱化版: a i a_i ai 非负,求第 k k k小。

最小的子序列和是空集 0 0 0

算法流程:

使用优先队列,用二元组 ( s u m , i ) (sum,i) (sum,i) 表示当前下标 i i i结尾的子序列和 s u m sum sum

初始加入 ( 0 , 0 ) (0,0) (0,0)

每次取出队首。

然后加入 ( s u m + a i + 1 , i + 1 ) , ( s u m + a i + 1 − a i , i + 1 ) (sum+a_i+1,i+1),(sum+a_i+1-a_i,i+1) (sum+ai+1,i+1),(sum+ai+1ai,i+1)

k k k次即为第 k k k小的序列和。

时间复杂度: O ( n log ⁡ n + k log ⁡ k ) O(n\\log n+k\\log k) O(nlogn+klogk)

证明

我们需要证明这样做得到的子序列是不重复、不遗漏、按顺序。


强化版1: a i a_i ai 为整数,求第 k k k大。

显然可以把 a i a_i ai分成两部分,负数和非负数。

显然第 1 1 1大的和就是 s u m ( a i ) , a i ≥ 0 sum(a_i),a_i\\ge 0 sum(ai),ai0

接下来要么减去一个非负数,要么加上一个负数。

这两种情况可以等价为减上一个非负数。然后我们对这些非负数从小到大排序。

然后就转化弱化版的题目的做法,使用优先队列即可。

代码

class Solution 
public:
    long long kSum(vector<int> &nums, int k) 
        long sum = 0L;
        for (int &x : nums)
            if (x >= 0) sum += x;
            else x = -x;
        sort(nums.begin(), nums.end());
        priority_queue<pair<long, int>> pq;
        pq.emplace(sum, 0);
        while (--k) 
            auto[sum, i] = pq.top();
            pq.pop();
            if (i < nums.size()) 
                pq.emplace(sum - nums[i], i + 1); // 保留 nums[i-1]
                if (i) pq.emplace(sum - nums[i] + nums[i - 1], i + 1); // 不保留 nums[i-1]
            
        
        return pq.top().first;
    
;

强化版2: a i a_i ai 为整数,求第 k k k小。

类似地。最小的就是负数之和。

接下可以等价为加一个非负数,对这些非负数从小到大排序。

然后转弱化版解法。

代码略。


以上是关于求数组第k小/大的序列和的主要内容,如果未能解决你的问题,请参考以下文章

关于求解区间第k大的在线和离线做法

【python滴滴出行】整数无序数组求第K大数?

如何查找无序数组中的Top n

HDU 6231 (二分+双指针)

算法题之找出数组里第K大的数

寻找数组中的第K大的元素,多种解法以及分析