一、二叉树的前序、后序、中序,层次遍历的递归和循环实现
class TreeNode(object):
"""定义节点类"""
def __init__(self, x, left=None, right=None):
self.val = x
self.left = None
self.right = None
class Tree(object):
def __init__(self):
self.root = TreeNode(-1)
self.queue = []
def add(self, elem):
node = TreeNode(elem)
if self.root.val == -1:
self.root = node
self.queue.append(self.root)
else:
treeNode = self.queue[0]
if treeNode.left==None:
treeNode.left = node
self.queue.append(treeNode.left)
else:
treeNode.right = node
self.queue.append(treeNode.right)
self.queue.pop(0)
def inorderTraversal(self, root):
"""
二叉树的中序遍历:左中右
递归实现
"""
if not root:
return []
return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
def inorderTraversal2(self, root):
"""二叉树的迭代实现"""
stack = []
cur = root
res = []
while stack or cur:
if cur:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
res.append(cur.val)
cur = cur.right
return res
def postorderTraversal(self, root):
"""二叉树的后序遍历:左右中"""
if not root:
return []
return self.postorderTraversal(root.left)+self.postorderTraversal(root.right) + [root.val]
def preorderTraversal(self, root):
"""二叉树的前序遍历:中左右"""
if not root:
return []
return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)
def preorderTraversal2(self, root):
"""前序遍历循环实现"""
stack = []
cur = root
res = []
while cur or stack:
if cur:
res.append(cur.val)
stack.append(cur.right)
cur = cur.left
else:
cur = stack.pop()
return res
def postorderTraversal2(self, root):
"""类似前序遍历,中右左,先遍历右节点,将左节点压入栈
最后将数组翻转,就变成了左右中"""
stack = []
cur = root
res = []
while cur or stack:
if cur:
res.append(cur.val)
stack.append(cur.left)
cur = cur.right
else:
cur = stack.pop()
return res[::-1]
def levelTraversal(self, root):
"""递归实现
递归函数需要有个参数level表示当前节点的层数
遍历的结果返回到一个res=[[]]一个二维列表中
res中每个子列表保存了对应index层的从左到右所有节点的值"""
def helper(node, level):
if not node:
return
else:
res[-1].append(node.val)
if len(res)==level:
res.append([])
helper(node.left, level+1)
helper(node.right,level+1)
res = [[]]
helper(root,1)
return res[:-1]
def levelTraversal2(self, root):
"""循环实现,需要借助队列queue"""
if not root:
return []
res = []
queue = [root]
cur = root
while queue:
cur = queue.pop(0)
res.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
return res
二、 leetcode例题
1、从先序遍历还原二叉树
我们从二叉树的根节点 root 开始进行深度优先搜索。
在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度),然后输出该节点的值。(如果节点的深度为 D,则其直接子节点的深度为 D + 1。根节点的深度为 0)。
如果节点只有一个子节点,那么保证该子节点为左子节点。
给出遍历输出 S,还原树并返回其根节点 root。
示例 1:
输入:“1-2–3--4-5–6--7”
输出:[1,2,5,3,4,6,7]
基本思路:遍历字符串,利用一个字典记录每个节点的层数和该节点,然后根据当前的值创建节点,并在字典中查找它的父节点是否存在,即寻找比当前节点层数小1的节点是否存在,如果存在,就将这个父节点与当前节点相连。
代码如下:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def create_node(self, num, value, node_dict):
node = TreeNode(value)
node_dict[num] = node
if num-1 in node_dict:
parent_node = node_dict[num-1]
if parent_node.left == None:
parent_node.left = node
else:
parent_node.right = node
def recoverFromPreorder(self, S: str) -> TreeNode:
value = ""
num = 0
node_dict = {}
for c in S:
if c=="-":
if value:
self.create_node(num,value,node_dict)
value = ""
num = 1
else:
num +=1
else:
value += c
self.create_node(num, value, node_dict)
#print(node_dict)
return node_dict[0]
2、重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
3
/
9 20
/
15 7
基本思路;递归法
前序遍历特点: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序,以题目示例为例:[ 3 | 9 | 20 15 7 ]
中序遍历特点: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序,以题目示例为例:[ 9 | 3 | 15 20 7 ]
根据题目描述输入的前序遍历和中序遍历的结果中都不含重复的数字,其表明树中每个节点值都是唯一的。
根据以上信息,可以顺序完成以下工作:
前序遍历的首个元素即为根节点 root 的值;
在中序遍历中搜索根节点 root 的索引 ,可将中序遍历划分为 [ 左子树 | 根节点 |
右子树 ] 。 根据中序遍历中的左(右)子树的节点数量,可将前序遍历划分为 [ 根节点 | 左子树 | 右子树 ] 。
代码如下:
from typing import List
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def __init__(self):
self.preorder = None
self.reverses = None
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
pre_size = len(preorder)
in_size = len(inorder)
if pre_size != in_size:
return None
self.preorder = preorder
self.reverses = dict()
for i in range(in_size):
self.reverses[inorder[i]] = i
return self.__build_tree(0, pre_size - 1, 0, in_size - 1)
def __build_tree(self, pre_left, pre_right, in_left, in_right):
if pre_left > pre_right or in_left > in_right:
return None
pivot = self.preorder[pre_left]
root = TreeNode(pivot)
pivot_index = self.reverses[pivot]
root.left = self.__build_tree(pre_left + 1, pivot_index - in_left + pre_left,
in_left, pivot_index - 1)
root.right = self.__build_tree(pivot_index - in_left + pre_left + 1, pre_right,
pivot_index + 1, in_right)
return root
3、二叉树的最大路径和
题目描述:给定一个非空二叉树,返回其最大路径和。
本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。
基本思路:根据当前节点的角色,路径和可以分两种情况:
一:以当前节点为根结点
1.只有当前节点
2.当前节点+左子树
3.当前节点+右子树
4.当前节点+左右子树
这四种情况的最大值即为以当前节点为根结点的最大路径和,此时最大值需要与已经保存的最大值进行比较
二:当前节点作为父节点的一个子节点,此时当前节点与父节点相连接,则需要取单端最大值,因此只有三种情况。
1.只有当前节点
2.当前节点+左子树
3.当前节点+右子树
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def maxPathSum(self, root: TreeNode) -> int:
self.res = float("-inf")
def dfs(root):
if not root:
return 0
val = root.val
sum_left = max(0,dfs(root.left))
sum_right = max(0,dfs(root.right))
sum_max = val+sum_left+sum_right
self.res = max(self.res,sum_max)
return max(sum_left,sum_right)+val
dfs(root)
return self.res
4、二叉树中和为某一值得路径
题目描述:输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
基本思路:深度优先搜索,因为题目要求路径为从根结点到叶节点,因此递归的终止条件是路径和等于taeget且该节点没有左右子节点。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
res = []
def dfs(root,cur,target):
if target==root.val and not root.left and not root.right:
cur += [root.val]
res.append(cur)
return
if root.left:
dfs(root.left,cur+[root.val],target-root.val)
if root.right:
dfs(root.right,cur+[root.val],target-root.val)
if not root:return []
dfs(root,[],sum)
return res
5、二叉树的最近公共祖先
题目描述:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
基本思路:(1)先说一个简单的思路,先序遍历然后用是用一个字典存储树中所有节点的祖先,然后取两个节点公共祖先中第一个,是最靠近这两个节点的,所以是最近公共祖先。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
dict = {}
dict[root.val] = [root]
def dfs(root):
if not root:
return
parent = root.val
if root.left:
dict[root.left.val] = [root.left] + dict[parent]
if root.right:
dict[root.right.val] = [root.right]+ dict[parent]
dfs(root.left)
dfs(root.right)
dfs(root)
#print(dict)
a = [i for i in dict[p.val] if i in dict[q.val]]
print(a)
return a[0]
(2)跟(1)的思路类似,从上到下遍历树所有节点,记录每个节点的最近祖先,
从根节点开始遍历整棵二叉树,用哈希表记录每个节点的父节点指针。
从 p 节点开始不断往它的祖先移动,并用数据结构记录已经访问过的祖先节点。
同样,我们再从 q 节点开始不断往它的祖先移动,如果有祖先已经被访问过,即意味着这是 p 和 q 的深度最深的公共祖先,即 LCA 节点。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
stack = [root]
parent = {root:None}
while p not in parent or q not in parent:
node = stack.pop()
if node.left:
parent[node.left]=node
stack.append(node.left)
if node.right:
parent[node.right]=node
stack.append(node.right)
ancestor=set()
while p:
ancestor.add(p)
p=parent[p]
while q not in ancestor:
q = parent[q]
return q
(3)祖先的定义:若节点
p
p
p在
r
o
o
t
root
root的左右子树中,或
p
=
r
o
o
t
p=root
p=root,则称
r
o
o
t
root
root是
p
p
p的祖先。
最近公共祖先的定义: 设节点 root为节点 p, q的某公共祖先,若其左子节点 root.left,和右子节点 root.right都不是 p,q的公共祖先,则称 root 是 “最近的公共祖先” 。
根据以上定义,若 root 是 p, q 的 最近公共祖先 ,则只可能为以下情况之一:
p 和 q在 root 的子树中,且分列 root 的 异侧(即分别在左、右子树中);
p = root,且 q在 root的左或右子树中;
q = root,且 p在 root 的左或右子树中
考虑通过递归对二叉树进行后序遍历,当遇到节点 p 或 q 时返回。从底至顶回溯,当节点 p, q在节点 root的异侧时,节点 root即为最近公共祖先,则向上返回 root
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if not root or root==p or root==q:return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if not left and not right:return
if not right:return left
if not left: return right
return root
6、验证二叉搜索树
题目描述:给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
基本思路:中序遍历,先访问左子树,再访问中间节点,再访问右子树,所以如果这个树是二叉搜索树,那么中序遍历得到的一定是升序的。所以我们中序遍历二叉树,如果当前节点小于前一个节点,那么就返回False。
代码如下:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
stack, preValue = [], float("-inf")
while root or stack:
while root:
stack.append(root)
root = root.left
root = stack.pop()
if root.val<=preValue:
return False
preValue = root.val
root = root.right
return True
7、不同的二叉搜索树
题目描述:给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
基本思路:假设n个结点的二叉搜索树的个数为G(n),那么这个二叉搜索树可以以1,2,…,n为根结点,然后该根结点左边又必须构成一个二叉搜索数,右边也必须构成一个二叉搜索树,那么G(n)就等于左边能构成的二叉搜索树的个数与右边能构成二叉搜索树的个数的笛卡尔积,又因为能够成二叉搜索输的个数与数本身无关,只跟序列的个数有关,即1,2,3,4与2,3,4,5能构成二叉搜索树的个数相同。所以我们可以利用动态规划得到这个问题的解。
代码如下:
class Solution:
def numTrees(self, n: int) -> int:
if n <= 1:return n
dp = [1]*(n+1)
for i in range(2,n+1):
dp[i]=0
for j in range(1,i+1):
dp[i] += dp[j-1]*dp[i-j]
return dp[n]
8、二叉搜索树的第k大节点
题目描述:给定一棵二叉搜索树,请找出其中第k大的节点。
基本思路:二叉搜索树的特点是当前节点小于右子树所有节点,大于左子树所有节点,因此我们右中左遍历树,到第k节点返回,该节点就是二叉搜索树第k大的节点。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
stack = []
res = []
cur = root
while stack or cur:
if cur:
stack.append(cur)
cur = cur.right
else:
cur = stack.pop()
res.append(cur.val)
if len(res)==k:return cur.val
cur = cur.left
9、二叉树的直径
题目描述:给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。、
基本思路:树的递归
代码思路:我们定义一个递归函数 depth(node) 计算 d node d n o d e d_{\textit{node}}dnode dnodednode,函数返回该节点为根的子树的深度。先递归调用左儿子和右儿子求得它们为根的子树的深度 LL 和 RR ,则该节点为根的子树的深度即为$max(L,R)+1
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
self.ans = 1
def depth(node):
# 访问到空节点了,返回0
if not node: return 0
# 左儿子为根的子树的深度
L = depth(node.left)
# 右儿子为根的子树的深度
R = depth(node.right)
# 计算d_node即L+R+1 并更新ans
self.ans = max(self.ans, L+R+1)
# 返回该节点为根的子树的深度
return max(L, R) + 1
depth(root)
return self.ans - 1
10、特定深度节点链表
题目描述:给定一棵二叉树,设计一个算法,创建含有某一深度上所有节点的链表(比如,若一棵树的深度为 D,则会创建出 D 个链表)。返回一个包含所有深度的链表的数组。
输入:[1,2,3,4,5,null,7,8]
1
/ \
2 3
/ \ \
4 5 7
/
8
输出:[[1],[2,3],[4,5,7],[8]]
基本思路:二叉树的层次遍历,将每一层的节点建立一个链表
代买如下:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def listOfDepth(self, tree: TreeNode) -> List[ListNode]:
if not tree:
return []
queue = [tree]
sol = []
while queue:
n = len(queue)
node = ListNode(None)
cur = node
while n>0:
curr = queue.pop(0)
cur.next = ListNode(curr.val)
cur = cur.next
n -= 1
if curr.left:
queue.append(curr.left)
if curr.right:
queue.append(curr.right)
sol.append(node.next)
return sol
11、不同的二叉搜索树2
题目描述:给定一个整数 n,生成所有由 1 … n 为节点所组成的 二叉搜索树 。
基本思路:递归 1到n所有结点都可以作为根结点,取i作为根结点,i左边就是左子树的结点,i右边就是右子树的结点。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def generateTrees(self, n: int) -> List[TreeNode]:
def generate_tree(start, end):
if start>end:
return [None,]
all_tree = []
for i in range(start, end+1):
left_tree = generate_tree(start, i-1)
print(left_tree)
right_tree = generate_tree(i+1, end)
for l in left_tree:
for r in right_tree:
current_tree = TreeNode(i)
current_tree.left = l
current_tree.right = r
all_tree.append(current_tree)
return all_tree
return generate_tree(1,n) if n>0 else []
12、将二叉树转化成单链表
基本思路:前序遍历的迭代实现,利用一个栈存储当前节点的右子节点,然后令子节点为None,右子节点为左子节点,注意右子节点先入栈,,左子节点后入栈。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def flatten(self, root: TreeNode) -> None:
"""
Do not return anything, modify root in-place instead.
"""
stack = [root]
if root == None:
return None
while stack:
node = stack.pop()
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
if stack != []:
node.left = None
node.right = stack[-1]