题目描述
给定一个可包含重复数字的序列,返回所有不重复的全排列。
思路
序列中的元素可重复,但返回的结果不能重复。
在一定会产生重复结果的地方剪枝。
一个比较容易想到的办法是在结果集中去重,但是这些结果集中的元素是一个又一个的列表,对列表去重不像用哈希表对基本元素去重那样容易。
如果要比较两个列表是否一样,一个很显然的方法是分别排序,然后逐个对比。既然要排序,我们可以在搜索之前就对候选数组排序,一旦发现这一支搜索下去可能搜索到重复的元素就停止搜索,这样结果集中不会包含重复元素。
产生重复结点的地方,这是图中标注了“剪刀”,且被绿色框框住的地方。
在图中 ② 处,搜索的数也和上次一样,但是上次的 1 还在使用中;
在图中 ① 处,搜索的数也和上一次一样,但是上一次的 1 刚刚被撤销,正是因为刚被撤销,下面的搜索中还会使用到,因此会产生重复,剪掉的就应该是这样的分支。
遇到两个相同数字,可以:
- 两个都选
- 两个都不选
- 如果只选一个,那么选哪一个都可以,只有这种情况需要剪枝。
这部分的剪枝条件为:和前一个元素值相同(此时隐含这个元素的index>0),并且前一个元素还没有被使用过。
还有一个剪枝条件:用过的元素不能再使用。
代码
一个问题
两种不同剪枝条件(!visited[i - 1] ) 和 (visited[i - 1] )的区别:
-
!visited[i - 1]
-
visited[i - 1]
-
结论
visited[i - 1]前面加不加感叹号的区别仅在于保留的是相同元素的顺序索引还是倒序索引。很明显,顺序索引(即使用(!visited[i - 1])作为剪枝判定条件得到)的递归树剪枝更彻底,思路也相对更自然。