数组、链表、二叉树、动态规划、栈与队列是面试中常考的知识点,而在这几个知识点中,二叉树属于难度比较大的部分,因此在这里总结一下二叉树常考的操作,包括:
(1)二叉树的递归遍历;
(2)二叉树的非递归遍历;
(3)求二叉树叶子节点的个数;
(4)求二叉树的深度;
(5)二叉树的子结构;
(6)求二叉树中和为某个值的路径;
(7)求二叉树中的最大路径和;
(8)二叉树的镜像;
(9)判断某个序列是否是二叉搜索树的后序遍历序列;
(10)二叉搜索树和双向链表的相互转换;
(11)树中两个结点的最近祖先;
(12)二叉树搜索树的插入与删除;
(13)堆的插入与删除;
(14)判断一棵树是不是平衡二叉树;
(15)平衡二叉树的调整;
(5)二叉树的子结构
基本思想:判断二叉树A中是否存在某个子结构,该子结构和二叉树B相同。
比如树A和树B分别如下所示:
二叉树A:
二叉树B:
当要在二叉树 A A A中查找某个子结构 B B B时,需要两步:第一步,在树 A A A中找到与树 B B B的根节点相同的节点 B ′ B' B′;第二步,判断以 B ′ B' B′为根节点的子树是否与树 B B B相同。
对于第一步,在树 A A A中找到与树 B B B根节点相等的节点 B ′ B' B′,我们可以
(1) 先比较 A A A的某个节点 B ′ B' B′和树 B B B的根节点值是否相等;如果相等,判断以 B ′ B' B′为根节点的子树是否和树 B B B相同,并将结果记为 i s e x i s t isexist isexist;
(2)如果 i s e x i s t = F a l s e isexist=False isexist=False,递归地判断 B ′ B' B′的左子树是否和 B B B相同,更新 i s e x i s t isexist isexist的值;
(3)如果 i s e x i s t = F a l s e isexist=False isexist=False,递归地判断 B ′ B' B′的右子树是否和 B B B相同,再一次更新 i s e x i s t isexist isexist的值。
对于第二步,判断以 B ′ B' B′为根节点的子树和树 B B B是否相同,我们可以
如果 B B B为空返回 True;
如果 B ′ B' B′为空,返回False;
如果 B B B和 B ′ B' B′的值不相等,返回False;
比较 B B B的左子树 B ′ B' B′的左子树是否相同,结果记为left_equal;
比较 B B B的左子树 B ′ B' B′的右子树是否相同,结果记为right_equal;
返回left_equal && right_equal;
代码实现:
def FindNode(treenodeA,treenodeB):
isexist = False
if treenodeA != None and treenodeB != None:
if treenodeA.val == treenodeB.val:
isexist = IsEqual(treenodeA,treenodeB)
if isexist == False:
isexist = FindNode(treenodeA.left,treenodeB)
if isexist == False:
isexist = FindNode(treenodeA.right,treenodeB)
return isexist
def IsEqual(treenode1,treenode2):
if treenode2 == None:
return True
if treenode1 == None:
return False
if treenode1.val != treenode2.val:
return False
left_equal = IsEqual(treenode1.left,treenode2.left)
right_equal = IsEqual(treenode1.right,treenode2.right)
return left_equal and right_equal
(6)求二叉树中和为某个值的路径
基本思路:二叉树的路径指的是从根节点到叶节点所经过节点形成的序列。我们使用一个栈来保存从根节点到当前节点的路径,如果当前节点为叶节点,则栈中的节点构成一条路径。每当首次访问一个节点时,将该节点进栈,当该节点的所有子节点都访问过之后,将该节点从栈中弹出。
代码实现:
def PathSumEqualtoN_Recursion(all_path,rootnode,path,number):
if rootnode == None:
return
path.append(rootnode)
if rootnode.left == None and rootnode.right == None:
nodeval = [node.val for node in path]
if sum(nodeval) == number:
all_path.append(nodeval)
PathSumEqualtoN_Recursion(all_path,rootnode.left,path,number)
PathSumEqualtoN_Recursion(all_path,rootnode.right,path,number)
path.pop()
return
def PathSumEqualtoN_NoRecursion(all_path,rootnode,number):
if rootnode == None:
return
stack = []
stack.append(rootnode)
last_pop = rootnode
while len(stack) != 0:
curnode = stack[-1]
#print("stack={}".format([node.val for node in stack])
if curnode.left == None and curnode.right == None:
nodeval = [node.val for node in stack]
if sum(nodeval) == number:
all_path.append(nodeval)
if curnode.left != None and curnode.left != last_pop and curnode.right != last_pop:
stack.append(curnode.left)
elif curnode.right != None and curnode.right != last_pop and (curnode.left == None or curnode.left == last_pop):
stack.append(curnode.right)
else:
stack.pop()
last_pop = curnode
return
(7)求二叉树中的最大路径和
基本思路:这里的路径被定义为从二叉树中任意节点出发,到达另一个任意节点的所经过节点构成的序列。
先看几个简单的二叉树:
(1)第一棵树
1
/ \
2 3
(2)第二棵树
1
/ \
-2 3
(3)第三棵树
1
/ \
2 -3
(4)第四棵树
1
/ \
- 2 -3
接着我们看从节点1出发到其子树能得到的最大路径max_gain(1),
对于第一棵树,max_gain(1) = 1 + 3 = 4;
对于第二棵树,max_gain(1) = 1 + 3 = 4;
对于第三棵树,max_gain(1) = 1 + 2 = 3;
对于第四棵树,max_gain(1) = 1 + 0 = 1;
综上,我们可以得到从一个节点node出发到其子树所能得到的最大路径=从node的左节点出发到其子树所能得到的最大路径、从node的右节点出发到其子树所能得到的最大路径、0,这三者中的最大值加上节点node的值
即:
m a x _ g a i n ( n o d e ) = n o d e . v a l + M a x { m a x _ g a i n ( n o d e . l e f t ) , m a x _ g a i n ( n o d e . r i g h t ) , 0 } max\_gain(node) = node.val + Max\{max\_gain(node.left),max\_gain(node.right),0\} max_gain(node)=node.val+Max{max_gain(node.left),max_gain(node.right),0}
根据上面得出的结论,我们可以计算出每一个二叉树从根节点出发到其子树所能得到的最大路径max_gain(root),但是max_gain(root)不一定是该二叉树的最大路径,比如(1)第一棵树,它的最大路径是是从root的左子树穿过root到达其右子树的一条路径,路径和为max_gain(root.left) + root.val + max_gain(root.right),
所以,在求出从根节点出发到其子树所能得到的最大路径max_gain(root)之后,还需要计算可能的最大路径prob_gain = max_gain(root.left) + root.val + max_gain(root.right),如果prob_gain > max_gain(root),则最大路径和为prob_gain,否则为max_gain(root)。
代码实现:
max_path_sum = -float("inf")
def Max_gain(rootnode):
if rootnode == None:
return 0
left_gain = Max_gain(rootnode.left)
right_gain = Max_gain(rootnode.right)
root_gain = rootnode.val + max(left_gain,right_gain,0)
prob_gain = rootnode.val + left_gain + right_gain
global max_path_sum
max_path_sum = max(root_gain,prob_gain,max_path_sum)
return root_gain
参考:leetcode - [二叉树] -(124)二叉树中的最大路径和
(8)二叉树的镜像
基本思路:二叉树的镜像可以通过递归地交换每个节点的左右子树得到,如下图所示:
(1)原二叉树
-10
/ \
9 20
/ \
15 7
(2)镜像
-10
/ \
20 9
/ \
7 15
代码实现:
def Mirror_Tree(rootnode):
if rootnode == None:
return
Mirror_Tree(rootnode.left)
Mirror_Tree(rootnode.right)
temp = rootnode.left
rootnode.left = rootnode.right
rootnode.right = temp
return
def Mirror_Tree_Nochange(rootnode):
if rootnode == None:
return None
new_rootnode = TreeNode()
new_rootnode.val = rootnode.val
new_rootnode.right = Mirror_Tree_Nochange(rootnode.left)
new_rootnode.left = Mirror_Tree_Nochange(rootnode.right)
return new_rootnode
>>下一篇:通用算法 - [树结构] -二叉树的一些基本操作(三)