今天我们来拆解LeetCode第1547题"商品折扣后的最终价格"。这是一道典型的数组处理题目,主要考察对数组元素的遍历和条件判断能力。题目要求我们根据给定的商品价格数组,为每个商品寻找第一个价格小于或等于它的后续商品,然后计算折扣后的最终价格。
在实际业务场景中,这种计算方式非常常见。比如电商平台的"后续订单优惠"功能:当用户购买某商品后,如果在一定时间内再次购买相同或同类商品,可以享受以第一次购买价格为基准的折扣。我们需要快速计算出每个商品在考虑各种折扣规则后的实际成交价。
最直观的解法是使用双重循环:
python复制def finalPrices(prices):
n = len(prices)
result = prices.copy()
for i in range(n):
for j in range(i+1, n):
if prices[j] <= prices[i]:
result[i] -= prices[j]
break
return result
时间复杂度O(n²),空间复杂度O(n)。这种方法虽然简单,但对于大规模数据效率较低。
更高效的解法是使用单调栈:
python复制def finalPrices(prices):
stack = []
result = prices.copy()
for i in range(len(prices)):
while stack and prices[stack[-1]] >= prices[i]:
result[stack.pop()] -= prices[i]
stack.append(i)
return result
时间复杂度O(n),空间复杂度O(n)。这种方法利用了栈结构维护一个单调不减的序列,可以快速找到每个元素右侧第一个小于等于它的值。
在实际编码时需要注意几个边界条件:
如果允许修改原数组,可以进一步优化空间:
python复制def finalPrices(prices):
stack = []
for i in range(len(prices)):
while stack and prices[stack[-1]] >= prices[i]:
prices[stack.pop()] -= prices[i]
stack.append(i)
return prices
这样空间复杂度降为O(1)(不考虑输出空间)。
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 暴力解法 | O(n²) | O(n) | 小规模数据 |
| 单调栈 | O(n) | O(n) | 大规模数据 |
| 空间优化版 | O(n) | O(1) | 允许修改原数组情况 |
这种算法模式在实际开发中有广泛应用:
当处理大规模数据时,递归实现的单调栈可能导致栈溢出。建议始终使用迭代实现。
题目要求"小于或等于",注意不要漏掉等于的情况。测试用例要包含连续相等元素的情况。
对于特别大的数组,可以考虑:
完整的测试应该包含:
python复制test_cases = [
([8,4,6,2,3], [4,2,4,2,3]), # 常规情况
([1,2,3,4,5], [1,2,3,4,5]), # 无折扣情况
([10,1,1,6], [9,0,1,6]), # 多个折扣
([5,5,5,5], [0,0,0,5]), # 全部相等
([], []), # 空数组
([100], [100]) # 单元素
]
不同语言可以利用其特性写出更简洁的代码:
JavaScript实现:
javascript复制function finalPrices(prices) {
const stack = [];
const res = [...prices];
prices.forEach((price, i) => {
while (stack.length && prices[stack[stack.length-1]] >= price) {
res[stack.pop()] -= price;
}
stack.push(i);
});
return res;
}
Go实现:
go复制func finalPrices(prices []int) []int {
stack := []int{}
res := make([]int, len(prices))
copy(res, prices)
for i, price := range prices {
for len(stack) > 0 && prices[stack[len(stack)-1]] >= price {
res[stack[len(stack)-1]] -= price
stack = stack[:len(stack)-1]
}
stack = append(stack, i)
}
return res
}
如果题目改为:
这些变种都可以基于单调栈的思路进行扩展,只是判断条件和栈操作需要相应调整。
在电商系统实际开发中,这类价格计算通常会在以下场景出现:
一个经验之谈:在实际项目中,这类计算最好封装成独立服务,并做好缓存策略。因为价格计算往往是高频操作,且对一致性要求较高。我们曾经在项目中遇到过因为价格计算服务性能问题导致的购物车超时错误,后来通过引入本地缓存+分布式缓存的二级缓存机制解决了这个问题。