Let's call any (contiguous) subarray B (of A) a mountain if the following properties hold:html
B.length >= 3
0 < i < B.length - 1
such that B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]
(Note that B could be any subarray of A, including the entire array A.)git
Given an array A
of integers, return the length of the longest mountain. github
Return 0
if there is no mountain.數組
Example 1:優化
Input: [2,1,4,7,3,2,5] Output: 5 Explanation: The largest mountain is [1,4,7,3,2] which has length 5.
Example 2:spa
Input: [2,2,2] Output: 0 Explanation: There is no mountain.
Note:code
0 <= A.length <= 10000
0 <= A[i] <= 10000
Follow up:htm
O(1)
space?
這道題給了咱們一個數組,而後定義了一種像山同樣的子數組,就是先遞增再遞減的子數組,注意這裏是強行遞增或者遞減的,並不存在相等的狀況。那麼實際上這道題就是讓在數組中尋找一個位置,使得以此位置爲終點的遞增數組和以此位置爲起點的遞減數組的長度最大。而以某個位置爲起點的遞減數組,若是反個方向來看,其實就是就該位置爲終點的遞增數列,那麼既然都是求最長的遞增數列,咱們能夠分別用兩個 dp 數組 up 和 down,其中 up[i] 表示以 i 位置爲終點的最長遞增數列的個數,down[i] 表示以 i 位置爲起點的最長遞減數列的個數,這樣咱們正向更新 up 數組,反向更新 down 數組便可。先反向更新好了 down 以後,在正向更新 up 數組的同時,也能夠更新結果 res,當某個位置的 up[i] 和 down[i] 均大於0的時候,那麼就能夠用 up[i] + down[i] + 1 來更新結果 res 了,參見代碼以下:blog
解法一:leetcode
class Solution { public: int longestMountain(vector<int>& A) { int res = 0, n = A.size(); vector<int> up(n), down(n); for (int i = n - 2; i >= 0; --i) { if (A[i] > A[i + 1]) down[i] = down[i + 1] + 1; } for (int i = 1; i < n; ++i) { if (A[i] > A[i - 1]) up[i] = up[i - 1] + 1; if (up[i] > 0 && down[i] > 0) res = max(res, up[i] + down[i] + 1); } return res; } };
咱們能夠對空間進行優化,沒必要使用兩個數組來記錄全部位置的信息,而是隻用兩個變量 up 和 down 來分別記錄以當前位置爲終點的最長遞增數列的長度,和以當前位置爲終點的最長遞減數列的長度。 咱們從 i=1 的位置開始遍歷,由於山必需要有上坡和下坡,因此 i=0 的位置永遠不可能成爲 peak。此時再看,若是當前位置跟前面的位置相等了,那麼當前位置的 up 和 down 都要重置爲0,從當前位置開始找新的山,和以前的應該斷開。或者是當 down 不爲0,說明此時是在下坡,若是當前位置大於以前的了,忽然變上坡了,那麼以前的累計也須要重置爲0。而後當前位置再進行判斷,若大於前一個位置,則是上坡,up 自增1,若小於前一個位置,是下坡,down 自增1。當 up 和 down 同時爲正數,則用 up+down+1 來更新結果 res 便可,參見代碼以下:
解法二:
class Solution { public: int longestMountain(vector<int>& A) { int res = 0, up = 0, down = 0, n = A.size(); for (int i = 1; i < n; ++i) { if ((down && A[i - 1] < A[i]) || (A[i - 1] == A[i])) { up = down = 0; } if (A[i - 1] < A[i]) ++up; if (A[i - 1] > A[i]) ++down; if (up > 0 && down > 0) res = max(res, up + down + 1); } return res; } };
咱們能夠換一種思路,仍是一次遍歷就行,進行 while 循環,條件是 i < n-1,而後判斷,當前數字大於等於下一個數字則跳過,由於咱們但願首先上坡,當找到遞增的起點i後,則再開始循環,找山頂 peak,找到了以後,再進行下坡,找到山腳j,這樣若是i,peak,和j都不相同的話,說明找到了一個完整的山,用 j-i+1 來更新結果 res 便可,而後i從j開始繼續遍歷,參見代碼以下:
解法三:
class Solution { public: int longestMountain(vector<int>& A) { int res = 0, i = 0, n = A.size(); while (i < n - 1) { while (i < n - 1 && A[i] >= A[i + 1]) ++i; int peak = i; while (peak < n - 1 && A[peak] < A[peak + 1]) ++peak; int j = peak; while (j < n - 1 && A[j] > A[j + 1]) ++j; if (i < peak && peak < j) res = max(res, j - i + 1); i = j; } return res; } };
也能夠再換種思路,首先來找山峯,peak 的範圍是 [1, n-1],由於首尾兩個數字都不能作山峯,能作山峯的位置上的數必須大於其左右兩邊的數字,而後分別向左右兩個方向遍歷,這樣就能夠找到完整的山,用 right-left+1 來更新結果 res,參見代碼以下:
解法四:
class Solution { public: int longestMountain(vector<int>& A) { int res = 0, n = A.size(); for (int i = 1; i < n - 1; ++i) { if (A[i - 1] < A[i] && A[i + 1] < A[i]) { int left = i - 1, right = i + 1; while (left > 0 && A[left - 1] < A[left]) --left; while (right < n - 1 && A[right] > A[right + 1]) ++right; res = max(res, right - left + 1); } } return res; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/845
參考資料:
https://leetcode.com/problems/longest-mountain-in-array/