Given an integer array, your task is to find all the different possible increasing subsequences of the given array, and the length of an increasing subsequence should be at least 2 .
Example:
Input: [4, 6, 7, 7] Output: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
Note:
- The length of the given array will not exceed 15.
- The range of integer in the given array is [-100,100].
- The given array may contain duplicates, and two equal integers should also be considered as a special case of increasing sequence.
這道題讓我們找出所有的遞增子序列,應該不難想到,這題肯定是要先找出所有的子序列,從中找出遞增的。找出所有的子序列的題之前也接觸過 Subsets 和 Subsets II,那兩題不同之處在於數組中有沒有重復項。而這道題明顯是有重復項的,所以需要用到 Subsets II 中的解法。首先來看一種迭代的解法,對於重復項的處理,最偷懶的方法是使用 TreeSet,利用其自動去處重復項的機制,然后最后返回時再轉回 vector 即可。由於是找遞增序列,所以需要對遞歸函數做一些修改,首先題目中說明了遞增序列數字至少兩個,所以只有子序列個數大於等於2時,才加入結果。然后就是要遞增,如果之前的數字大於當前的數字,那么跳過這種情況,繼續循環,參見代碼如下:
解法一:
class Solution { public: vector<vector<int>> findSubsequences(vector<int>& nums) { set<vector<int>> res; vector<int> out; helper(nums, 0, out, res); return vector<vector<int>>(res.begin(), res.end()); } void helper(vector<int>& nums, int start, vector<int>& out, set<vector<int>>& res) { if (out.size() >= 2) res.insert(out); for (int i = start; i < nums.size(); ++i) { if (!out.empty() && out.back() > nums[i]) continue; out.push_back(nums[i]); helper(nums, i + 1, out, res); out.pop_back(); } } };
我們也可以在遞歸中進行去重復處理,方法是用一個 HashSet 保存中間過程的數字,如果當前的數字在之前出現過了,就直接跳過這種情況即可,參見代碼如下:
解法二:
class Solution { public: vector<vector<int>> findSubsequences(vector<int>& nums) { vector<vector<int>> res; vector<int> out; helper(nums, 0, out, res); return res; } void helper(vector<int>& nums, int start, vector<int>& out, vector<vector<int>>& res) { if (out.size() >= 2) res.push_back(out); unordered_set<int> st; for (int i = start; i < nums.size(); ++i) { if ((!out.empty() && out.back() > nums[i]) || st.count(nums[i])) continue; out.push_back(nums[i]); st.insert(nums[i]); helper(nums, i + 1, out, res); out.pop_back(); } } };
下面我們來看迭代的解法,還是老套路,先看偷懶的方法,用 TreeSet 來去處重復。對於遞歸的處理方法跟之前相同,參見代碼如下:
解法三:
class Solution { public: vector<vector<int>> findSubsequences(vector<int>& nums) { set<vector<int>> res; vector<vector<int>> cur(1); for (int i = 0; i < nums.size(); ++i) { int n = cur.size(); for (int j = 0; j < n; ++j) { if (!cur[j].empty() && cur[j].back() > nums[i]) continue; cur.push_back(cur[j]); cur.back().push_back(nums[i]); if (cur.back().size() >= 2) res.insert(cur.back()); } } return vector<vector<int>>(res.begin(), res.end()); } };
我們來看不用 TreeSet 的方法,使用一個 HashMap 來建立每個數字對應的遍歷起始位置,默認都是0,然后在遍歷的時候先取出原有值當作遍歷起始點,然后更新為當前位置,如果某個數字之前出現過,那么取出的原有值就不是0,而是之前那個數的出現位置,這樣就不會產生重復了,如果不太好理解的話就帶個簡單的實例去試試吧,參見代碼如下:
解法四:
class Solution { public: vector<vector<int>> findSubsequences(vector<int>& nums) { vector<vector<int>> res, cur(1); unordered_map<int, int> m; for (int i = 0; i < nums.size(); ++i) { int n = cur.size(), start = m[nums[i]]; m[nums[i]] = n; for (int j = start; j < n; ++j) { if (!cur[j].empty() && cur[j].back() > nums[i]) continue; cur.push_back(cur[j]); cur.back().push_back(nums[i]); if (cur.back().size() >= 2) res.push_back(cur.back()); } } return res; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/491
類似題目:
參考資料:
https://leetcode.com/problems/increasing-subsequences/