回溯算法学习体会

回溯算法的学习体会。本文包括回溯算法的引入,编程框架,以及相关例题的详解。希望通过此文,加深读者对回溯算法的理解。
摘要由CSDN通过智能技术生成

引言

在正式谈论回溯算法以前,我们不妨以一道经典算法题作为引入。LeetCode 46. 全排列,给定一个没有重复数字的序列,返回其所有可能的全排列。 例如,输入为[1,2,3],则输出应为[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]。我们暂时不要考虑如何用编程方式去实现这一过程,现在假设是人力罗列,我们应该遵循一个什么样的列写规则?一种简单的列写规则如下图所示。
初始时,可供我们选择的数字包含在集合{1,2,3}中。第一步,我们从集合中选取1作为起始节点,然后更新集合为{2,3}。第二步,我们从集合中选取2作为第二个节点,然后更新集合为{3}。第三步,我们从集合中选取3作为第三个节点,然后更新集合为{},此时集合为空集,代表我们已经完整找到了一个可行结果[1,2,3]。回顾来看,我们每一步都在做着相同的事——从当前集合中选一个作为当前节点的数,然后更新集合。于是我们可以将问题进行抽象简化,初始时我们希望找到[1,2,3]的全排列(母问题),第一步选取1为起始节点后,我们希望找到[2,3]的全排列(子问题),因为一旦找到了[2,3]的全排列,只要在这些排列前面补上1,我们就可以得到母问题中以1为起始节点的全部结果。这就形成了用递归法求解这个问题的基本思路。
再来看图,纵观整个搜索过程,无非是一种全遍历过程。每到一个节点,如果有可选项,则生成新节点,然后更新备选集合,再进一步求解子问题;而如果无可选项,则回退到上过一个节点的状态。当然,途中我们要判断是否已经产生了解。
在这里插入图片描述

伪代码

反思上面的过程,我们形成一个伪代码框架,用来求解回溯问题。
整个方法其实就3大核心部件:1.解的判断以及结束判断;2.筛选满足约束的备选项;3.产生子问题并求解,求解完后恢复求解之前的状态。

public void backtrack(...){
   
	if(是一个可行解) 将结果存入集合中;
	if(无备选项或无须进一步搜索) return;
	
	for(所有的备选项){
   
		if(该备选项不满足约束) continue;
		生成当前节点;
		更新集合;
		子问题backtrack();
		//状态回退
		删除节点;
		回退集合;
	}
}

例子

全排列

LeetCode 46. 全排列
我们回顾上一节给的3大核心部件。1.解的判断以及结束判断。当temp列表的大小和给定数组的长度一致时,说明形成了一个可行结果,需要存入ret中。同时两者数值大小一致,也说明再无备选项,搜索应该回溯到上一步。2.筛选满足约束的备选项。这里用到了一个布尔数组uesd,用来记录哪些数是已经被使用了的。显然我们应该选取那些未被使用过的数。布尔数组的技巧非常实用,应该记住。3.产生子问题并求解,求解完后恢复求解之前的状态。子问题求解前需要更新布尔数组used和暂存列表temp,子问题求解完以后,需要恢复used和temp之前的状态。

class Solution {
   
    public List<List<Integer>> permute(int[] nums) {
   
        List<List<Integer>> ret = new ArrayList<>();
        backtrack(ret,new ArrayList<Integer>(
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值