回溯法基础入门一--c++实现:深度优先遍历c++实现/深度优先遍历对树的应用


前言

因为最近算法课将要学习回溯法,而学习回溯法必须要掌握深度优先遍历,刚好挤出一点时间复习(预习)一下深度优先遍历的相关内容。


1、深度优先遍历初识

1.1深度优先遍历的概念:

深度优先遍历(Depth-First-Search)简称DFS,是一种遍历或搜索图的算法,沿着树的深度,竖向的尽可能深的搜索树的分支。
过程如下:
1.假如以a为源节点,当与节点相邻的节点都已经被访问过,则回溯到发现a节点的那个节点,直到全部节点都被访问。
2.如果还存在未被访问的节点,则选择其中一个作为源节点并重复过程1;
简单来说:
深度优先遍历的思想就是从上至下,对每一个分支一直往下一层遍历直到这个分支结束,然后返回上一层,对上一层这个分支继续遍历,直到一整棵树完全遍历,因此符合栈后进先出的特点,所以完全可以用栈来实现深度优先遍历。

1.2深度优先遍历举例:

比如说如下二叉树:
在这里插入图片描述
其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次,后面我会详细列出如何使用栈来进行遍历操作。

2、为什么回溯法要用深度优先遍历?

那么为什么回溯法要用深度优先遍历而不用广度优先遍历呢?
首先我们来看一下回溯法的概念:

2.1回溯法的概念

回溯法是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。(此处搬运百度百科)

2.2为什么要用深度优先遍历?

回到刚开始的问题,为什么回溯法要用深度优先遍历而不用广度优先遍历?
由回溯法的概念可以知道,回溯法就是在解决问题的过程中舍去那些达不到目标的解,而深度优先遍历恰好符合这种解决问题的思路,想想,如果使用广度优先遍历,那么就是对于树的按行遍历,那么如果这样遍历,结果就是遍历出整个过程,而无法进行对于明明知道达不到目标的舍去,如果这样的话,反而不如用穷举法(回溯法就是穷举法的优化)。

3.使用深度优先遍历的过程:

3.1使用的相关数据结构:

因为深度优先遍历是先进后出的,所以可以用栈来实现,可以自定义栈,也可以用封装好的stl(stack)对于初学的我们来说还是先要先学会造轮子才能学会用轮子,所以建议先自己写栈的相关操作,等熟练掌握之后再用stack。

3.2使用深度优先遍历的详细过程

刚刚列出的那个树:

在这里插入图片描述

使用深度优先遍历的步骤如下:
1、把起始点放入栈中;
2、重复下述3步骤,直到栈为空为止:
(1)从栈中访问栈顶的点;
(2)找出与此点邻接的且尚未遍历的点,进行标记,然后全部放入栈中;
(3)如果此点没有尚未遍历的邻接点,则将此点从栈中弹出。
由上面的步骤可知,如果要访问上图树,步骤如下:
一:将节点A放入栈中标记为已遍历
在这里插入图片描述
二:访问栈顶的节点A,找出与此点邻接的点,有B和C,可以选择其中的任意一个(顺序由我们确定,这里我们选择先左后右),我们选择B,标记为已遍历,放入栈中。
在这里插入图片描述
三:同样的从栈顶取出节点B,找出与此点邻接的点,有A,D和E,A已经遍历过了,所以排除,这里可以从D和E中选择其中的任意一个(顺序由我们确定,这里我们选择先左后右),我们选择D,标记为已遍历,放入栈中。
在这里插入图片描述
四:从栈顶取出节点D,找出与此点邻接的点,有B,但是B已经遍历过了,所以,没有尚未遍历的邻接点,则将此点从栈中弹出。
在这里插入图片描述
五:从栈顶取出节点B,找出与此点邻接的点,有A,D和E,A和D已经遍历过了,所以排除,这里我们只能选择E,标记为已遍历,放入栈中。
在这里插入图片描述
l六:从栈顶取出节点E,找出与此点邻接的点,有B,但是B已经遍历过了,所以,没有尚未遍历的邻接点,则将此点从栈中弹出。
在这里插入图片描述
七:从栈顶取出节点B,找出与此点邻接的点,有A,D和E,但是都已经遍历过了,所以,没有尚未遍历的邻接点,则将此点从栈中弹出。
在这里插入图片描述

八:从栈顶取出节点A,找出与此点邻接的点,有B和C,B已经遍历过了,所以排除,这里只能选择C,标记为已遍历,放入栈中。
在这里插入图片描述
九:从栈顶取出节点C,找出与此点邻接的点,有A,F,G和H,A已经遍历过了,所以排除,这里从F,G,H中选择,我们选择F,标记为已遍历,放入栈中。
在这里插入图片描述
十:从栈顶取出节点F,找出与此点邻接的点,只有C,但是C已经遍历过了,所以,没有尚未遍历的邻接点,则将此点从栈中弹出。
在这里插入图片描述
十一:从栈顶取出节点C,找出与此点邻接的点,有A,F,G和H,A和F已经遍历过了,所以排除,这里从G,H中选择,我们选择G,标记为已遍历,放入栈中。
在这里插入图片描述
十二:从栈顶取出节点G,找出与此点邻接的点,只有C,但是C已经遍历过了,所以,没有尚未遍历的邻接点,则将此点从栈中弹出。
在这里插入图片描述
十三:从栈顶取出节点C,找出与此点邻接的点,有A,F,G和H,A,F和G已经遍历过了,所以排除,这里只能选择H,标记为已遍历,放入栈中。
在这里插入图片描述

十四:从栈顶取出节点H,找出与此点邻接的点,只有C,但是C已经遍历过了,所以,没有尚未遍历的邻接点,则将此点从栈中弹出。
在这里插入图片描述
十五:相信看到这里都已经明白了,最后再进行两次判定,栈最后为空,而树也遍历完成。

4、回溯法中深度优先遍历的应用:

4.1解空间:

首先了解解空间的概念:
一个复杂问题的解决方案可以分成多个步骤,而每个步骤都有很多种选择,这些每个步骤的组合构成了这个问题的解空间解空间树。(为什么叫解空间树:因为解空间一般是用树的形式来组织,这也是这里学习深度优先遍历的目的)

而在解决具体问题的时候,我们进行解空间树的遍历,在遍历过程中,满足问题描述的约束条件的解空间叫可行解,同样我们也可从所有可行解中求出最优解
我们遇到的问题往往都是求所有的可行解或最优解。
而解空间树也可以分为子集树排列树
子集树:顾名思义就是从n个元素的集合中求满足条件的幂集。
排列树:当所给的问题是确定的n个元素满足某种条件时,相应的解空间树就是排列树。

在这里插入图片描述

4.2、剪枝函数

在求解空间时,如何优化?
约束函数和限界函数就是为了避免无效的搜索。
约束函数:在扩展结点处剪除不满足约束条件的路径。
限界函数:剪去得不到问题解或者最优解的路径。
这两类函数都称为剪枝函数。

4.3、回溯法解题的一般步骤:

(1)针对问题确定解空间树,问题的解空间树至少包含问题的一个可行解或者最优解
(2)确定结点的扩展搜索规则
(3)以深度优先方式遍历解空间树,在遍历过程中用剪枝函数来避免无效的遍历,深度优先可以用递归也可以用非递归实现。

总结

本次复习了一下基本概念,下面看一下具体题目。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值