We define a harmonious array is an array where the difference between its maximum value and its minimum value is exactly 1.
Now, given an integer array, you need to find the length of its longest harmonious subsequence among all its possible subsequences.
Example 1:
Input: [1,3,2,2,5,2,3,7] Output: 5 Explanation: The longest harmonious subsequence is [3,2,2,2,3].
Note: The length of the input array will not exceed 20,000.
這道題給了我們一個數組,讓我們找出最長的和諧子序列,關於和諧子序列就是序列中數組的最大最小差值均為1。由於這里只是讓我們求長度,並不需要返回具體的子序列。所以我們可以對數組進行排序,那么實際上我們只要找出來相差為1的兩個數的總共出現個數就是一個和諧子序列的長度了。明白了這一點,我們就可以建立一個數字和其出現次數之間的映射,利用 TreeMap 的自動排序的特性,那么我們遍歷 TreeMap 的時候就是從小往大開始遍歷,我們從第二個映射對開始遍歷,每次跟其前面的映射對比較,如果二者的數字剛好差1,那么就把二個數字的出現的次數相加並更新結果 res 即可,參見代碼如下:
解法一:
class Solution { public: int findLHS(vector<int>& nums) { if (nums.empty()) return 0; int res = 0; map<int, int> m; for (int num : nums) ++m[num]; for (auto it = next(m.begin()); it != m.end(); ++it) { auto pre = prev(it); if (it->first == pre->first + 1) { res = max(res, it->second + pre->second); } } return res; } };
其實我們並不用向上面那種解法那樣用 next 和 prev 來移動迭代器,因為其用到了 TreeMap 的自動排序功能,所以才可以利用 next 和 prev。其實我們還可以用 HashMap 來做,先遍歷一遍,建立每個數字跟其出現次數之間的映射,然后再遍歷每個數字的時候,只需在 HashMap 中查找該數字加1是否存在,存在就更新結果 res,這樣更簡單一些,參見代碼如下:
解法二:
class Solution { public: int findLHS(vector<int>& nums) { int res = 0; unordered_map<int, int> m; for (int num : nums) ++m[num]; for (auto a : m) { if (m.count(a.first + 1)) { res = max(res, m[a.first] + m[a.first + 1]); } } return res; } };
我們其實也可以在一個 for 循環中搞定,遍歷每個數字時,先累加其映射值,然后查找該數字加1是否存在,存在的話用 m[num] 和 m[num+1] 的和來更新結果 res,同時,還要查找該數字減1是否存在,存在的話用 m[num] 和 m[num-1] 的和來更新結果 res,這樣也是可以的,參見代碼如下:
解法三:
class Solution { public: int findLHS(vector<int>& nums) { int res = 0; unordered_map<int, int> m; for (int num : nums) { ++m[num]; if (m.count(num + 1)) { res = max(res, m[num] + m[num + 1]); } if (m.count(num - 1)) { res = max(res, m[num] + m[num - 1]); } } return res; } };
下面方法不用任何 map,但是需要對數組進行排序,當數組有序了之后,我們就可以一次遍歷搞定了。這實際上用到了滑動窗口 Sliding Window 的思想,用變量 start 記錄當前窗口的左邊界,初始化為0。用 new_start 指向下一個潛在窗口的左邊界,初始化為0。i為當前窗口的右邊界,從1開始遍歷,首先驗證當前窗口的差值是否小於1,用 nums[i] 減去 nums[start],若不滿足,則將 start 賦值為 new_start,即移動到下一個窗口。然后看當前數字跟之前一個數字是否相等,若不相等,說明當前數字可能是下一個潛在窗口的左邊界,將 new_start 賦值為i。然后再看窗口的左右邊界值是否剛好為1,因為題目中說了差值必須正好為1,由於我們對數組排序了,所以只要左右邊界差值正好為1,那么這個窗口包含的數字就可以組成滿足題意的子序列,用其長度來更新結果 res 即可,參見代碼如下:
解法四:
class Solution { public: int findLHS(vector<int>& nums) { int res = 0, start = 0, new_start = 0; sort(nums.begin(), nums.end()); for (int i = 1; i < nums.size(); ++i) { if (nums[i] - nums[start] > 1) start = new_start; if (nums[i] != nums[i - 1]) new_start = i; if (nums[i] - nums[start] == 1) res = max(res, i - start + 1); } return res; } };
參考資料:
https://leetcode.com/problems/longest-harmonious-subsequence/
