Medium
题目描述
设计一个算法,收集某只股票的每日报价,并返回该股票当日价格的跨度。
股票价格在某一天的跨度是指从该天开始向前推(包括该天),连续多少天的股票价格小于或等于该天价格的最大天数。
例如,如果股票在过去四天的价格是 [7,2,1,2],今天的价格是 2,那么今天的跨度是 4,因为从今天开始,连续 4 天的股票价格都小于或等于 2。
又例如,如果股票在过去四天的价格是 [7,34,1,2],今天的价格是 8,那么今天的跨度是 3,因为从今天开始,连续 3 天的股票价格都小于或等于 8。
实现 StockSpanner 类:
StockSpanner()初始化类的对象。int next(int price)返回给定今天价格 price 的股票价格跨度。
示例 1:
输入:
["StockSpanner", "next", "next", "next", "next", "next", "next", "next"]
[[], [100], [80], [60], [70], [60], [75], [85]]
输出:
[null, 1, 1, 1, 2, 1, 4, 6]
解释:
StockSpanner stockSpanner = new StockSpanner();
stockSpanner.next(100); // 返回 1
stockSpanner.next(80); // 返回 1
stockSpanner.next(60); // 返回 1
stockSpanner.next(70); // 返回 2
stockSpanner.next(60); // 返回 1
stockSpanner.next(75); // 返回 4,因为最后 4 个价格(包括今天的价格 75)都小于或等于今天的价格
stockSpanner.next(85); // 返回 6
提示:
1 <= price <= 10^5- 最多调用
next方法10^4次
解题思路
解题思路
这是一个典型的单调栈问题。我们需要找到当前价格之前连续多少天的价格小于等于当前价格。
暴力解法: 每次调用 next 时,遍历之前所有的价格,计算连续天数。时间复杂度为 O(n)。
单调栈优化: 使用单调递减栈来优化查找过程。栈中存储 (价格, 跨度) 对:
- 当新价格到来时,如果栈顶元素的价格小于等于当前价格,说明这些天都包含在当前价格的跨度中
- 将栈顶元素弹出并累加其跨度,直到栈为空或栈顶价格大于当前价格
- 当前价格的跨度 = 1(自己) + 所有弹出元素的跨度之和
- 将当前价格和其跨度入栈
核心思想: 利用单调栈的性质,避免重复计算之前已经处理过的天数跨度。
例如序列 [100,80,60,70]:
- 100: 栈空,跨度1,栈:[(100,1)]
- 80: 80<100,跨度1,栈:[(100,1),(80,1)]
- 60: 60<80,跨度1,栈:[(100,1),(80,1),(60,1)]
- 70: 70>60,弹出(60,1),跨度=1+1=2,栈:[(100,1),(80,1),(70,2)]
代码实现
class StockSpanner {
private:
stack<pair<int, int>> st; // (price, span)
public:
StockSpanner() {
}
int next(int price) {
int span = 1;
while (!st.empty() && st.top().first <= price) {
span += st.top().second;
st.pop();
}
st.push({price, span});
return span;
}
};
class StockSpanner:
def __init__(self):
self.stack = [] # (price, span)
def next(self, price: int) -> int:
span = 1
while self.stack and self.stack[-1][0] <= price:
span += self.stack.pop()[1]
self.stack.append((price, span))
return span
public class StockSpanner {
private Stack<(int price, int span)> stack;
public StockSpanner() {
stack = new Stack<(int, int)>();
}
public int Next(int price) {
int span = 1;
while (stack.Count > 0 && stack.Peek().price <= price) {
span += stack.Pop().span;
}
stack.Push((price, span));
return span;
}
}
var StockSpanner = function() {
this.stack = []; // [price, span]
};
StockSpanner.prototype.next = function(price) {
let span = 1;
while (this.stack.length > 0 && this.stack[this.stack.length - 1][0] <= price) {
span += this.stack.pop()[1];
}
this.stack.push([price, span]);
return span;
};
复杂度分析
| 操作 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 构造函数 | O(1) | O(1) |
| next() | 均摊 O(1) | O(n) |
说明:
- 时间复杂度:虽然单次调用可能是 O(n),但每个元素最多入栈和出栈各一次,所以均摊时间复杂度是 O(1)
- 空间复杂度:O(n),其中 n 是调用次数,最坏情况下所有元素都在栈中
相关题目
- . Daily Temperatures (Medium)