Find the K-Sum of an Array

本文介绍了一种巧妙利用数据结构解决寻找数组中第k大子序列和的方法。通过将数组中的元素转换为正数并排序,结合最大堆实现高效求解。文章详细解释了算法思路,并提供了具体实例帮助理解。

Question:

请添加图片描述

思路:

这个题也是对data structure 绝妙使用的一题. 首先很显然, 拿到一个nums array. 肯定所有的正数之和是最大的sum. 然后第二大第三大就是每回依次从第一大里要么减去最小的正数, 要么减去最大的负数.
然后因为这个题问的是subsequence, 不是substring那种, 就是不需要选择的sequence 必须里面所有数字都紧挨着. 所以可以有各种排列。
所以第一个思路就是, 我们如何知道下一个大的sum 是该减去最小的正数还是最大的负数. 还是最小的两个正数或者最大的两个负数

好办, 我们把所有的数字都转换成正数, 然后再从小到大排序, 每次找下一个最大的就从这个排序里面从头减就行了. 就不需要考虑正负的事情…

然后我们要思考怎么找遍每个sequence 的问题. 这个时候就是运用heap的美丽结构了.
我们确定要用maxheap. 就是把所有的sum 扔进heap里让heap排序.
但是我们没必要必须把所有的subsequence 一起扔进heap. 我们其实只要一边pop一边push就可以.
举个例子, 运用heap的结构。.

比如说我们现在的sum是 maxSum. 然后我们排完序, 现在array里是nums[] = [1,2,3,4,5,xxxxx]
然后我们要找下一个largest num,
那么很显然, 下一个largest sum 一定是 maxSum - nums[0]
maxSum 减最小的数字形成的sequence.
然后再下一个是, maxSum - nums[1], 再下一个是 maxSum - nums[0] - nums[1], 再一个, maxSum - nums[2], 然后 maxSum - nums[0] - nums[2], 接着maxSum - nums[3]
balabalabal

我们发现这个就是maxSum - 任意的subsequence of nums[] 从头开始.

有一个办法可以让我们一边push给heap 一边pop出来, 然后还能涵盖所有的subsequence.
我们每次把新的maxSum和当前的index 一起push 给heap
每次 push 给heap:
maxSum(pop出来的) + nums[index] - nums[index+1] (相当于把nums[0] 还回去, 然后只考虑maxSum - nums[1])
maxSum(pop出来的) - nums[index+1] (相当于算maxSum - nums[0] - nums[1]).

举个具体的例子, 我们来看一下规律.
初始值给heap push (maxSum - nums[index], index) index = 0.
下一轮, maxSum = pop出来的val. 再 push (maxSum + nums[index] - nums[index + 1]) 和 (maxSum - nums[index+1])
此时我们的heap里有 (maxSum - nums[0] - nums[1]) 和 (maxSum - nums[1])
然后再pop
再push(maxSum + nums[1] - nums[2]) 和 (maxSum - nums[2])
现在heap里有 (maxSum - nums[0] - nums[1], maxSum - nums[2], maxSum - nums[1] - nums[2])
再pop (maxSum - nums[0] - nums[1])
再push (maxSum + nums[1] - nums[2]) 和 (maxSum - nums[2])
heap: ( maxSum - nums[2], maxSum - nums[1] - nums[2], maxSum - nums[0] - nums[2], maxSum - nums[0]-nums[1] - nums[2])

发现了什么!!!
是不是heap 随着我们循环, 实际上push给heap的涵盖了所有的subsequence!!! 然后还是按照从大到小排序的。

NB!!!!!

class Solution {
    public long kSum(int[] nums, int k) {
        long maxSum = 0; 
        for(int i = 0;i < nums.length; ++i){
            if(nums[i] < 0){
                nums[i] = Math.abs(nums[i]);
            } else{
                maxSum += nums[i];
            }
        }
        
        k--;
        Arrays.sort(nums);
        PriorityQueue<long[]> pq = new PriorityQueue<>((a,b) -> Long.compare(b[0], a[0]));
        pq.add(new long[]{maxSum - nums[0], 0});
        
        while(k > 0){
            long[] front = pq.poll();
            int index = (int) front[1];
            maxSum = front[0];
            
            // use the tree structure of heap, to have all the combination of subsequence
            if(index < nums.length-1){
                pq.add(new long[]{maxSum + nums[index] - nums[index + 1], index+1});
                pq.add(new long[]{maxSum - nums[index+1], index+1});
            }
            k--;
        }
        
        return maxSum;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值