面试题6: 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
思路:找出中间节点后,基于递归思想实现。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回构造的TreeNode根节点
def reConstructBinaryTree(self, pre, tin):
# 判断前序遍历和中序遍历序列是否为空,为空就返回None
if not pre or not tin:
return None
# 因为pre为先序遍历,pre中的第一个点为首节点
root = TreeNode(pre[0])
# 找到首节点在中序遍历中的索引位置
pivot = tin.index(pre[0])
# 对左右分别递归 如: [1|2,3,4||,5,6,7],[3,2,4|1|6,7]
root.left = self.reConstructBinaryTree(pre[1:pivot+1],tin[0:pivot])
root.right = self.reConstructBinaryTree(pre[pivot+1:],tin[pivot+1:])
return root
面试题18:树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
思路:对于两棵二叉树来说,要判断B是不是A的子结构,首先第一步在树A中查找与B根节点的值一样的节点。 通常对于查找树中某一个节点,我们都是采用递归的方法来遍历整棵树。 第二步就是判断树A中以R为根节点的子树是不是和树B具有相同的结构。 这里同样利用到了递归的方法,如果节点R的值和树的根节点不相同,则以R为根节点的子树和树B肯定不具有相同的节点; 如果它们值是相同的,则递归的判断各自的左右节点的值是不是相同。 递归的终止条件是我们达到了树A或者树B的叶节点。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def HasSubtree(self, pRoot1, pRoot2):
# 依题意,如果pRoot2为空,返回False
if not pRoot1 or not pRoot2:
return False
result = False
# 三个遍历方向
# 如果说当前结点存放的元素相同,则直接往后遍历
if pRoot1.val == pRoot2.val:
result = self.IsSubtree(pRoot1, pRoot2)
# 通过上面if语句的讨论,result还是为False,pRoot1的左子树继续讨论
if not result:
result = self.IsSubtree(pRoot1.left, pRoot2)
# 右子树
if not result:
result = self.IsSubtree(pRoot1.right, pRoot2)
return result
def IsSubtree(self, p1, p2):
# p2如果遍历完,则证明pRoot2是pRoot1的子结构,返回True
if not p2:
return True
# 如果说p1遍历完,则证明p2还没有匹配结束,返回False
if not p1:
return False
# 如果说结点元素不相等,则不能匹配
if p1.val != p2.val:
return False
return self.IsSubtree(p1.left, p2.left) and self.IsSubtree(p1.right, p2.right)
面试题19:二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像。
二叉树的镜像定义:源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5
思路:
由题目我们可以知道所谓二叉树的镜像无非就是不断交换根结点的左右子树(注意不是左右孩子,当然说左右孩子也对,但是每次递归的时候真正交换的是两个子树的头结点+整棵子树)。明白了原理,让我们用递归实现吧!
1、我们知道我们要定义一个方法 Mirror ,这个方法的主要功能是实现整棵树的交换,注意是整棵树,而不是某颗子树,为什么这么说,接下来便知道了。
2、首先我们肯定是交换根节点的左右子树嘛!对了,交换完之后,你会发现,对于以根节点左孩子右孩子为根节点的两棵子树,也需要做同样的事情,因此,为了不把代码写得跟论文那么长,我们决定用递归,自己调用自己的功能(想想它自己的功能是什么?无非就是将以参数为头结点的两棵子树交换嘛!),但传入的参数为根节点的左孩子、右孩子了
3、由于是先序遍历,所以呢!我们就先对左子树进行递归,在递归右子树
4、当2,3都执行完成,也就是我们的Mirror函数结束了,你会发现,这个Mirror函数实际操作的是整棵树,但函数体我们很直观理解的代码却只有根节点左右子树的交换的那部分代码(没涉及递归的那部分),其他谁帮我们做了呢?没错,就是递归函数。
还有就是上面强调的Mirror这个大函数是对整棵大树而言的,为什么呢?因为这个函数的方法体,已经包含了帮你处理左右子树的递归函数了!
最后,我不管你是看懂了还是没看懂,我尽力了。对于递归的理解,就是某些相同的功能跟自己实现的功能一模一样时,用递归,但要注意递归的终止条件哦!
class Solution:
# 返回镜像树的根节点
def Mirror(self, root):
if not root:
return
if root:
# 用先序遍历从根节点出发
root.left,root.right = root.right,root.left
self.Mirror(root.left)
self.Mirror(root.right)
面试题25:二叉树中和为某一值的路径
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
思路:
class Solution:
# 返回二维列表,内部每个列表表示找到的路径
def FindPath(self, root, expectNumber):
if not root:
return []
if not root.left and not root.right and expectNumber == root.val: # 左右子孩子都是空,并且当前值等于expectNumber返回当前结点的值
return [[root.val]]
result = []
left = self.FindPath(root.left, expectNumber - root.val)
right = self.FindPath(root.right, expectNumber - root.val)
for i in left+right:
result.append([root.val]+i)
return result
面试题39:二叉树的深度
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
思路:
方法一:递归
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def TreeDepth(self, pRoot):
if not pRoot:
return 0
left = self.TreeDepth(pRoot.left)
right = self.TreeDepth(pRoot.right)
return max(left+1,right+1)
方法二:非递归,层序遍历
from collections import deque
def TreeDepth(self, pRoot):
if not root:
return
depth = 0
queue = deque([pRoot])
last =pRoot
while queue:
current_node = queue.popleft()
if current_node.left:
queue.append(current_node.left)
if current_node.right:
queue.append(current_node.right)
if current_node == last:
depth+=1
if queue:
last = queue[-1]
return depth
面试题58:二叉树的下一个结点
给定一颗二叉树和其中的一个结点,如何找出中序遍历顺序的下一个结点?树中的结点除了有两个分别指向左右子结点的指针以外,还有一个指向父节点的指针。
思路:
- 若该节点存在右子树:则下一个节点为右子树最左子节点(如图节点B)
- 若该节点不存在右子树:这时分两种情况:
2.1 该节点为父节点的左子节点,则下一个节点为其父节点(如图节点D)
2.2 该节点为父节点的右子节点,则沿着父节点向上遍历,知道找到一个节点的父节点的左子节点为该节点,则该节点的父节点下一个节点(如图节点I,沿着父节点一直向上查找找到B(B为其父节点的左子节点),则B的父节点A为下一个节点)。
# -*- coding:utf-8 -*-
# class TreeLinkNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
# self.next = None
class Solution:
def GetNext(self, pNode):
# 中序遍历:左中右
if not pNode:
return None
# 判断当前结点有没有右子树,若存在右子树,下一个结点就是右子树的最左子结点
if pNode.right:
pNode = pNode.right # 进入到右子树的父节点
while pNode.left: # 不断遍历左结点
pNode = pNode.left
return pNode # 返回右子树的最左子结点
# 没有右子树就向上遍历找到他的父亲结点,如果他是父亲结点的左孩子结点,下一个结点就是父亲结点
# 如果不是,就继续向上找寻当前父亲结点w的父亲结点q,并判断q是否为他的父亲结点e的左孩子
# 如此反复直到找到匹配的情况,找不到就返回None
while pNode.next:
if pNode.next.left == pNode:
return pNode.next
pNode = pNode.next
return None
面试题58:对称的二叉树
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
思路:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSymmetrical(self, pRoot):
if not pRoot:
return True
return self.judge_helper(pRoot.left,pRoot.right)
def judge_helper(self,left,right):
if not left and not right: # 当left和right都为空时,返回True,跳出
return True
if not left or not right: # 当left和right有一边为空时,返回False,跳出
return False
if left.val == right.val: # 判断left和right的val是否相等,相等就继续递归判断应该对成的两个点是否对称
return self.judge_helper(left.left,right.right) and self.judge_helper(left.right,right.left)
return False
面试题60:把二叉树打印成多行
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
思路:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
from collections import deque
class Solution:
# 返回二维列表[[1,2],[4,5]]
def Print(self, pRoot):
if not pRoot:
return[]
res = [] # res列表用于记录最终结果
tmp = [] # 用于每一层结点的缓存
last = pRoot # 定义last结点表示下一层的最末结点
next_level = deque([pRoot]) # 初始化下一行结点的队列,起始位为根结点[pRoot]
while next_level: # 当下一行有结点时
current_node = next_level.popleft() # 取出下一行结点最左边的结点
tmp.append(current_node.val) # 将最左边的值放入tmp
if current_node.left: # 如果当前结点有左孩子
next_level.append(current_node.left) # 在队列右侧添加该左孩子结点
if current_node.right: # 如果当前结点有右孩子
next_level.append(current_node.right) # 在队列右侧添加该右孩子结点
if current_node == last: # 若当前结点正好是下一行结点的最后一个位置
res.append(tmp) # 给结果增添上tmp,也就是当前行的所有结点
tmp = [] # 清空tmp
if next_level: # 如果还有下一层
last = next_level[-1] # 令last等于末尾项
return res
面试题61:按之字形顺序打印二叉树
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
思路:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
from collections import deque
class Solution:
def Print(self, pRoot):
if not pRoot:
return []
result = []
tmp = []
last = pRoot #记录每层的最后一个结点,方便层序遍历换行
left_to_right = True
next_level_node_LtoR = deque([pRoot]) #初始化下一层所有节点的队列,起初为只有根结点
while next_level_node_LtoR: #当下一层不为空时
current_node = next_level_node_LtoR.popleft() #不停从下层队列左边弹出
tmp.append(current_node.val) #将弹出结点放入tmp中
if current_node.left:
next_level_node_LtoR.append(current_node.left)
if current_node.right:
next_level_node_LtoR.append(current_node.right)
if current_node == last: #当运行到最后一个结点,给result赋值当前行的所有元素,也就是tmp
if left_to_right:
result.append(tmp)
else: #若该行应该为从右到左,则倒序append
result.append(tmp[::-1])
tmp = [] #清空tmp,以便下一层继续使用
left_to_right = not left_to_right #调整此项,颠倒下一行的输出顺序
if next_level_node_LtoR: #更新下一行的last结点,如果下一行已经没有元素就会退出
last = next_level_node_LtoR[-1]
return result
面试题62:序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树
思路:也可以用层序遍历
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
index = -1
def Serialize(self, root):
if root == None:
return '#'
return str(root.val) + ',' + self.Serialize(root.left) + ',' + self.Serialize(root.right)
# return形如s = '5,#,#,6'
def Deserialize(self, s):
self.index += 1 #每次递归给index+1去到下一个字符
l = s.split(',')
if self.index >= len(s):
return None
root = None
if l[self.index] != '#':
root = TreeNode(int(l[self.index]))
root.left = self.Deserialize(s) # 对当前结点的左右结点递归
root.right = self.Deserialize(s)
return root
面试题:平衡二叉树
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
思路:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def IsBalanced_Solution(self, pRoot):
if not pRoot:
return True
# 对左右两边同时递归
if abs(self.Tree_depth(pRoot.left) - self.Tree_depth(pRoot.right)) > 1:
return False
return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
def Tree_depth(self,pRoot):
if not pRoot:
return 0
# 二叉树的后序遍历
left = self.Tree_depth(pRoot.left)
right = self.Tree_depth(pRoot.right)
return max(left+1,right+1) # 返回当前树深