【Lintcode】751. John‘s Business

题目地址:

https://www.lintcode.com/problem/johns-business/description

给定一个长 n n n数组 A A A,再给定一个非负整数 k k k,要求返回一个数组 B B B,使得 B [ i ] = A [ i ] − min ⁡ A [ i − k : i + k ] B[i]=A[i]-\min A[i-k:i+k] B[i]=A[i]minA[ik:i+k]

可以用单调队列。开一个单调上升的队列,维护队头是当前询问区间的最小值,并且从队头到队尾是单调上升的。当遍历到 A [ i ] A[i] A[i]的时候,那么查询区间就是 A [ i − k : i + k ] A[i-k:i+k] A[ik:i+k],我们考虑以 A [ i + k ] A[i+k] A[i+k]为右端点的查询区间,首先把出了查询区间的那个数(即 A [ i − k − 1 ] A[i-k-1] A[ik1])出队,然后容易知道, A [ i + k ] A[i+k] A[i+k]可以把队尾比它大于等于的数全都PK下去,原因是在以 A [ i + k ] A[i+k] A[i+k]及其右边所有数为右端点的查询区间里,大于等于它的数都不可能是查询区间最小值。我们可以用数学归纳法证明在上述PK过程下,队列始终单调上升,并且队头始终是以当前队尾为右端点的查询区间的最小值(单调上升性质很容易证明,至于为什么队头总是最小值,首先出了窗口的已经被踢掉了,还没出窗口的队头,首先窗口里的位置在该队头左边的数一定比它大于等于,因为它们被当前队头或者大于等于当前队头的数PK掉了,而当前队头右边的数也是一定比它大于等于,否则它们应该能把当前队头PK掉。所以当前队头就是当前查询区间的最小值)。在本题里,可以先遍历 A [ 0 : k − 1 ] A[0:k-1] A[0:k1],得到一个单调队列,然后再重新遍历 A [ 0 : n − 1 ] A[0:n-1] A[0:n1](本质上是在枚举中心点),每遍历一个数的时候,先把它左边出了窗口的数踢掉,然后将它为中心的右端点入队并维护单调性,这样以当前数为中心的查询区间的最小值就是队头。代码如下:

import java.util.ArrayDeque;
import java.util.Deque;

public class Solution {
    
    
    /**
     * @param A: The prices [i]
     * @param k:
     * @return: The ans array
     */
    public int[] business(int[] A, int k) {
    
    
        // Write your code here
        Deque<Integer> deque = new ArrayDeque<>();
        for (int i = 0; i < Math.min(k, A.length); i++) {
    
    
            while (!deque.isEmpty() && A[deque.peekLast()] >= A[i]) {
    
    
                deque.pollLast();
            }
            deque.offerLast(i);
        }
        
        int[] res = new int[A.length];
        // 进行枚举,i是中心点,idx是res的下标,r是i为中心点的时候的区间右端点
        for (int i = 0, idx = 0, r = k; i < A.length; i++) {
    
    
        	// 踢掉左边出了窗口的数
            if (deque.peekFirst() < i - k) {
    
    
                deque.pollFirst();
            }
            
            // 将右端点入队
            if (r < A.length) {
    
    
                while (!deque.isEmpty() && A[deque.peekLast()] >= A[r]) {
    
    
                    deque.pollLast();
                }
                deque.offerLast(r);
                r++;
            }
            
            // 存一下A[i]和以i为中心点的查询区间的最小值的差
            res[idx++] = A[i] - A[deque.peekFirst()];
        }
        
        return res;
    }
}

时空复杂度 O ( n ) O(n) O(n)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/114122222