LeetCode 回溯法合集

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);
	}

90. Subsets II

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);
	}

40. Combination Sum II

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);
	}

46. Permutations

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);
		}
	}

51. N-Queens

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数组。



你可能感兴趣的:(LeetCode解题报告)