这道LeetCode题目看似简单,却蕴含着电商定价策略中的典型场景。题目要求我们实现一个函数,对于给定的商品价格数组prices,其中prices[i]是第i件商品的价格,需要返回一个数组answer,其中answer[i]是第i件商品在享受折扣后的最终价格。
折扣规则是:对于第i件商品,如果在它后面存在第一个价格<=prices[j]的商品j(j>i),那么第i件商品可以获得prices[j]的折扣,否则该商品没有折扣。换句话说,我们需要为每个商品找到其右侧第一个不大于它的价格作为折扣价。
最直观的解法是使用双重循环:对于每个元素prices[i],向后遍历查找第一个满足prices[j] <= prices[i]的元素。这种方法的时间复杂度是O(n²),在数据量较小时可以接受,但对于大规模数据(比如数万件商品)效率会明显下降。
python复制def finalPrices(prices):
n = len(prices)
answer = [0] * n
for i in range(n):
discount = 0
for j in range(i + 1, n):
if prices[j] <= prices[i]:
discount = prices[j]
break
answer[i] = prices[i] - discount
return answer
更高效的解法是使用单调栈(Monotonic Stack)。单调栈是一种特殊的栈结构,它可以帮助我们在O(n)时间复杂度内解决这类"寻找下一个更小/更大元素"的问题。
具体思路是维护一个从栈底到栈顶递增的栈(即栈顶元素最大)。对于当前元素prices[i],如果它小于等于栈顶元素,那么它就是栈顶元素的"下一个更小元素",可以立即应用折扣。
python复制def finalPrices(prices):
stack = []
answer = prices.copy()
for i in range(len(prices)):
while stack and prices[i] <= prices[stack[-1]]:
j = stack.pop()
answer[j] = prices[j] - prices[i]
stack.append(i)
return answer
让我们用一个具体例子来理解单调栈的工作过程。假设输入价格为[8,4,6,2,3]:
在实际实现中需要注意几个边界条件:
单调栈解法中,每个元素最多被压入和弹出栈各一次,因此时间复杂度是O(n),比暴力解法的O(n²)有了显著提升。
除了输出数组外,我们最多需要O(n)的额外空间来存储栈,在最坏情况下(严格递减序列)所有元素都会被压入栈。
这种折扣模式在实际电商系统中很常见,比如:
这种算法模式可以解决多种类似问题:
java复制public int[] finalPrices(int[] prices) {
Stack<Integer> stack = new Stack<>();
int[] res = Arrays.copyOf(prices, prices.length);
for (int i = 0; i < prices.length; i++) {
while (!stack.isEmpty() && prices[i] <= prices[stack.peek()]) {
int j = stack.pop();
res[j] = prices[j] - prices[i];
}
stack.push(i);
}
return res;
}
cpp复制vector<int> finalPrices(vector<int>& prices) {
stack<int> s;
vector<int> res(prices);
for (int i = 0; i < prices.size(); ++i) {
while (!s.empty() && prices[i] <= prices[s.top()]) {
int j = s.top(); s.pop();
res[j] = prices[j] - prices[i];
}
s.push(i);
}
return res;
}
javascript复制function finalPrices(prices) {
const stack = [];
const res = [...prices];
for (let i = 0; i < prices.length; i++) {
while (stack.length && prices[i] <= prices[stack[stack.length-1]]) {
const j = stack.pop();
res[j] = prices[j] - prices[i];
}
stack.push(i);
}
return res;
}
如果题目改为寻找左侧第一个更小元素作为折扣,只需调整遍历方向:
python复制def previousDiscount(prices):
stack = []
answer = prices.copy()
for i in range(len(prices)-1, -1, -1):
while stack and prices[i] < prices[stack[-1]]:
j = stack.pop()
answer[j] = prices[j] - prices[i]
stack.append(i)
return answer
考虑更复杂的折扣策略,比如累积所有后续符合条件的折扣:
python复制def cumulativeDiscount(prices):
stack = []
answer = prices.copy()
for i in range(len(prices)):
while stack and prices[i] <= prices[stack[-1]]:
j = stack.pop()
answer[j] -= prices[i]
stack.append(i)
return answer
在实际电商系统中,折扣可能考虑多个维度(如商品类别、购买时间等),这时可能需要结合其他数据结构如优先队列或线段树来实现更复杂的折扣逻辑。