第二篇:DFS、BFS、二叉树的前、中、后序遍历及层序遍历
深度优先搜索:
英文是Depth First Search,简称为DFS。主要思路就是从图/树中一个顶点开始,沿着一条路一直走到底,然后从这条路尽头的结点回退到上一个结点,再从另一条路走到底。。。。不断重复此过程,直到所有的结点都遍历完成。DFS的特点就是一条道走到黑,不撞南墙不回头。
DFS遍历下面的树,步骤是:

1、从根结点1开始遍历,它的子结点有2、3、4,先遍历结点2,再遍历结点2的子结点5,再遍历结点5的子结点9。如下图

2、走到底后要回退。从结点9回退到结点5,看下结点5是否有除结点9外的子结点。没有,则继续回退到结点2,再继续回退到结点1。结点1有除结点2外的子结点3、4,从结点3开始深度遍历。如下图

3、同理,从结点10往上回退到结点6,结点6没有除10外的子结点,再回退到结点3,结点3有除结点6外的子结点7,遍历7。如下图

4、从结点7往上回退到结点3,结点3没有除结点6、7外的子结点了,再往上回退到结点1。结点1还有除结点2、3外的子结点4,所以此时沿着结点4、8遍历。
综上,完整的遍历顺序如下

二叉树的DFS就是前序遍历。获取二叉树的所有路径就可以用DFS。定义一个二级函数,有两个入参,没有出参,第一个入参是一个结点,第二个入参是从根结点到此结点的路径,函数的逻辑是根据给定结点的情况,判断给定路径是否是完整路径,如果是完整路径,就把路径放到结果集中。否则,递归调用此函数(如果给定结点有左子结点,则递归调用的函数时第一个入参是给定结点的左子结点,第二个入参是给定路径+"->"+左子结点的值,给定结点有右子结点的话,同理)。我们只需在一级函数中调用此函数即可,调用时第一个入参是根结点,第二个入参是根结点的值。所谓二级函数是指会被递归调用且会被其他函数调用的函数,一级函数是指调用二级函数的函数,是自己创造的概念。
DFS有递归和非递归两种实现方式。以二叉树的DFS讲解。
递归方式:
递归实现比较简单。从根结点开始,依次遍历根结点及其左右子树。遍历子树时,也是先遍历子树的根结点,再遍历其左右子树。递归方式,如果层级过深,有可能会栈溢出。可以考虑用尾递归来优化。在面试时使用尾递归也是加分项。
非递归方式:
非递归方式是有固定套路的,即建一个栈,先把根结点push到栈中。然后开启循环,循环中的操作是pop栈得到一个结点,访问其数据,然后把这个结点的右结点和左结点依次push到栈中。循环的退出条件是栈为空。使用非递归方式不用担心栈溢出。
广度优先搜索:
英文是Breadth First Search,简称为BFS,适用于层序遍历和最短路径问题。
广度优先是有固定套路的,即建一个队列(java中,Queue接口表示队列,常用实现类是ArrayDeque。ArrayDeque is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue。golang中,用list包的List表示队列),先把根结点放到队列中。然后开启循环(循环的退出条件是队列为空),循环中的操作是先算出队列的长度,假设为n,然后执行n次【弹出首结点,并把这个首结点的左子结点和右子结点加到队列中】操作。
二叉树的前、中、后序遍历及层序遍历
前序遍历,英文是preorderTraversal,定义是先访问根结点,然后再访问左子树,最后再访问右子树。在访问子树时,也是先访问其根结点,然后再访问其左子树,最后再访问其右子树。是典型的递归调用。根+左+右。
中序遍历定义是先访问左子树,然后再访问根结点,最后再访问右子树。在访问子树时,也是先访问其左子树,然后再访问其根结点,最后再访问其右子树。也是典型的递归调用。左+根+右。中序遍历二叉搜索树(BST),遍历出来的结果是升序序列。
后序遍历,英文是postorderTraversal,定义是先访问左子树,然后再访问右子树,最后再访问根结点。在访问子树时,也是先访问其左子树,然后再访问其根结点,最后再访问其右子树。也是典型的递归调用。左+右+根。后续遍历可以用来判断一个二叉树是否是平衡二叉树。定义一个计算二叉树高度的函数,如果树是平衡的,则返回实际高度,否则返回-1。一个树是平衡树的条件是其左右子树的高度都为-1且高度差不超过1(先判断左子树的高度是否为-1,如果为-1,则直接返回-1。否则继续判断右子树的高度是否为-1,如果为-1,则返回-1。否则继续判断左右子树高度差是否大于1,如果大于1,则返回-1,否则返回左右子树高度的较大值加1)。最终判断给定二叉树根结点的高度是否为-1,是则说明此二叉树不是平衡二叉树,否则是平衡树。
前序、中序、后序,是站在根结点的角度来说的,前序就是根结点在前面遍历,中序就是根结点在中间遍历,后序就是根结点在后面遍历。就好像I/O是站在内存的角度说的一样。
层序遍历和其他遍历不一样,是一层层的遍历,每一层是先左后右。不能用递归,需要用广度优先搜索。
浙公网安备 33010602011771号