Backtracking总结

基本概念:

backtracking(回溯算法)也叫试探法,它是一种系统地搜索问题的解的方法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试

回溯算法说白了就是穷举法。不过回溯算法使用剪枝函数,剪去一些不可能到达最终状态(即答案状态)的节点,从而减少状态空间树节点的生成。 
回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。

  • 回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。
  • 而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。

这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。


backtracking也是一种编程思想,使用到了递归。

backtracking要解决的问题大致具有这样的特征,为了得到问题的解,需要进行若干步骤,每一步的抉择都是相同的,每一步都是在上一步的基础上完成的,需要记录之前的轨迹,直到终点情况,不过有可能是正确也有可能是错误。比如最典型的N皇后问题。需要部署N个皇后,每一次部署都有N种可能。

其程序在实现上满足下列特征:

(1)每一步的处理,先check特殊情况,即return case;这里必须有returncase。

(2)再使用一个for循环,尝试每一种选择,在for循环内,先检测该种选择是否正确,然后如果正确,就在轨迹上记录,然后递归地处理下一步,处理完以后,再把轨迹恢复,供下一中选择进行。这里恢复轨迹是很重要的。

可以分为两类,一种是寻找全部的解,一种是找到一个解。

一个解:递归函数需要返回值,在for循环里面尝试每一种可能时,如果该选择返回true,那么就返回。否则for结束的时候(执行到这里说明所有尝试都失败了)要返回false。利用返回值可以让程序提前返回,只找到一个解。

多个解:这时递归函数可以是void类型,这样就可以搜全部的解,for循环结束也不需要处理。

和dp的比较:这里是指up到bottom递归地dp,二者都涉及了递归,但是有差别,dp的递归是小规模的递归,即解决一个子问题。但是回溯的递归是每一步的选择,可以看成是并列的,都是在一次完整的搜索中的一步。而且dp是从高到低递归,而回溯是从开始到结束,有点从低到高的感觉。

目前为止,涉及到递归地思路有分治法,子问题,dp和回溯。很相近,不过还是有差别。

分治法的子问题不重合,子问题是不带memo的dp,回溯需要记录轨迹。这些事他们的特征。

回溯法用于搜索解,dp找最优解。

下面上一个N皇后的代码,回溯的思路体现的很清晰。包括轨迹和恢复。

public List<List<String>> solveNQueens(int n) {
	    List<List<String>> result = new ArrayList<>();
	    char[][] cash = new char[n][n];
	    for(int i = 0; i < n; i++){
	    	for(int j = 0; j < n; j++){
	    		cash[i][j] = '.';
	    	}
	    }
	    solveQ(result, cash, 0, n);
	    return result;
	}
	public void solveQ(List<List<String>> result, char[][] cash, int row, int n){
		if(row == n){
			List<String> r = new ArrayList<>();
			for(int i = 0; i < n; i++){
				String s = "";
				for(int j = 0; j < n; j++){
					s += cash[i][j];
				}
				r.add(s);
			}
			result.add(r);
			return;
		}
		for(int i = 0; i < n; i++){
			if(isright(cash, row, i, n)){
				cash[row][i] = 'Q';
				solveQ(result, cash, row + 1, n);
				cash[row][i] = '.';
			}
		}
	}
	private boolean isright(char[][] cash, int row, int col, int n){
		for(int i = 0; i <= row; i++){
			if(cash[i][col] == 'Q')
				return false;
		}
		int x = row, y = col;
		while(x >= 0 && y >= 0){
			if(cash[x--][y--] == 'Q')
				return false;
		}
		x = row;y = col;
		while(x >= 0 && y < n){
			if(cash[x--][y++] == 'Q')
				return false;
		}
		return true;
	}


在leetcode中比较经典的backtracking问题有以下几个: 
51. N-Queens 
52. N-Queens II 
39. Combination Sum 
40. Combination Sum II 
216. Combination Sum III  
46. Permutations 
47. Permutations II 
78. Subsets 
90. Subsets II 

131. Palindrome Partitioning

leetcode解题思路:https://blog.csdn.net/crystal6918/article/details/51924665




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值