算法通过村第十八关-回溯|青铜笔记|什么叫回溯(初篇)

文章目录


前言


提示:我对你透露一个大密码,这是人类最古老的玩笑。往哪走,都是往前走。 --米兰·昆德拉

回溯是非常重要的算法思想之一,主要解决一些暴力枚举也搞不定的问题(这里埋个坑💣)例如组合、分割、子集、棋盘等等。从性能角度来看回溯算法的效率并不是很高,但是对于暴力也解决不了的问题,它往往很快可以出结果,效率低就可以理解了吧。接下来,就看看回溯的事情吧🤩

回溯可以视为递归的拓展,很多思想和解法都和递归密切相关,再很多材料中都将回溯与递归(一同解释)

,后续我们会遇到路径问题,这就是很典型的问题(相结合的形式),学习回溯时,可以更好的理解递归,相反也是一样的。

关于递归和回溯,这里也区分一下,假设一个场景是这样的:我想脱单怎么办

  • 递归策略:先与意中人制造偶遇,然后了解人家的情况,然后约人家吃饭,有好感以后尝试拉手,如果没拒绝,就尝试表白啦。
  • 回溯策略:先统计周围所有的单身女孩,然后一个一个表白,被拒绝就说"我喝醉了",然后就当什么事情也没发生,继续找下一个。

其实回溯的本质就是这么一个过程,好好想一下,看看有没有感觉~。

回溯最大的好处就是有非常明确的模板,所以这里的回溯都是有一个大框架,因此透彻理解回溯的框架是解决一切回溯问题的基础。所以这里做到是就是分析这个框架,让你彻底搞明白。

当然回溯并不是万能的,而且解决问题也是非常明确,例如:

  1. 组合
  2. 分割
  3. 子集
  4. 排序
  5. 棋盘

不过这些问题,在处理的时候具体措施也有不同,这里我们接着往下看。

回溯可以理解为递归的扩展,代码层面上又很像深度遍历的N叉树,因此只要知道递归,理解回溯并不难,难在很多人不理解为什么在递归之后会有个"撤销"的动作。这里通过图示给你解释清楚,这里假设一个场景,如果你这个时候谈新女朋友了,来你家之前,你时候会将你前任的东西藏起来?回溯就是这样,有些信息是前任的,需要处理掉,重新开始才行。

回溯最让人激动的是有非常清晰的解题模板,如下所示,大部分的回溯代码的框架都是这个样子,具体的我们后面在解释:

void backtracking(参数){
	if(终止条件){
		存放结果;
		return;
	}
	for(选择本层集中元素(画成树,就是树节点孩子的大小)){
		处理节点;
		backtracking();
		回溯,撤销处理结果;
	}
}

回溯是有明确的解题模板,下面我们就具体分析一下。

从N叉树说起

解释之前,还是看看N叉树的遍历吧,我们知道二叉树的前序过程:

java 复制代码
void treeDFS(TreeNode root) {
	if(root == null){
		return;
	}
	system.out.println(root.val);
	treeDFS(root.left);
	treeDFS(root.right);
}

class TreeNode{
	int val;
	TreeNode left;
	TreeNode right;
}

这里如果是三叉树,四叉树甚至N叉树该怎么处理呢?很显然这里不能用left和right来表示分支了,使用一个List比较好,也就是下面这个例子:

java 复制代码
class TreeNode{
	int val;
	List<TreeNode> nodes;
}

代码就需要改一下了:

public statis void treeDFS(TreeNode root) {
	// 递归的必要终止条件
	if(root == null){
		return;
	}
	// 处理节点
	system.out.println(root.val);
	// 通过循环,分别遍历N个字数
	for(int i = 1; i <= node.length; i++){
		treeDFS("第i个字节点");
	}
}

到这里,你有没有发现已经很相似了,说明两者之间确实存在某种必要的联系。其他的咱们暂时不管,我们是否已经记住刚才的那个框架了。(N叉树的遍历)

为什么有的问题暴力搜索也不行

试想一下,回溯主要解决暴力枚举处理不了的问题,为什么这么神奇,暴力解决不了?

参考题目介绍:77. 组合 - 力扣(LeetCode)

这里说先明确一点:如果n=4,k=2.那就是说从4个数种选择2个,问你最后能选出多少组数据。

这里类似高中的一个数学题:大致说一下过程,如果数字n=4,能用的数字就是{1,2,3,4}

  1. 先取出一个1,则有[1,2]、[1,3]、[1,4]三种可能。
  2. 再取出一个2,1已经取过了,不能再取了,则可以取[2,3]、[2,4]两种可能。
  3. 再取一个3,1和2都已经取过了,不能再取了,这里就剩下[3,4]一种可能了。
  4. 取4,以为1,2,3都已经取过了,所以就直接返回null。
  5. 最后的结果就是[1,2]、[1,3]、[1,4、[2,3]、[2,4]、[3,4]

这里我们思考下该问题要怎么实现,假如只有两个数,采用双层循环就可以了:

int n = 4;
for(int i = 1; i <= n; i++){
	for(int j = i + 1; j <= n; j++){
		system.out.println(i + " "+j);
	}
}

那如果n和k都变大,比如说n=200,k是3呢?也可以采用三层循环搞定:

int n = 200;
for(int i = 1; i <= n; i++){
	for(int j = i + 1; j <= n; j++){
		for(int l = j + 1; l <= n; l++){
			system.out.println(i + " "+j +" " + l);
		}
	}
}

如果这里的k是5呢?如果更大呢?这里就不好写了吧?甚至告诉你k就是一个未知的正整数k,你需要怎么写循环呀,是不是无解了,这里已经无能为力的吧,这里就是说暴力搜素不能解决。

这就是组合类型问题,除此以外子集、排序、切割、棋盘等方面都有类似的问题,我们可以好好学习一下。


总结

提示:回溯算法;初始回溯;什么叫回溯;回溯的套路;回溯的核心问题


如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

如有不理解的地方,欢迎你在评论区给我留言,我都会逐一回复 ~

也欢迎你 关注我 ,喜欢交朋友,喜欢一起探讨问题。

相关推荐
daqinzl3 分钟前
java获取机器ip、mac
java·mac·ip
激流丶19 分钟前
【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
java·大数据·kafka·topic
Themberfue22 分钟前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
让学习成为一种生活方式39 分钟前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
晨曦_子画1 小时前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
南宫生1 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
Heavydrink1 小时前
HTTP动词与状态码
java
ktkiko111 小时前
Java中的远程方法调用——RPC详解
java·开发语言·rpc
计算机-秋大田2 小时前
基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
java·论文阅读·spring boot·后端·vue
神里大人2 小时前
idea、pycharm等软件的文件名红色怎么变绿色
java·pycharm·intellij-idea