今天遇到了很有意思的一類題目,也就是標題裏說的,“下一個更大元素”。
先看看它的簡單版本:
給定兩個沒有重複元素的數組 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每個元素在 nums2 中的下一個比其大的值。
nums1 中數字 x 的下一個更大元素是指 x 在 nums2 中對應位置的右邊的第一個比 x 大的元素。如果不存在,對應位置輸出-1。
一開始看到這個題,第一反應就是HashMap(因爲我用的C++,實際上是unordered_map),建立nums2中每個元素和下標的對應關係,然後拿nums1中的值去找到下標,然後遍歷找到第一個比它大的值:
vector<int> nextGreaterElement(vector<int> &nums1, vector<int> &nums2)
{
int length2 = nums2.size();
unordered_map<int, int> nums2_index_map;
for (int i = 0; i < length2; ++i)
{
nums2_index_map[nums2[i]] = i;
}
vector<int> result;
int length1 = nums1.size();
for (const int &target : nums1)
{
bool has_found = false;
for (int i = nums2_index_map[target]; i < length2; ++i)
{
if (nums2[i] > target)
{
has_found = true;
result.push_back(nums2[i]);
break;
}
}
if (!has_found)
{
result.push_back(-1);
}
}
return result;
}
複雜度大概是O(kn)?反正取決於nums1的長度就是了。結果也相當不錯,歸功於HashMap本身的效率,這麼一個相當暴力的算法的速度居然能超過100%,實在是讓人失去進一步優化的動力……
按理說到這裏就可以結束了,但是我看到一個很有意思的算法,專門用來解決這一類問題,就是所謂的“單調棧”(以下來自labuladong@LeetCode,這是我看到的最好的解釋了):
vector<int> nextGreaterElement(vector<int>& nums) {
vector<int> ans(nums.size()); // 存放答案的數組
stack<int> s;
for (int i = nums.size() - 1; i >= 0; i--) { // 倒着往棧裏放
while (!s.empty() && s.top() <= nums[i]) { // 判定個子高矮
s.pop(); // 矮個起開,反正也被擋着了。。。
}
ans[i] = s.empty() ? -1 : s.top(); // 這個元素身後的第一個高個
s.push(nums[i]); // 進隊,接受之後的身高判定吧!
}
return ans;
}
其中有一個非常有意思的點:vector<int> ans(nums.size());
。我一開始覺得沒啥用,就是直接定義了一個普通的vector,後來纔想起來,聲明大小可以避免vector在push_back的時候重新分配空間,可以提高效率。
所以做算法題的時候還是要錙銖必較啊……