[LeetCode] 901. Online Stock Span 股票價格跨度



Write a class StockSpanner which collects daily price quotes for some stock, and returns the span of that stock's price for the current day.

The span of the stock's price today is defined as the maximum number of consecutive days (starting from today and going backwards) for which the price of the stock was less than or equal to today's price.

For example, if the price of a stock over the next 7 days were [100, 80, 60, 70, 60, 75, 85], then the stock spans would be [1, 1, 1, 2, 1, 4, 6].

Example 1:

Input: ["StockSpanner","next","next","next","next","next","next","next"], [[],[100],[80],[60],[70],[60],[75],[85]]
Output: [null,1,1,1,2,1,4,6]
Explanation:
First, S = StockSpanner() is initialized.  Then:
S.next(100) is called and returns 1,
S.next(80) is called and returns 1,
S.next(60) is called and returns 1,
S.next(70) is called and returns 2,
S.next(60) is called and returns 1,
S.next(75) is called and returns 4,
S.next(85) is called and returns 6.

Note that (for example) S.next(75) returned 4, because the last 4 prices
(including today's price of 75) were less than or equal to today's price.

Note:

  1. Calls to StockSpanner.next(int price)will have 1 <= price <= 10^5.
  2. There will be at most 10000 calls to StockSpanner.next per test case.
  3. There will be at most 150000 calls to StockSpanner.next across all test cases.
  4. The total time limit for this problem has been reduced by 75% for C++, and 50% for all other languages.

這道題定義了一個 StockSpanner 的類,有一個 next 函數,每次給當天的股價,讓我們返回之前連續多少天都是小於等於當前股價。跟 OJ 抗爭多年的經驗告訴我們,不要想着可以用最暴力的向前搜索的方法,這種解法太 trivial 了,肯定無法通過的。那么可以找連續遞增的子數組的長度么,其實也是不行的,就拿題目中的例子來說吧 [100, 80, 60, 70, 60, 75, 85],數字 75 前面有三天是比它小的,但是這三天不是有序的,是先增后減的,那怎么辦呢?我們先從簡單的情況分析,假如當前的股價要小於前一天的,那么連續性直接被打破了,所以此時直接返回1就行了。但是假如大於等於前一天股價的話,情況就比較 tricky 了,因為此時所有小於等於前一天股價的天數肯定也是小於等於當前的,那么我們就需要知道是哪一天的股價剛好大於前一天的股價,然后用這一天的股價跟當前的股價進行比較,若大於當前股價,說明當前的連續天數就是前一天的連續天數加1,而若小於當前股價,我們又要重復這個過程,去比較剛好大於之前那個的股價。所以我們需要知道對於每一天,往前推剛好大於當前股價的是哪一天,用一個數組 pre,其中 pre[i] 表示從第i天往前推剛好大於第i天的股價的是第 pre[i] 天。接下來看如何實現 next 函數,首先將當前股價加入 nums 數組,然后前一天在數組中的位置就是 (int)nums.size()-2。再來想想 corner case 的情況,假如當前是數組中的第0天,前面沒有任何股價了,我們的 pre[0] 就賦值為 -1 就行了,怎么知道當前是否是第0天,就看 pre 數組是否為空。再有就是由於i要不斷去 pre 數組中找到之前的天數,所以最終i是有可能到達 pre[0] 的,那么就要判斷當i為 -1 時,也要停止循環。循環的最后一個條件就是當之前的股價小於等當前的估計 price 時,才進行循環,這個前面講過了,循環內部就是將 pre[i] 賦值給i,這樣就完成了跳到之前天的操作。while 循環結束后要將i加入到 pre 數組,因為這個i就是從當前天往前推,一個大於當前股價的那一天,有了這個i,就可以計算出連續天數了,參見代碼如下:
解法一:
class StockSpanner {
public:
    StockSpanner() {}
    
    int next(int price) {
    	nums.push_back(price);
		int i = (int)nums.size() - 2;
		while (!pre.empty() && i >= 0 && nums[i] <= price) {
			i = pre[i];
		}
		pre.push_back(i);
		return (int)pre.size() - 1 - i;
    }

private:
	vector<int> nums, pre;
};

我們還可以使用棧來做,里面放一個 pair 對兒,分別是當前的股價和之前比其小的連續天數。在 next 函數中,使用一個 cnt 變量,初始化為1。還是要個 while 循環,其實核心的本質都是一樣的,循環的條件首先是棧不能為空,並且棧頂元素的股價小於等於當前股價,那么 cnt 就加上棧頂元素的連續天數,可以感受到跟上面解法在這里的些許不同之處了吧,之前是一直找到第一個大於當前股價的天數在數組中的位置,然后相減得到連續天數,這里是在找的過程中直接累加連續天數,最終都可以得到正確的結果,參見代碼如下:
解法二:
class StockSpanner {
public:
    StockSpanner() {}
    
    int next(int price) {       
        int cnt = 1;
    	while (!st.empty() && st.top().first <= price) {
            cnt += st.top().second; st.pop();
        }
        st.push({price, cnt});
        return cnt;
    }

private:
	stack<pair<int, int>> st;
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/901


參考資料:

https://leetcode.com/problems/online-stock-span/

https://leetcode.com/problems/online-stock-span/discuss/168311/C%2B%2BJavaPython-O(1)

https://leetcode.com/problems/online-stock-span/discuss/168288/Java-short-solution-using-list-with-explanation


[LeetCode All in One 題目講解匯總(持續更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM