回溯算法去重问题的另一种写法
参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!
# 回溯算法去重问题的另一种写法 > 在 [本周小结!(回溯算法系列三)](https://programmercarl.com/周总结/20201112回溯周末总结.html) 中一位录友对 整棵树的本层和同一节点的本层有疑问,也让我重新思考了一下,发现这里确实有问题,所以专门写一篇来纠正,感谢录友们的积极交流哈! 接下来我再把这块再讲一下。 在[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)中的去重和 [回溯算法:递增子序列](https://programmercarl.com/0491.递增子序列.html)中的去重 都是 同一父节点下本层的去重。 [回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)也可以使用set针对同一父节点本层去重,但子集问题一定要排序,为什么呢? 我用没有排序的集合{2,1,2,2}来举例子画一个图,如图:  图中,大家就很明显的看到,子集重复了。 那么下面我针对[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html) 给出使用set来对本层去重的代码实现。 ## 90.子集II used数组去重版本: [回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html) 使用set去重的版本如下:class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex, vector<bool>& used) {
result.push_back(path);
unordered_set<int> uset; // 定义set对同一节点下的本层去重
for (int i = startIndex; i < nums.size(); i++) {
if (uset.find(nums[i]) != uset.end()) { // 如果发现出现过就pass
continue;
}
uset.insert(nums[i]); // set跟新元素
path.push_back(nums[i]);
backtracking(nums, i + 1, used);
path.pop_back();
}
}
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
result.clear();
path.clear();
vector<bool> used(nums.size(), false);
sort(nums.begin(), nums.end()); // 去重需要排序
backtracking(nums, 0, used);
return result;
}
};
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
unordered_set<int> uset; // 把uset定义放到类成员位置
void backtracking(vector<int>& nums, int startIndex, vector<bool>& used) {
result.push_back(path);
for (int i = startIndex; i < nums.size(); i++) {
if (uset.find(nums[i]) != uset.end()) {
continue;
}
uset.insert(nums[i]); // 递归之前insert
path.push_back(nums[i]);
backtracking(nums, i + 1, used);
path.pop_back();
uset.erase(nums[i]); // 回溯再erase
}
}
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
unordered_set<int> uset; // 把uset定义放到类成员位置
void backtracking(vector<int>& nums, int startIndex, vector<bool>& used) {
result.push_back(path);
uset.clear(); // 到每一层的时候,清空uset
for (int i = startIndex; i < nums.size(); i++) {
if (uset.find(nums[i]) != uset.end()) {
continue;
}
uset.insert(nums[i]); // set记录元素
path.push_back(nums[i]);
backtracking(nums, i + 1, used);
path.pop_back();
}
}
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {
if (sum == target) {
result.push_back(path);
return;
}
unordered_set<int> uset; // 控制某一节点下的同一层元素不能重复
for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
if (uset.find(candidates[i]) != uset.end()) {
continue;
}
uset.insert(candidates[i]); // 记录元素
sum += candidates[i];
path.push_back(candidates[i]);
backtracking(candidates, target, sum, i + 1);
sum -= candidates[i];
path.pop_back();
}
}
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
path.clear();
result.clear();
sort(candidates.begin(), candidates.end());
backtracking(candidates, target, 0, 0);
return result;
}
};
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking (vector<int>& nums, vector<bool>& used) {
if (path.size() == nums.size()) {
result.push_back(path);
return;
}
unordered_set<int> uset; // 控制某一节点下的同一层元素不能重复
for (int i = 0; i < nums.size(); i++) {
if (uset.find(nums[i]) != uset.end()) {
continue;
}
if (used[i] == false) {
uset.insert(nums[i]); // 记录元素
used[i] = true;
path.push_back(nums[i]);
backtracking(nums, used);
path.pop_back();
used[i] = false;
}
}
}
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
result.clear();
path.clear();
sort(nums.begin(), nums.end()); // 排序
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return result;
}
};
class Solution {
private List<List<Integer>> res = new ArrayList<>();
private List<Integer> path = new ArrayList<>();
private boolean[] used = null;
public List<List<Integer>> permuteUnique(int[] nums) {
used = new boolean[nums.length];
Arrays.sort(nums);
backtracking(nums);
return res;
}
public void backtracking(int[] nums) {
if (path.size() == nums.length) {
res.add(new ArrayList<>(path));
return;
}
HashSet<Integer> hashSet = new HashSet<>();//层去重
for (int i = 0; i < nums.length; i++) {
if (hashSet.contains(nums[i]))
continue;
if (used[i] == true)//枝去重
continue;
hashSet.add(nums[i]);//记录元素
used[i] = true;
path.add(nums[i]);
backtracking(nums);
path.remove(path.size() - 1);
used[i] = false;
}
}
}
class Solution {
List<List<Integer>> reslut = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
if(nums.length == 0){
reslut.add(path);
return reslut;
}
Arrays.sort(nums);
backtracking(nums,0);
return reslut;
}
public void backtracking(int[] nums,int startIndex){
reslut.add(new ArrayList<>(path));
if(startIndex >= nums.length)return;
HashSet<Integer> hashSet = new HashSet<>();
for(int i = startIndex; i < nums.length; i++){
if(hashSet.contains(nums[i])){
continue;
}
hashSet.add(nums[i]);
path.add(nums[i]);
backtracking(nums,i+1);
path.removeLast();
}
}
}
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort( candidates );
if( candidates[0] > target ) return result;
backtracking(candidates,target,0,0);
return result;
}
public void backtracking(int[] candidates,int target,int sum,int startIndex){
if( sum > target )return;
if( sum == target ){
result.add( new ArrayList<>(path) );
}
HashSet<Integer> hashSet = new HashSet<>();
for( int i = startIndex; i < candidates.length; i++){
if( hashSet.contains(candidates[i]) ){
continue;
}
hashSet.add(candidates[i]);
path.add(candidates[i]);
sum += candidates[i];
backtracking(candidates,target,sum,i+1);
path.removeLast();
sum -= candidates[i];
}
}
}
class Solution:
def subsetsWithDup(self, nums):
nums.sort() # 去重需要排序
result = []
self.backtracking(nums, 0, [], result)
return result
def backtracking(self, nums, startIndex, path, result):
result.append(path[:])
used = set()
for i in range(startIndex, len(nums)):
if nums[i] in used:
continue
used.add(nums[i])
path.append(nums[i])
self.backtracking(nums, i + 1, path, result)
path.pop()
class Solution:
def combinationSum2(self, candidates, target):
candidates.sort()
result = []
self.backtracking(candidates, target, 0, 0, [], result)
return result
def backtracking(self, candidates, target, sum, startIndex, path, result):
if sum == target:
result.append(path[:])
return
used = set()
for i in range(startIndex, len(candidates)):
if sum + candidates[i] > target:
break
if candidates[i] in used:
continue
used.add(candidates[i])
sum += candidates[i]
path.append(candidates[i])
self.backtracking(candidates, target, sum, i + 1, path, result)
sum -= candidates[i]
path.pop()
class Solution:
def permuteUnique(self, nums):
nums.sort() # 排序
result = []
self.backtracking(nums, [False] * len(nums), [], result)
return result
def backtracking(self, nums, used, path, result):
if len(path) == len(nums):
result.append(path[:])
return
used_set = set()
for i in range(len(nums)):
if nums[i] in used_set:
continue
if not used[i]:
used_set.add(nums[i])
used[i] = True
path.append(nums[i])
self.backtracking(nums, used, path, result)
path.pop()
used[i] = False
function subsetsWithDup(nums) {
nums.sort((a, b) => a - b);
const resArr = [];
backTraking(nums, 0, []);
return resArr;
function backTraking(nums, startIndex, route) {
resArr.push([...route]);
const helperSet = new Set();
for (let i = startIndex, length = nums.length; i < length; i++) {
if (helperSet.has(nums[i])) continue;
helperSet.add(nums[i]);
route.push(nums[i]);
backTraking(nums, i + 1, route);
route.pop();
}
}
};
function combinationSum2(candidates, target) {
candidates.sort((a, b) => a - b);
const resArr = [];
backTracking(candidates, target, 0, 0, []);
return resArr;
function backTracking( candidates, target, curSum, startIndex, route ) {
if (curSum > target) return;
if (curSum === target) {
resArr.push([...route]);
return;
}
const helperSet = new Set();
for (let i = startIndex, length = candidates.length; i < length; i++) {
let tempVal = candidates[i];
if (helperSet.has(tempVal)) continue;
helperSet.add(tempVal);
route.push(tempVal);
backTracking(candidates, target, curSum + tempVal, i + 1, route);
route.pop();
}
}
};
function permuteUnique(nums) {
const resArr = [];
const usedArr = [];
backTracking(nums, []);
return resArr;
function backTracking(nums, route) {
if (nums.length === route.length) {
resArr.push([...route]);
return;
}
const usedSet = new Set();
for (let i = 0, length = nums.length; i < length; i++) {
if (usedArr[i] === true || usedSet.has(nums[i])) continue;
usedSet.add(nums[i]);
route.push(nums[i]);
usedArr[i] = true;
backTracking(nums, route);
usedArr[i] = false;
route.pop();
}
}
};
function subsetsWithDup(nums: number[]): number[][] {
nums.sort((a, b) => a - b);
const resArr: number[][] = [];
backTraking(nums, 0, []);
return resArr;
function backTraking(nums: number[], startIndex: number, route: number[]): void {
resArr.push([...route]);
const helperSet: Set<number> = new Set();
for (let i = startIndex, length = nums.length; i < length; i++) {
if (helperSet.has(nums[i])) continue;
helperSet.add(nums[i]);
route.push(nums[i]);
backTraking(nums, i + 1, route);
route.pop();
}
}
};
function combinationSum2(candidates: number[], target: number): number[][] {
candidates.sort((a, b) => a - b);
const resArr: number[][] = [];
backTracking(candidates, target, 0, 0, []);
return resArr;
function backTracking(
candidates: number[], target: number,
curSum: number, startIndex: number, route: number[]
) {
if (curSum > target) return;
if (curSum === target) {
resArr.push([...route]);
return;
}
const helperSet: Set<number> = new Set();
for (let i = startIndex, length = candidates.length; i < length; i++) {
let tempVal: number = candidates[i];
if (helperSet.has(tempVal)) continue;
helperSet.add(tempVal);
route.push(tempVal);
backTracking(candidates, target, curSum + tempVal, i + 1, route);
route.pop();
}
}
};
function permuteUnique(nums: number[]): number[][] {
const resArr: number[][] = [];
const usedArr: boolean[] = [];
backTracking(nums, []);
return resArr;
function backTracking(nums: number[], route: number[]): void {
if (nums.length === route.length) {
resArr.push([...route]);
return;
}
const usedSet: Set<number> = new Set();
for (let i = 0, length = nums.length; i < length; i++) {
if (usedArr[i] === true || usedSet.has(nums[i])) continue;
usedSet.add(nums[i]);
route.push(nums[i]);
usedArr[i] = true;
backTracking(nums, route);
usedArr[i] = false;
route.pop();
}
}
};
use std::collections::HashSet;
impl Solution {
pub fn subsets_with_dup(mut nums: Vec<i32>) -> Vec<Vec<i32>> {
let mut res = vec![];
let mut path = vec![];
nums.sort();
Self::backtracking(&nums, &mut path, &mut res, 0);
res
}
pub fn backtracking(
nums: &Vec<i32>,
path: &mut Vec<i32>,
res: &mut Vec<Vec<i32>>,
start_index: usize,
) {
res.push(path.clone());
let mut helper_set = HashSet::new();
for i in start_index..nums.len() {
if helper_set.contains(&nums[i]) {
continue;
}
helper_set.insert(nums[i]);
path.push(nums[i]);
Self::backtracking(nums, path, res, i + 1);
path.pop();
}
}
}
use std::collections::HashSet;
impl Solution {
pub fn backtracking(
candidates: &Vec<i32>,
target: i32,
sum: i32,
path: &mut Vec<i32>,
res: &mut Vec<Vec<i32>>,
start_index: usize,
) {
if sum > target {
return;
}
if sum == target {
res.push(path.clone());
}
let mut helper_set = HashSet::new();
for i in start_index..candidates.len() {
if sum + candidates[i] <= target {
if helper_set.contains(&candidates[i]) {
continue;
}
helper_set.insert(candidates[i]);
path.push(candidates[i]);
Self::backtracking(candidates, target, sum + candidates[i], path, res, i + 1);
path.pop();
}
}
}
pub fn combination_sum2(mut candidates: Vec<i32>, target: i32) -> Vec<Vec<i32>> {
let mut res = vec![];
let mut path = vec![];
candidates.sort();
Self::backtracking(&candidates, target, 0, &mut path, &mut res, 0);
res
}
}
use std::collections::HashSet;
impl Solution {
pub fn permute_unique(mut nums: Vec<i32>) -> Vec<Vec<i32>> {
let mut res = vec![];
let mut path = vec![];
let mut used = vec![false; nums.len()];
Self::backtracking(&mut res, &mut path, &nums, &mut used);
res
}
pub fn backtracking(
res: &mut Vec<Vec<i32>>,
path: &mut Vec<i32>,
nums: &Vec<i32>,
used: &mut Vec<bool>,
) {
if path.len() == nums.len() {
res.push(path.clone());
return;
}
let mut helper_set = HashSet::new();
for i in 0..nums.len() {
if used[i] || helper_set.contains(&nums[i]) {
continue;
}
helper_set.insert(nums[i]);
path.push(nums[i]);
used[i] = true;
Self::backtracking(res, path, nums, used);
used[i] = false;
path.pop();
}
}
}