78. Subsets
public List> subsets(int[] nums) {
//把空集加进去
List> result = new ArrayList>();
result.add(new ArrayList());
List item = new ArrayList();
generate(result, item, nums, 0);
return result;
}
public void generate(List> result,List item, int[] nums, int i) {
//递归结束条件
if(i >= nums.length) {
return;
}
List tempItem = new ArrayList(item);
tempItem.add(nums[i]);
result.add(tempItem);
generate(result, tempItem, nums, i + 1);
generate(result, item, nums, i + 1);
}
public List> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
List> result = new ArrayList>();
result.add(new ArrayList());
generate(result, new ArrayList(), new HashSet>(), nums, 0);
return result;
}
public void generate(List> result, List item,
HashSet> set, int[] nums, int i) {
if(i >= nums.length) {
return;
}
List tempItem = new ArrayList(item);
tempItem.add(nums[i]);
if(set.add(tempItem)) {
result.add(tempItem);
}
generate(result, tempItem, set, nums, i + 1);
generate(result, item, set, nums, i + 1);
}
public List> combinationSum2(int[] nums, int target) {
Arrays.sort(nums);
List> result = new ArrayList>();
generate(result, new ArrayList(), new HashSet>(), nums, target, 0, 0);
return result;
}
public void generate(List> result, List item,
HashSet> set, int[] nums,
int target, int sum, int i) {
if(i >= nums.length || sum > target) { //剪枝操作
return;
}
List tempItem = new ArrayList(item);
tempItem.add(nums[i]);
sum += nums[i];
if(sum == target && set.add(tempItem)) {
result.add(tempItem);
}
generate(result, tempItem, set, nums, target, sum, i + 1);
//回溯
sum -= nums[i];
generate(result, item, set, nums, target, sum, i + 1);
}
public List> permute(int[] nums) {
List> result = new ArrayList>();
generate(0, result, new ArrayList(), nums);
return result;
}
public void generate(int i, List> result, List item,
int[] nums) {
if(i == nums.length) {
result.add(new ArrayList(item));
return;
}
//i代表第i个位置,j代表第j个元素
for(int j = 0; j < nums.length; j++) {
if(item.contains(nums[j])) {
continue;
}
List tempItem = new ArrayList(item);
tempItem.add(i, nums[j]);
generate(i + 1, result, tempItem, nums);
}
}
public List> solveNQueens(int n) {
List> result = new ArrayList>();
int[][] mark = new int[n][n];
List item = new ArrayList();
for(int i = 0; i < n; i++) { //初始化item
StringBuffer sb = new StringBuffer();
for(int j = 0; j < n; j++) {
sb.append(".");
}
item.add(sb.toString());
}
generate(0, item, result, mark);
return result;
}
public void putDownTheQueen(int x, int y, int[][] mark) {
int[] dx = {-1, -1, -1, 0, 1, 1, 1, 0}; //行
int[] dy = {-1, 0, 1, 1, 1, 0, -1, -1}; //列
for(int i = 1; i < mark.length; i++) { //最多走n - 1步
for(int j = 0; j < 8; j++) { //8个方向
int newX = x + i * dx[j];
int newY = y + i * dy[j];
if(newX >= 0 && newX < mark.length //新坐标没有越界,标记为1
&& newY >= 0 && newY < mark.length) {
mark[newX][newY] = 1;
}
}
}
}
public void generate(int k, List item, List> result, int[][] mark) {
if(k == mark.length) {
result.add(new ArrayList(item));
return;
}
for(int i = 0; i < mark.length; i++) {
if(mark[k][i] == 0) {
//放下皇后,并更新item和mark
String tempString = item.get(k);
StringBuffer sb = new StringBuffer(tempString);
sb.setCharAt(i, 'Q');
item.set(k, sb.toString());
//备份mark
int[][] tempMark = new int[mark.length][mark.length];
for(int x = 0; x < mark.length; x++) {
for(int y = 0; y < mark.length; y++) {
tempMark[x][y] = mark[x][y];
}
}
putDownTheQueen(k, i, tempMark);
//进行下一行的放置
generate(k + 1, item, result, tempMark);
item.set(k, tempString);
}
}
}
这几题有很多相似的地方:
1.这几题都有选择与不选择的情况
2.当要把子结果push到result时,如果item是会发生变化的,注意要push(new ArrayList(item)),因为我们要的子结果是这个状态下的item。java是传址的,如果直接push(item) 会导致如果item改变后,result里的子结果跟着改变,最终结果不正确;
3.选择时:我们要对数据进行操作,这时候要把原数据备份,即类似于tempItem。
然后原数据不变,备份数据变,再把备份数据进行递归调用。不选择时:直接把原数据进行递归调用。
4.有时候还可能出现剪枝等优化操作。
5.当进行递归时,如果要避免重复,可以用mark数组。