!!!题解 | 有重复项数字的全排列 理解剪枝,看注释 妙,实在是妙
有重复项数字的全排列
https://www.nowcoder.com/practice/a43a2b986ef34843ac4fdd9159b69863
#include <vector>
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param num int整型vector
* @return int整型vector<vector<>>
*/
vector<vector<int> > permuteUnique(vector<int>& num) {
// write code here
/*去重。以字典序升序所以需要排列,因为是要去重所以更要排序了。
排序的作用就是让那些相同的数字挨在一起,就比较好操作。
可以这样想,全排列就是让不同下标的元素位于不同的位置,如果没有重复元素就是让这些下标分别处在不同的位置;
但是有了重复元素之后,两个相同元素相当于它们对应的下标位置对应的叶子节点是完全一致的,所以对于这样的位置,只需要取其中的一个叶子节点就可以了*/
/*-___________这里的关键在于,排序之后重复的元素一定会相邻。
所以只需要一个判断条件,就是当前元素等于前一个元素并且还前一个元素并没有被使用到,
那么这就是一条等效路径,可以被剪枝*/
int size=num.size();
vector<vector<int> > res;
if(size==0)return res;
sort(num.begin(),num.end());
vector<int> path;
vector<bool> sign(size,false);
func(num, size, sign, path, res);
return res;
}
void func(vector<int>& num,int size,vector<bool>& sign,vector<int>& path, vector<vector<int> >& res){
if(path.size()==size){
res.push_back(path);
return;
}
for(int i=0;i<size;i++){
//去重
/*剪枝可以在判断当前元素是否被使用过之后,因为这个优先级最高。
如果被使用过了,那么就没必要判断是否需要剪枝了*/
if(sign[i]==1)continue;;//这里也不是return,而是continue
if(i>0&&num[i]==num[i-1]&&sign[i-1]==0)continue;
//剪枝,就不会进入以这个元素为根的递归,直接进入下一个元素
/*continue 的作用是去重,结果是否加入只取决于 path 长度是否达到目标,但是这剪枝只有continue没有return,
所以只有找到一条path才会返回,而重复的路径已经被continue给剪枝了。
剪枝只是避免了重复的路径被探索,从而避免了重复的结果被加入*/
path.push_back(num[i]);
sign[i]=true;
func(num, size, sign, path, res);
path.pop_back();
sign[i]=false;
}
}
};

查看20道真题和解析