树的遍历
一、树的结构
- 实现树的结构
'''
值:val
两个指针:left,right
二叉树中叶子结点的左右节点都为None
'''
'''
值:val
两个指针:left,right
二叉树中叶子结点的左右节点都为None
'''
class TreeNode(object): # x为值
def __init__(self,x):
self.val = x
self.left = None
self.right = None
# 取出数据在大类上有两种方式:深度优先与广度优先
# 深度优先:先找到最深,纵着来
# 广度优先:从第一行开始找第二行,第三行,横着来
if __name__ == '__main__':
t1 = TreeNode(1)
t2 = TreeNode(2)
t3 = TreeNode(3)
t4 = TreeNode(4)
t5 = TreeNode(5)
t6 = TreeNode(6)
t7 = TreeNode(7)
t8 = TreeNode(8)
t1.left = t2
t1.right = t3
t2.left = t4
t2.right = t5
t3.left = t6
t3.right = t7
t6.right = t8
二、深度优先与广度优先遍历
实现完数据结构后,我们需要查找,查找的方式有两大类:深度优先与广度优先。
深度优先又分为前序遍历、中序遍历和后序遍历
先序遍历:根节点->左子树->右子树
中序遍历:左子树->根节点->右子树
后续遍历:左子树->右子树->根节点
例如:
先序遍历:ABDHIECFGJK
中序遍历:HIDBEAFCJKG
后序遍历:HIDEBFKJGCA
1、先序中序后序遍历的递归实现
- 先序遍历
# 先序遍历
def preOrderRecursive(root):
if root == None:
return None
print(root.val)
# 递归
preOrderRecursive(root.left)
preOrderRecursive(root.right)
- 中序遍历
# 中序遍历
def midOrderRecursive(root):
if root == None:
return None
midOrderRecursive(root.left)
print(root.val)
midOrderRecursive(root.right)
- 后序遍历
# 后序遍历
def latOrderRecursive(root):
if root == None:
return None
latOrderRecursive(root.left)
latOrderRecursive(root.right)
print(root.val)
看一下效果:
# 先序遍历
def preOrderRecursive(root):
if root == None:
return None
print(root.val)
# 递归
preOrderRecursive(root.left)
preOrderRecursive(root.right)
# 中序遍历
def midOrderRecursive(root):
if root == None:
return None
midOrderRecursive(root.left)
print(root.val)
midOrderRecursive(root.right)
# 后序遍历
def latOrderRecursive(root):
if root == None:
return None
latOrderRecursive(root.left)
latOrderRecursive(root.right)
print(root.val)
'''
值:val
两个指针:left,right
二叉树中叶子结点的左右节点都为None
'''
class TreeNode(object): # x为值
def __init__(self,x):
self.val = x
self.left = None
self.right = None
# 取出数据在大类上有两种方式:深度优先与广度优先
# 深度优先:先找到最深,纵着来
# 广度优先:从第一行开始找第二行,第三行,横着来
if __name__ == '__main__':
t1 = TreeNode(1)
t2 = TreeNode(2)
t3 = TreeNode(3)
t4 = TreeNode(4)
t5 = TreeNode(5)
t6 = TreeNode(6)
t7 = TreeNode(7)
t8 = TreeNode(8)
t1.left = t2
t1.right = t3
t2.left = t4
t2.right = t5
t3.left = t6
t3.right = t7
t6.right = t8
print('先序遍历')
preOrderRecursive(t1)
print('中序遍历')
midOrderRecursive(t1)
print('后序遍历')
latOrderRecursive(t1)
先序遍历 12453687
中序遍历 42516837
后序遍历 45286731
2、非递归方式实现遍历
其实递归和循环(利用栈进行循环)是互相转换的,递归是一定要事先找到一个出口的,f否则一直递归下去什么时候是个头,比如我们的程序if root == None: return None
,所以用递归第一件事就是要找到一个出口,看到这里一定要将上面递归的过程从脑海里过一遍!
递归与栈其实是可以互相转换。回想一下上面的递归过程,以前序为例:preOrderRecursive(root),然后root不为空,print(root.val)继续向下走:
- 此时碰上了自己递归自己,
preOrderRecursive(root.left=2)
,此时重复的又进入了函数内部,判断是否为空,不为空,打印2,继续往下走;
– 此时又碰上了自己递归自己,preOrderRecursive(root.left=4)
,此时又重复的进入了函数内部,判断是否为空,不为空,打印4,继续往下走;
— 此时又碰上了自己d递归自己,preOrderRecursive(root.left=none)
,此时又重复的进入了函数内部,为空,返回none,这个preOrderRecursive(root.left=none)
结束;
此时并代表整个函数结束,而是接着preOrderRecursive(root.left=4)
,这个函数第一个递归已经结束,进入第二个递归,preOrderRecursive(root.right=none)
,返回none;preOrderRecursive(root.left=4)
结束,接着继续preOrderRecursive(root.left=2)
,进入preOrderRecursive(root.right=5)
,非空,打印5,进入5的左节点,空,返回,进入5的右节点,空返回,preOrderRecursive(root.left=2)
结束;进入根节点的右边子树…
这个过程很像是一个栈结构,首先给一个栈stack=[],stack.append(1) stack = [1];stack,append(2), stack = [1,2];stack,append(4), stack = [1,2,4];然后碰到none了,开始蹦出,stack,pop(4), stack = [1,2];stack,append(5), stack = [1,2,5];碰到None,开始蹦出,stack,pop(5), stack = [1,2];stack,pop(2), stack = [1];stack,append(3), stack = [1,3]…
注意以上栈中的数字并不是节点的val,而是递归的函数中的参数,记录的是递归的函数,pop就是这个函数结束了,append就是开始进入这个函数递归。很明显1在栈中的时间最长,什么时候1pop了,证明这个函数已经完全结束!
- 先序遍历
def preOrder(root):
if root == None:
return None
# 列表表示栈
stack = []
tmpNode = root # tmNode可以理解成指针,也可以理解成当前循环到那个节点了
'''
循环结束的条件并不仅仅是tmpNode为空,比如当你一直tmpNode = left的时候迟早为空
但是节点还没有遍历完呢!这个时候应该怎么做呢?想象一下递归,当递归到叶子结点时,
这个左函数结束啦,是不是该右函数了,因此此时该pop了。所以循环到栈为空为止
'''
while tmpNode or stack:
# 先找所有的左子树加入栈,循环所有的子节点,此循环主要是更新所有的左子节点
while tmpNode:
# 先序打印跟
print(tmpNode.val)
stack.append(tmpNode)
tmpNode = tmpNode.left # 结束前中国left为空,并没有进入栈
# 左子树完了,碰到了None,开始pop
node = stack.pop()
# 更新节点
tmpNode = node.right
- 中序遍历
中序遍历与前序遍历的循环结构一样,只不过打印的位置变为pop那了
def midOrder(root):
if root == None:
return None
# 列表表示栈
stack = []
tmpNode = root # tmNode可以理解成指针,也可以理解成当前循环到那个节点了
'''
循环结束的条件并不仅仅是tmpNode为空,比如当你一直tmpNode = left的时候迟早为空
但是节点还没有遍历完呢!这个时候应该怎么做呢?想象一下递归,当递归到叶子结点时,
这个左函数结束啦,是不是该右函数了,因此此时该pop了。所以循环到栈为空为止
'''
while tmpNode or stack:
# 先找所有的左子树加入栈,循环所有的子节点,此循环主要是更新所有的左子节点
while tmpNode:
# 先序打印跟
# print(tmpNode.val)
stack.append(tmpNode)
tmpNode = tmpNode.left # 结束前中国left为空,并没有进入栈
# 左子树完了,碰到了None,开始pop
node = stack.pop()
# 后序打印
print(node.val)
# 更新节点
tmpNode = node.right
def latOrder(root):
if root == None:
return None
# 列表表示栈
stack = []
tmpNode = root # tmNode可以理解成指针,也可以理解成当前循环到那个节点了
while tmpNode or stack:
# 先找所有的左子树加入栈,循环所有的子节点,此循环主要是更新所有的左子节点
while tmpNode:
# 先序打印跟
# print(tmpNode.val)
stack.append(tmpNode)
tmpNode = tmpNode.left # 结束前中国left为空,并没有进入栈
# node = stack.pop() 因为先打印右子树后打印根节点,所以先不能pop
node = stack[-1]
# 后序打印
# 更新节点
tmpNode = node.right
# 因为是先打印左节点后再打印右节点,现在左节点为空,那来判断右节点是不是为空
if node.right == None:
print(node.val)
node = stack.pop()
while stack and node == stack[-1].right:
node = stack.pop()
print(node.val)