剑指Offer数据结构之树[Python版]
面试题004 重建二叉树
题目描述:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
解题思路:
递归思想。前序遍历中第一个元素是根,因此在中序遍历中找到根的位置下标,根将中序遍历分为两部分,分别是左子树和右子树,然后继续递归寻找左右子树的根节点。
代码:
class Solution:
# 返回构造的TreeNode根节点
def reConstructBinaryTree(self, pre, tin):
# write code here
if not pre or not tin:
return None
root = TreeNode(pre[0])
index = tin.index(pre[0])
root.left = self.reConstructBinaryTree(pre[1:1+index],tin[:index])
root.right = self.reConstructBinaryTree(pre[1+index:],tin[1+index:])
return root
面试题017 树的子结构
题目描述:
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
解题思路:判断A和B的根是否相同,如果不同的话,将A的左子树和右子树再与B做判断;如果相同的话,则判断子树是否相同:比较每个节点,有一个不相同则不相同
代码
class Solution:
def HasSubtree(self, pRoot1, pRoot2):
# write code here
result = False
if pRoot1 and pRoot2:
if pRoot1.val==pRoot2.val:
result = self.DoesTree1HaveTree2(pRoot1,pRoot2)
if not result:
result = self.HasSubtree(pRoot1.left,pRoot2)
if not result:
result = self.HasSubtree(pRoot1.right,pRoot2)
return result
def DoesTree1HaveTree2(self,pRoot_A,pRoot_B):
if not pRoot_B:
return True
if not pRoot_A:
return False
if pRoot_A.val != pRoot_B.val:
return False
return self.DoesTree1HaveTree2(pRoot_A.left,pRoot_B.left) and self.DoesTree1HaveTree2(pRoot_A.right,pRoot_B.right)
面试题018 二叉树的镜像
题目描述:操作给定的二叉树,将其变换为源二叉树的镜像。
示例:
二叉树的镜像定义:源二叉树
8
/
6 10
/ \ /
5 7 9 11
镜像二叉树
8
/
10 6
/ \ /
11 9 7 5
解题思路:将二叉树的每个子树的左右节点交换位置就可以得到二叉树的镜像,由于每一层的树操作都是一样的,所以使用递归求解。
代码
class Solution:
# 返回镜像树的根节点
def Mirror(self, root):
# write code here
if not root:
return root
node = root.left
root.left = root.right
root.right = node
self.Mirror(root.left)
self.Mirror(root.right)
return root
面试题022 从上往下打印二叉树
题目描述:从上往下打印出二叉树的每个节点,同层节点从左至右打印。
解题思路:二叉树层次遍历,用队列存储每层结点,再依次弹出
代码
class Solution:
# 返回从上到下每个节点值列表,例:[1,2,3]
def PrintFromTopToBottom(self, root):
# write code here
if not root:
return []
quene = []
result = []
quene.append(root)
while len(quene)>0:
node = quene.pop(0)
result.append(node.val)
if node.left:
quene.append(node.left)
if node.right:
quene.append(node.right)
return result
面试题023 二叉搜索树的后序遍历
题目描述:输入一个非空整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
解题思路:
已知条件:后序遍历最后一个值为root;二叉搜索树左子树值都比root小,右子树值都比root大。
1、确定root;
2、遍历序列(除去root结点),找到第一个大于root的位置,则该位置左边为左子树,右边为右子树;
3、遍历右子树,若发现有小于root的值,则直接返回false;
4、分别判断左子树和右子树是否仍是二叉搜索树(即递归步骤1、2、3)
代码
class Solution:
def VerifySquenceOfBST(self, sequence):
# write code here
if not sequence:
return False
return self.helper(sequence)
def helper(self, sequence):
if not sequence:
return True
root = sequence[-1]
for i in range(len(sequence)):
if sequence[i] > root:
break
for right in sequence[i:-1]:
if right < root:
return False
return self.helper(sequence[:i]) and self.helper(sequence[i:-1])
面试题024 二叉树中和为某一值得路径
题目描述:输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
解题思路:前序遍历,每次访问一个节点就将当前权值求和,如果当前权值和期待值一致,就保存,每次深度遍历到底部
代码
class Solution:
# 返回二维列表,内部每个列表表示找到的路径
def FindPath(self, root, expectNumber):
# write code here
if not root:
return []
if root and not root.left and not root.right and root.val==expectNumber:
return [[root.val]]
res = []
left = self.FindPath(root.left, expectNumber-root.val)
right = self.FindPath(root.right, expectNumber-root.val)
for i in left+right:
res.append([root.val]+i)
res = sorted(res,key=lambda x:len(x),reverse=True)
return res
面试题026 二叉搜索树与双向链表
题目描述:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
解题思路:From leetcode
解法基于性质:二叉搜索树的中序遍历为 递增序列 。
将二叉搜索树转换成一个 “排序的循环双向链表” ,其中包含三个要素:
排序链表: 节点应从小到大排序,因此应使用 中序遍历 “从小到大”访问树的节点;
双向链表: 在构建相邻节点(设前驱节点 prepre ,当前节点 curcur )关系时,不仅应 pre.right = curpre.right=cur ,也应 cur.left = precur.left=pre 。
循环链表: 设链表头节点 headhead 和尾节点 tailtail ,则应构建 head.left = tailhead.left=tail 和 tail.right = headtail.right=head 。
根据以上分析,考虑使用中序遍历访问树的各节点 curcur ;并在访问每个节点时构建 curcur 和前驱节点 prepre 的引用指向;中序遍历完成后,最后构建头节点和尾节点的引用指向即可。
代码
class Solution(object):
def treeToDoublyList(self, root):
"""
:type root: Node
:rtype: Node
"""
def dfs(cur):
if not cur: return
dfs(cur.left) # 递归左子树
if self.pre: # 修改节点引用
self.pre.right, cur.left = cur, self.pre
else: # 记录头节点
self.head = cur
self.pre = cur # 保存 cur
dfs(cur.right) # 递归右子树
if not root: return
self.pre = None
dfs(root)
self.head.left, self.pre.right = self.pre, self.head
return self.head
解题思路:二叉树的中序遍历
中序遍历中每个结点的链接
代码
class Solution:
def Convert(self, pRootOfTree):
if not pRootOfTree:
return None
p = pRootOfTree
stack = []
resStack = []
while p or stack:
if p:
stack.append(p)
p = p.left
else:
node = stack.pop()
resStack.append(node)
p = node.right
resP = resStack[0]
while resStack:
top = resStack.pop(0)
if resStack:
top.right = resStack[0]
resStack[0].left = top
return resP
解题思路:递归
class Solution:
def Convert(self, root):
if not root:
return None
if not root.left and not root.right:
return root
# 将左子树构建成双链表,返回链表头
left = self.Convert(root.left)
p = left
# 定位至左子树的最右的一个结点
while left and p.right:
p = p.right
# 如果左子树不为空,将当前root加到左子树链表
if left:
p.right = root
root.left = p
# 将右子树构造成双链表,返回链表头
right = self.Convert(root.right)
# 如果右子树不为空,将该链表追加到root结点之后
if right:
right.left = root
root.right = right
return left if left else root
面试题038 二叉树的深度
题目描述:输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
解题思路:一棵二叉树的深度是其左子树和右子树深度的最大值+1。拿左子树的深度来讲,左子树的深度等于其左右子树深度的最大值+1。递归这一过程即可
求树的深度,可以从层次遍历出发考虑,层次遍历可以使用队列完成,也可以使用递归完成
代码
方法一:使用队列
class Solution:
# 层次遍历
def levelOrder(self, root):
# write your code here
# 存储最后层次遍历的结果
res = []
# 层数
count = 0
# 如果根节点为空,则返回空列表
if root is None:
return count
# 模拟一个队列储存节点
q = []
# 首先将根节点入队
q.append(root)
# 列表为空时,循环终止
while len(q) != 0:
# 使用列表存储同层节点
tmp = []
# 记录同层节点的个数
length = len(q)
for i in range(length):
# 将同层节点依次出队
r = q.pop(0)
if r.left is not None:
# 非空左孩子入队
q.append(r.left)
if r.right is not None:
# 非空右孩子入队
q.append(r.right)
tmp.append(r.val)
if tmp:
count += 1 # 统计层数
res.append(tmp)
return count
def TreeDepth(self, pRoot):
# write code here
# 使用层次遍历
# 当树为空直接返回0
if pRoot is None:
return 0
count = self.levelOrder(pRoot)
return count
方法二:使用递归方法
class Solution:
def TreeDepth(self, pRoot):
# write code here
# 使用层次遍历
# 当树为空直接返回0
if pRoot is None:
return 0
# 方法2:使用递归
# 如果该树只有一个结点,它的深度为1.如果根节点只有左子树没有右子树,
# 那么树的深度为左子树的深度加1;同样,如果只有右子树没有左子树,
# 那么树的深度为右子树的深度加1。如果既有左子树也有右子树,
# 那该树的深度就是左子树和右子树的最大值加1.
count = max(self.TreeDepth(pRoot.left), self.TreeDepth(pRoot.right)) + 1
return count
面试题039 平衡二叉树
题目描述:输入一棵二叉树,判断该二叉树是否是平衡二叉树。
一棵高度平衡二叉树定义:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。
解题思路:
方法一:自顶向下,对于每个节点,都计算一下左子树以及右子树的差的绝对值,即每个节点都判断一下。
算法复杂度为O(N*2)
代码
class Solution:
def IsBalanced_Solution(self, pRoot):
# write code here
if not pRoot:
return True
left = self.depth(pRoot.left)
right = self.depth(pRoot.right)
return abs(left - right) <= 1 and self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
def depth(self, pRoot):
if not pRoot:
return 0
return 1 + max(self.depth(pRoot.left), self.depth(pRoot.right))
方法二:自下往上 算法复杂度O(N)
代码:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def IsBalanced_Solution(self, p):
return self.dfs(p) != -1
def dfs(self, p):
if p is None:
return 0
left = self.dfs(p.left)
if left == -1:
return -1
right = self.dfs(p.right)
if right == -1:
return -1
if abs(left - right) > 1:
return -1
return max(left, right) + 1
面试题057 二叉树的下一个结点
题目描述:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
解题思路:中序遍历的规则是:左根右
分成两大类:1、有右子树的,那么下个结点就是右子树最左边的点;(eg:D,B,E,A,C,G) 2、没有右子树的,也可以分成两类,a)是父节点左孩子(eg:N,I,L) ,那么父节点就是下一个节点 ; b)是父节点的右孩子(eg:H,J,K,M)找他的父节点的父节点的父节点...直到当前结点是其父节点的左孩子位置。如果没有eg:M,那么他就是尾节点。
代码
class Solution:
def GetNext(self, pNode):
# write code here
if pNode.right:#有右子树
p=pNode.right
while p.left:
p=p.left
return p
while pNode.next:#无右子树,则找第一个当前节点是父节点左孩子的节点
if(pNode.next.left==pNode):
return pNode.next
pNode = pNode.next#沿着父节点向上遍历
return None #到了根节点仍没找到,则返回空
面试题058 对称的二叉树
题目描述:请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/
2 2
/ \ /
3 4 4 3
输入:root = [1,2,2,3,4,4,3]
输出:true
解题思路:简单DFS
镜像也就是左右子树的左右节点值是否相等,通过比较即可得出结果
代码
class Solution:
def isSymmetrical(self, pRoot):
# write code here
if not pRoot:
return True
def isSyt(left_,right_):
if not left_ and not right_:#节点都为空,也是对称的
return True
elif left_ and right_:#都不为空
if left_.val != right_.val:#不为空且不相等则不是对称的
return False
else:
return isSyt(left_.right, right_.left) and isSyt(left_.left, right_.right)#遍历左右子树
else:#一个节点为空一个节点不为空
return False
return isSyt(pRoot.left, pRoot.right)
简化版
class Solution:
def isSymmetrical(self, pRoot):
# write code here
def recur(L, R):
if not L and not R: return True
if not L or not R or L.val != R.val: return False
return recur(L.left, R.right) and recur(L.right, R.left)
return recur(pRoot.left, pRoot.right) if pRoot else True
面试题059
题目描述:请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
解题思路:层序打印,和按层打印的唯一区别是是否需要将当前层打印倒置。
代码
class Solution:
def Print(self, pRoot):
# write code here
if not pRoot: return []
res, roots = [], [pRoot]
while roots:
tmp = []
for _ in range(len(roots)):
r = roots.pop(0)
tmp.append(r.val)
if r.left: roots.append(r.left)
if r.right: roots.append(r.right)
if len(res) & 1: tmp.reverse()
res.append(tmp)
return res
面试题060 把二叉树打印成多行
题目描述:从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
解题思路:层序打印
代码
class Solution:
# 返回二维列表[[1,2],[4,5]]
def Print(self, pRoot):
# write code here
if not pRoot:
return []
res, roots = [], [pRoot]
while roots:
temp = []
for _ in range(len(roots)):
r = roots.pop(0)
temp.append(r.val)
if r.left: roots.append(r.left)
if r.right: roots.append(r.right)
res.append(temp)
return res
面试题061 序列化二叉树
题目描述:请实现两个函数,分别用来序列化和反序列化二叉树
示例: 将以下二叉树:
1
/
2 3
/
4 5
序列化为 "[1,2,3,null,null,4,5]"
解题思路:From 剑指offer
递归实现二叉树前序遍历,前序遍历实现序列化与反序列化
代码
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def Serialize(self, root):
# write code here
if root is None:
return '#'
return str(root.val) + '!' + self.Serialize(root.left) + '!' + self.Serialize(root.right)
def Deserialize(self, s):
# write code
def deserialize(ls):
if len(s) <= 0:
return None
root = None
val = ls.pop(0)
if val != '#':
root = TreeNode(int(val))
root.left = deserialize(ls)
root.right = deserialize(ls)
return root
arr = s.split('!')
return deserialize(arr)
面试题062 二叉搜索树的第k个结点
题目描述:给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
解题思路:中序遍历第k个数
代码
class Solution:
# 返回对应节点TreeNode
def KthNode(self, pRoot, k):
# write code here
if not pRoot:
return None
stack = []
while pRoot or stack:
while pRoot:
stack.append(pRoot)
pRoot = pRoot.left
pRoot = stack.pop()
k -= 1
if k == 0:
return pRoot
pRoot = pRoot.right