hello 大家好,最近没有什么事,总结了一些关于python的算法题,希望能够帮到同学们!
最近总是听到一些朋友在BAT的面试经历,各种算法题让人脑瓜疼,如果没有提前准备的话,那就基本上只能回家等通知了,而今天要跟大家说的这个二叉树DFS和BFS就是这些大型企业的必考题
好了,那么接下来进行我们今天的话题,首先给大家展示一个二叉树,如下图
二叉树定义:
- 每个节点最多有两个子节点(子树)的树结构
二叉树的遍历:
- 深度优先遍历(DFS):沿着树的深度遍历结点,尽可能深的搜索树的分支。如果当前的节点所在的边都被搜索过,就回溯到当前节点所在的那条边的起始节点。一直重复直到进行到发现源节点所有可达的节点为止
- 广度优先遍历(BFS):又称层次遍历,从树的根节点(root)开始,从上到下从从左到右遍历整个树的节点
下面通过代码实现二叉树深度优先遍历(其实深度优先有两种方式) 先讲第一种栈的方式
- 先定义Node类代表树的节点,Node节点有三个属性,节点存放的值value, 左子树lchild_tree, 右子树rchild_tree
class Node(object):
def __init__(self, value, lchild_tree=None, rchild_tree=None):
self.value= value
self.lchild_tree= lchild_tree
self.rchild_tree= rchild_tree
- 然后定义二叉树,与上面的二叉树图一样的结构,这里我们用变量的形式定义
tree = Node(
5,
Node(
3,
Node(
1,
rchild_tree=Node(2)
),
Node(4)
),
Node(
7,
lchild_tree=Node(6)
)
)
- 再定义一个深度优先遍历的方法
def deep_visit(root):
if not root: return
stack = [root]
while stack:
current = stack.pop()
print(current.value, end=",")
if current.rchild_tree:
stack.append(current.rchild_tree)
if current.lchild_tree:
stack.append(current.lchild_tree)
这里需要解释一下了,我们将树传入deep_visit方法后,
第一步:就是判断是否为空,这是非常必要的异步,如果为空直接返回
第二步:用列表代替栈将树放入stack,这里我们把stack当做栈,此时stack只有一个节点那就是根节点root
第三步:循环遍历stack,并且用pop方法删除最后一个元素,并返回删除的元素current,将其打印出来
第四步:判断当前节点current的右子树是否为空,不为空则将current的右子树追加到stack中
第五步:判断当前节点current的左子树是否为空,不为空则将右子树追加到stack中
这里我解释下第二步为什么是栈:在遍历的时候,我们需要一个容器来装树的元素,因为是深度优先,我们在遍历的时候所经过的节点都需要被展示出来,并且是必须从根节点开始查找,所以顺序是 root(根节点)、左子树、右子树的顺序,因为栈的进出顺序是先进后出也是后进先出,所以当我们遍历到一个节点时应该先将右子树放进stack,然后再放左子树,这样我们下一次遍历时在栈最后的就是左子树了,也就可以用pop方法直接获取到左子树了
最后就可以直接调用了
deep_visit(tree)
# 结果为:5,3,1,2,4,7,6,
深度优先遍历第二种方式(递归的方式)
- 节点Node的定义和数的变量就用上面的了,这里只要改造一下遍历deep_visit方法就可以,看代码
def deep_visit(root): if not root: return print(root.value, end=',') deep_visit(root.lchild_tree) deep_visit(root.rchild_tree)
第一步:还是先判断树是否为空,为空直接返回,不啰嗦
第二步:打印当前节点的value
第三步:以当前节点的左子树为参数调用当前方法deep_visit(root.lchild_tree)
第四步:以当前节点的右子树为参数调用当前方法deep_visit(root.rchild_tree)
这样就可以实现深度优先遍历了
广度优先遍历
- 节点Node的定义和数的变量都不变,这里只要改造一下遍历方法
def wide_visit(root):
if not root: return
queue = [root]
while queue:
current = queue.pop(0)
print(current.value, end=',')
if current.lchild_tree:
queue.append(current.lchild_tree)
if current.rchild_tree:
queue.append(current.rchild_tree)
第一步:判断树是否为空
第二步:这里就跟深度优先不同了,用的是队列,不过我们还是拿列表代替
第三部:遍历queue队列,用pop(0)方法删除第一个元素,并返回当前被删掉的元素current,将current的value打印
第四步:判断当前节点的左子树是否为空,不为空将左子树追加到queue中
第五步:判断当前节点的右子树是否为空,不为空将右子树追加到queue中
这里解释下为什么用队列了,因为是广度优先遍历是层级遍历,顺序为根节点、左节点、有节点,按层级从左到右遍历的
,由于队列的元素进出规律是先进先出,所以我们先将左节点放入队列再把右节点放入队列才符合广度优先遍历的特点
如果有地方没看明白欢迎下方留言,大家相互学习,谢谢大家!