题目地址:
https://www.lintcode.com/problem/final-discounted-price/description
给定一个数组,要让每个数 a i a_i ai都减去其右边最近的不大于 a i a_i ai的数(如果没有就不减),返回修改后的数组。
这是很经典的单调栈的题目。维护一个严格单调上升的栈,栈里存储数组里数字的下标(虽然存的是下标,但严格上升是按照对应下标的数组值而言的)。遍历数组,当栈空或者栈的单调性不会被破坏时,就将数字push进栈;否则说明栈顶大于等于新来的数,此时将栈顶对应的数减去新来的数,并pop出栈,重复此操作直到新来的数push进栈不会破坏单调性(或者栈全pop完为空)为止,再将新来的数push进栈。数组遍历完之后,返回之即可。代码如下:
import java.util.Deque;
import java.util.LinkedList;
public class Solution {
/**
* @param prices: a list of integer
* @return: return the actual prices
*/
public int[] FinalDiscountedPrice(int[] prices) {
// write your code here
Deque<Integer> stack = new LinkedList<>();
for (int i = 0; i < prices.length; i++) {
// 如果栈不空并且新来的数小于等于栈顶(也就是说新来的数如果直接进栈的话,
// 就会破坏栈的严格单调增加的性质)
// 此时就将栈顶对应的数减去新来的数,并出栈;重复此操作
while (!stack.isEmpty() && prices[stack.peek()] >= prices[i]) {
prices[stack.peek()] -= prices[i];
stack.pop();
}
stack.push(i);
}
return prices;
}
}
时空复杂度 O ( n ) O(n) O(n)。
算法正确性证明:
只需要证明每个数都“恰当”的减去了它该减的数即可。首先显然每个数都会入栈一次;当某个数入栈后,之后入栈的数一定比它大;当第一次遇到比它小或者相等的数时,由while循环的逻辑,其后的所有数都会被弹掉,之后轮到它自己,就会减去新来的数。所以算法正确。