如果将!used[i - 1]改成 used[i - 1], 结果也是正确的!很神奇。
if (used[i] || i > 0 && nums[i - 1] == nums[i] && used[i - 1])
continue;
这是为什么呢,就是上面我刚说的,如果要对树层中前一位去重,就用!used[i - 1]。如果要对树枝前一位去重用used[i - 1]。
对于排列问题,树层上去重和树枝上去重,都是可以的,但是树层上去重效率更高!
用[1,1] 来举一个例子。
树层上去重(!used[i - 1]),的树形结构如下:
树枝上去重(used[i - 1])的树型结构如下:
用[1,1,1] 再来举一个例子。
树层上去重(!used[i - 1]),的树形结构如下:
树枝上去重(used[i - 1])的树型结构如下:
显然,树层上对前一位去重非常彻底,效率很高,树枝上对前一位去重虽然最后可以得到答案,但是做了很多无用搜索。
小结
树层上去重要加上!used[i - 1],似非而是,代码结合图加深理解吧!
参考
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class PermutationsII {
public List<List> permuteUnique(int[] nums) {
List<List> result = new ArrayList<>();
List path = new ArrayList<>();
boolean[] used = new boolean[nums.length];
Arrays.sort(nums);
backtracking(path, nums, used, result);
return result;
}
private void backtracking(List path, int[] nums, boolean[] used, List<List> result) {
if (path.size() == nums.length) {
result.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i] || i > 0 && nums[i - 1] == nums[i] && !used[i - 1])
continue;
used[i] = true;
path.add(nums[i]);
backtracking(path, nums, used, result);
path.remove(path.size() - 1);
used[i] = false;
}
}
}
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import org.hamcrest.Matcher;
import org.junit.Test;
@SuppressWarnings(“unchecked”)
public class PermutationsIITest {
private final int[] array1 = {1, 1, 2};
private final int[] array2 = {1, 2, 3};
private final int[] array3 = {0, 1, 0, 0, 9};
private final Matcher<Iterable<? extends List>> expected1 = containsInAnyOrder(Arrays.asList(1,1,2), //
Arrays.asList(1,2,1), Arrays.asList(2,1,1));
private final Matcher<Iterable<? extends List>> expected2 = containsInAnyOrder(Arrays.asList(1,2,3), Arrays.asList(1,3,2),//
Arrays.asList(2,1,3), Arrays.asList(2,3,1), Arrays.asList(3,1,2), Arrays.asList(3, 2, 1));
private final String expected3String = “[0,0,0,1,9],[0,0,0,9,1],[0,0,1,0,9],[0,0,1,9,0],[0,0,9,0,1],”//
-
“[0,0,9,1,0],[0,1,0,0,9],[0,1,0,9,0],[0,1,9,0,0],[0,9,0,0,1],”//
-
“[0,9,0,1,0],[0,9,1,0,0],[1,0,0,0,9],[1,0,0,9,0],[1,0,9,0,0],”//
-
“[1,9,0,0,0],[9,0,0,0,1],[9,0,0,1,0],[9,0,1,0,0],[9,1,0,0,0]”;
private final Matcher<Iterable<? extends List>> expected3 = containsInAnyOrder(string2IntegerList(expected3String));
@Test
public void test() {
PermutationsII obj = new PermutationsII();
assertThat(obj.permuteUnique(array1), expected1);
assertThat(obj.permuteUnique(array2), expected2);
assertThat(obj.permuteUnique(array3), expected3);
}
private List[] string2IntegerList(String original){
List[] result;
original = original.substring(1, original.length() - 1);
String[] strs = original.split(“\],\[”);
result = new ArrayList[strs.length];
for(int i = 0; i < strs.length; i++) {
String[] nums = strs[i].split(“,”);
List list = new ArrayList<>();
for(String num : nums) {
list.add(Integer.valueOf(num));
}
result[i] = list;
}
return result;
}
@Test
public void testString2IntegerList() {
String str = “[0,0,0,1,9],[0,0,0,9,1],[0,0,1,0,9],[0,0,1,9,0],[0,0,9,1,0],”
-
“[0,0,9,0,1],[0,1,0,0,9],[0,1,0,9,0],[0,1,9,0,0],[0,9,0,1,0],”
-
“[0,9,0,0,1],[0,9,1,0,0],[0,9,0,1,0],[0,9,0,0,1],[1,0,0,0,9],”
-
“[1,0,0,9,0],[1,0,9,0,0],[1,9,0,0,0],[9,0,0,1,0],[9,0,0,0,1],”
-
“[9,0,1,0,0],[9,0,0,1,0],[9,0,0,0,1],[9,1,0,0,0],[9,0,0,1,0],”
-
“[9,0,0,0,1],[9,0,1,0,0],[9,0,0,1,0],[9,0,0,0,1]”;