視頻講解:https://www.bilibili.com/video/BV1vm4y1F71J/?vd_source=a935eaede74a204ec74fd041b917810c
文檔講解:https://programmercarl.com/0090.%E5%AD%90%E9%9B%86II.html#%E6%80%9D%E8%B7%AF
力扣題目:https://leetcode.cn/problems/subsets-ii/
其實這道題時78.集合和40.組合總和III的結合體,主要需要注意以下幾點:
- 首先需要將數組排序,方便我們后續操作
- 子集去重:為了防止找到前面的元素從而輸出相同的子集,在遍歷過程中應該像78.集合中一樣,不要向前遍歷,要向后遍歷,好馬不吃回頭草。
- 數層去重:為了防止數組中重復元素導致的重復子集,我們引入used,used表示每一個數是否使用過的情況,如果相同元素都使用過,說明這個是在同一樹枝上的集合,說明這個子集合法,如果發現相同元素時used顯示一個用過一個沒用過,說明重新開始遍歷到了重復元素,所以直接continue掉他
- 在bool數組的賦值中,可以直接vector used(nums.size(), false);賦值,用for循環力扣會超時報錯。
class Solution {
private:vector<int> path;vector<vector<int>> result;void backtracking(vector<int>& nums, int startIndex, vector<bool>& used){//向result數組中存入子集result.push_back(path);/*//判斷終止條件if(startIndex >= nums.size()){return;}*///單層搜索for(int i = startIndex; i < nums.size(); i++){//樹層去重if(i > 0 && nums[i] == nums[i-1] && used[i-1] == false)//如果i>0且nums的i和i-1相等并且沒有用過之前的重復元素,說明時新的遍歷下的重復元素,剪枝刪掉{continue;}//存入元素path.push_back(nums[i]);//更新usedused[i] = true;//回溯算法backtracking(nums, i + 1, used);//彈出元素path.pop_back();//更新usedused[i] = false;}return;}
public:vector<vector<int>> subsetsWithDup(vector<int>& nums) {//path和result初始化path.clear();result.clear();//used初始化并賦初值vector<bool> used(nums.size(), false);sort(nums.begin(), nums.end()); // 去重需要排序//執行回溯算法backtracking(nums, 0, used);//返回結果return result;}
};