二叉树的遍历一般以根为起点,存在两种基本遍历方式:
深度优先遍历,顺着一条路径尽可能弹缩,必要时回溯。检查到二叉树的叶节点即符合这种情况,由于无法继续探索,只能向上回溯。
宽度优先遍历,实际上将二叉树进行分层处理,检查完每一个层次后继续向下检查,如果检查到叶节点,则忽略该叶节点之后的层次。
那么按照深度优先遍历规则,下面的基本图则描述了遍历过程中的三个基本单位:根节点、左子树、右子树。对于任何一个子树,也存在相同的访问策略,因为二叉树是递归的。
那么控制这三个单位的访问顺序,即构成了三种不同的遍历结果
前序遍历:即DLR顺序,先访问根节点,再访问左子树,最后访问右子树
后序遍历:即LRD顺序,先访问左子树,再访问右子树,最后访问根节点
中序遍历:即LDR顺序,先访问左子树,再访问根节点,最后访问右子树
给出下面这个一个例图,分别说明这三种遍历的结果:
对于前序遍历,结果是ABDHEICFJKG
对于后序遍历,结果是HDIEBJKFGCA
对于中序遍历,结果是DHBEIAJFKCG
宽度优先遍历的结果则是:ABCDEFGHIJK。
那构建一个二叉树,分别以代码实现这样的遍历过程。
构建二叉树的策略有很多种,常用的一种是传入列表,递归创建,需要注意的是,只有完全二叉树,满足列表创建的需求。
PS:完全二叉树是指对于一个高度为h的二叉树,如果其第0层到第h-1层的节点都是满的,如果最后一层不满但所有节点都是靠左连续排列,空位都在右边,这样的就是一个完全二叉树。
那么上述的图形,需要扩充一下,形成一个完全二叉树满足构建需求,扩充后的结果如下:
那么可以使用完全二叉树的性质, 若一棵二叉树共有n个节点,且按照列表序排序输入,那么:
1)序号为0的是根节点
2 对于序号i > 0,其父节点的序号是 (i - 1) / 2
3) 对于序号i,如果 2 * i + 1 < n,则其左节点序号为 2 * i + 1,否则无左节点;如果 2 * i + 2 < n,则其右节点序号为 2 * i + 2,否则无右节点
那么对于上述完全二叉树,可以用如下列表描述并构建:
[A, B, C, D, E, F, G, #, H, #, I, J, K]
按照一定层次描述即是
A || B, C || D, E, F, G || #, H, #, I, J, K
那么现在开始构建它:
1 class TreeNode(object): 2 def __init__(self, val=None, l=None, r=None): 3 self.val = val 4 self.left = l 5 self.right = r 6 7 8 class BinaryTree(object): 9 10 def __init__(self, tree_list): 11 self.bt = self.build_tree(tree_list, 0) 12 13 def build_tree(self, tree_list, i): 14 if i < len(tree_list): 15 if tree_list[i] == '#': 16 return None 17 t = TreeNode(tree_list[i]) 18 t.left = self.build_tree(tree_list, 2 * i + 1) 19 t.right = self.build_tree(tree_list, 2 * i + 2) 20 return t 21 return None 22 23 if __name__ == '__main__': 24 tree_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', '#', 'H', '#', 'I', 'J', 'K'] 25 bt_model = BinaryTree(tree_list)
二叉树构建好之后,开始对它进行分析并遍历。
首先来看一下前序遍历,它的顺序是根,左子树,右子树,那么使用递归的写法是:
1 class BinaryTree(object): 2 3 @staticmethod 4 def pre_order_recursion(t): 5 if t: 6 print t.val, 7 BinaryTree.pre_order_recursion(t.left) 8 BinaryTree.pre_order_recursion(t.right)
它的思路和前序遍历完全一致,如果根节点非空则访问它,并且继续按照这个规则分别访问它的左右子树
如果不使用递归写法,则需要手动维护一个栈,当遍历一个二叉树的时候,从根节点开始,向左下探索,但凡探索到了则同时压栈右子树;如果左下分支已经探索完毕,那么弹出栈顶右子树,继续上述操作(向左下探索并压栈右子树),直至栈内没有任何信息且树已经全部遍历完为止。
一个思路分析简图如下:
一定要谨记前序遍历的访问顺序,根,左子树,右子树:
手动维护一个栈,是空的
1)对于上图,根节点不为空,则访问根节点,将右子树入栈
2)一路向左,,如果节点不为空右子树入栈,则B的右子树,C, E的右子树也同时入栈,由于E的左子树为空,则无法继续左下,中止这个流程
3)弹出栈顶对象,为E的右子树,但该子树为空,继续弹栈顶对象为C的右子树,但仍为空,一直到弹出B的右子树
4)D不为空,则访问D为根节点的子树,将D的右子树入栈,由于左子树为空,后续弹出F为根节点的子树,F无法左下,则弹出F的右子树,此时栈内只剩下A的右子树且该子树不为空
5)A的右子树重复 2-4 流程
代码如下,并与递归的程序比较结果:
1 class TreeNode(object): 2 def __init__(self, val=None, l=None, r=None): 3 self.val = val 4 self.left = l 5 self.right = r 6 7 8 class BinaryTree(object): 9 10 def __init__(self, tree_list): 11 self.bt = self.build_tree(tree_list, 0) 12 13 def build_tree(self, tree_list, i): 14 if i < len(tree_list): 15 if tree_list[i] == '#': 16 return None 17 t = TreeNode(tree_list[i]) 18 t.left = self.build_tree(tree_list, 2 * i + 1) 19 t.right = self.build_tree(tree_list, 2 * i + 2) 20 return t 21 return None 22 23 @staticmethod 24 def pre_order_recursion(t): 25 if t: 26 print t.val, 27 BinaryTree.pre_order_recursion(t.left) 28 BinaryTree.pre_order_recursion(t.right) 29 30 @staticmethod 31 def pre_order(t): 32 sstack = [] 33 while t or sstack: 34 while t: #左下分支探索 35 sstack.append(t.right) #右分支入栈 36 print t.val, # 处理根数据 37 t = t.left #结合循环,沿着左下分支探索 38 t = sstack.pop() #无法左下探索,弹出栈顶元素 39 40 41 if __name__ == '__main__': 42 tree_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', '#', 'H', '#', 'I', 'J', 'K'] 43 bt = BinaryTree(tree_list) 44 bt.pre_order_recursion(bt.bt) 45 print 46 bt.pre_order(bt.bt)
1 /Users/kielchan/PycharmProjects/untitled1/venv/bin/python /Users/kielchan/PycharmProjects/untitled1/venv/binarytree.py 2 A B D H E I C F J K G 3 A B D H E I C F J K G 4 5 Process finished with exit code 0
与文中一开始的前序遍历结果一致。
既然如此,那么之后把中序遍历和后序遍历的递归和非递归也全写一下好了。
1 #-*- coding:utf-8 -*- 2 3 4 class TreeNode(object): 5 def __init__(self, val=None, l=None, r=None): 6 self.val = val 7 self.left = l 8 self.right = r 9 10 11 class BinaryTree(object): 12 13 def __init__(self, tree_list): 14 self.bt = self.build_tree(tree_list, 0) 15 16 def build_tree(self, tree_list, i): 17 if i < len(tree_list): 18 if tree_list[i] == '#': 19 return None 20 t = TreeNode(tree_list[i]) 21 t.left = self.build_tree(tree_list, 2 * i + 1) 22 t.right = self.build_tree(tree_list, 2 * i + 2) 23 return t 24 return None 25 26 @staticmethod 27 def pre_order_recursion(t): 28 if t: 29 print t.val, 30 BinaryTree.pre_order_recursion(t.left) 31 BinaryTree.pre_order_recursion(t.right) 32 33 @staticmethod 34 def in_order_recursion(t): 35 if t: 36 BinaryTree.in_order_recursion(t.left) 37 print t.val, 38 BinaryTree.in_order_recursion(t.right) 39 40 @staticmethod 41 def post_order_recursion(t): 42 if t: 43 BinaryTree.post_order_recursion(t.left) 44 BinaryTree.post_order_recursion(t.right) 45 print t.val, 46 47 @staticmethod 48 def pre_order(t): 49 sstack = [] 50 while t or sstack: 51 while t: 52 sstack.append(t.right) 53 print t.val, 54 t = t.left 55 t = sstack.pop() 56 57 @staticmethod 58 def in_order(t): 59 sstack = [] 60 while t or sstack: 61 while t: 62 sstack.append(t)#将所有左分支节点入栈 63 t = t.left 64 if sstack: 65 top = sstack.pop()#弹出左分支节点 66 print top.val,#因为是中序遍历,首先处理当前根节点 67 t = top.right #因为当前根节点的左子树为空,则处理当前根节点的右子树,符合(L)DR的访问顺序,如果R为空,则向上层回溯 68 69 @staticmethod 70 def post_order(t): 71 sstack = [] 72 while t or sstack: 73 while t: 74 sstack.append(t) 75 t = t.left if t.left else t.right #按照LRD规则,将L或者R放进堆栈中, L优先级大于R优先级 76 top = sstack.pop()#弹出栈顶对象并处理它 77 print top.val, 78 if sstack and top is sstack[-1].left: 79 t = sstack[-1].right #如果弹出对象是当前栈顶对象的左子树则处理当前对象的右子树 80 else: 81 t = None #否则弹出对象是右子树或者已经访问过了,强行退栈向上回溯 82 83 def findTarget(self, root, k): 84 """ 85 :type root: TreeNode 86 :type k: int 87 :rtype: bool 88 """ 89 if root is None: 90 return False 91 if (k - root.val) in self.rv: 92 return True 93 self.rv.add(root.val) 94 return self.findTarget(root.left, k) or self.findTarget(root.right, k) 95 96 97 if __name__ == '__main__': 98 tree_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', '#', 'H', '#', 'I', 'J', 'K'] 99 bt = BinaryTree(tree_list) 100 print '前序遍历递归算法' 101 bt.pre_order_recursion(bt.bt) 102 print 103 print '前序遍历非递归算法' 104 bt.pre_order(bt.bt) 105 print 106 print '中序遍历递归算法' 107 bt.in_order_recursion(bt.bt) 108 print 109 print '中序遍历非递归算法' 110 bt.in_order(bt.bt) 111 print 112 print '后序遍历递归算法' 113 bt.post_order_recursion(bt.bt) 114 print 115 print '后序遍历非递归算法' 116 bt.post_order(bt.bt)
运行结果:
1 /Users/kielchan/PycharmProjects/untitled1/venv/bin/python /Users/kielchan/PycharmProjects/untitled1/venv/binarytree.py 2 前序遍历递归算法 3 A B D H E I C F J K G 4 前序遍历非递归算法 5 A B D H E I C F J K G 6 中序遍历递归算法 7 D H B E I A J F K C G 8 中序遍历非递归算法 9 D H B E I A J F K C G 10 后序遍历递归算法 11 H D I E B J K F G C A 12 后序遍历非递归算法 13 H D I E B J K F G C A 14 15 Process finished with exit code 0
最后一个是宽度优先遍历算法,它的机制很简单,只需维护一个栈,处理当前层次的节点对象,处理一个则弹出一个,并将他的孩子入栈,代码如下:
1 #-*- coding:utf-8 -*- 2 3 4 class TreeNode(object): 5 def __init__(self, val=None, l=None, r=None): 6 self.val = val 7 self.left = l 8 self.right = r 9 10 11 class BinaryTree(object): 12 13 def __init__(self, tree_list): 14 self.bt = self.build_tree(tree_list, 0) 15 16 def build_tree(self, tree_list, i): 17 if i < len(tree_list): 18 if tree_list[i] == '#': 19 return None 20 t = TreeNode(tree_list[i]) 21 t.left = self.build_tree(tree_list, 2 * i + 1) 22 t.right = self.build_tree(tree_list, 2 * i + 2) 23 return t 24 return None 25 26 @staticmethod 27 def BFS(t): 28 sstack = [] 29 sstack.insert(0, t) 30 while sstack: 31 top = sstack.pop() 32 if top is None: 33 continue 34 print top.val, 35 sstack.insert(0, top.left) 36 sstack.insert(0, top.right) 37 38 39 if __name__ == '__main__': 40 tree_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', '#', 'H', '#', 'I', 'J', 'K'] 41 bt = BinaryTree(tree_list) 42 print '宽度优先遍历算法' 43 bt.BFS(bt.bt)
打印结果如下:
1 /Users/kielchan/PycharmProjects/untitled1/venv/bin/python /Users/kielchan/PycharmProjects/untitled1/venv/binarytree.py 2 宽度优先遍历算法 3 A B C D E F G H I J K 4 5 Process finished with exit code 0