二叉树的相关算法
二叉树的构造和遍历
- 说明:二叉树的结构包括:节点值,左子树和右子树。然后定义前序遍历、中序遍历、后序遍历和层次遍历几种遍历方法。
- 思路:前面三种遍历使用递归的思想最简单。层次遍历时可使用队列来实现。
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class BinaryTree:
# 前序遍历(递归)
def printTreeByPreorder(self, root):
res = []
def Preorder(root, res):
if root is None:
return None
# print(root.val)
res.append(root.val)
Preorder(root.left, res)
Preorder(root.right, res)
return res
return Preorder(root, res)
# 中序遍历(递归)
def printTreeByInorder(self, root)
res = []
def Inorder(root, res):
if root is None:
return None
Inorder(root.left, res)
# print(root.val)
res.append(root.val)
Inorder(root.right, res)
return res
return Inorder(root, res)
# 后序遍历(递归)
def printTreeByPosterorder(self, root):
res = []
def Posterorder(root, res):
if root is None:
return None
Posterorder(root.left, res)
Posterorder(root.right, res)
# print(root.val)
res.append(root.val)
return res
return Posterorder(root, res)
#层序遍历
def printTreeByLevel(self, root):
if root is None:
return None
res = []
queue= [root] # 队列
while queue:
current = queue.pop()
# print(current.val, queue)
res.append(current.val)
if current.left:
queue.insert(0, current.left)
if current.right:
queue.insert(0, current.right)
return res
二叉树的相关操作
二叉树的重构
- 说明:输入某二叉树的前序遍历要和中序遍历,。请重构出该二叉树。假设输入的前序和中序的结果都不含重复的数字。
- 思路:先根据前序的第一个数字确定头结点,然后根据中序遍历确定头结点的位置,从而确定左右子树的数量,然后使用递归的思想。
# 重构二叉树
def reConstructBinaryTree(self, pre, tin):
""" pre: 前序遍历的序列 tin: 中序遍历的序列 """
if not pre or not tin:
return None
if set(pre) != set(tin): # 确保先序遍历和中序遍历的数字是一致的
print('invalid input!')
return None
# 确定头结点:前序遍历结果中的第一个数
root_val = pre[0]
root = TreeNode(root_val) # 建立树的头结点
pos = tin.index(root_val) # 头结点在中序遍历结果中的位置
root.left = self.reConstructBinaryTree(pre[1: pos + 1], tin[0: pos]) # 用头节点以前的构造左子树
root.right = self.reConstructBinaryTree(pre[pos + 1:], tin[pos + 1:]) # 用头节点以后的构造右子树
return root
二叉树的镜像
- 说明:操作给定的二叉树,将其变换为源二叉树的镜像。
- 思路:二叉树的镜像就是保持根节点不便,然后左子树和右子树交换位置。所以思路就是:先先序遍历二叉树,如果有子树,那么将左右子树互换位置
# 递归法
def Mirror(self, root):
if not root:
return None
# 交换左右子树
root.left, root.right = root.right, root.left
# 在根节点的每层左右子树进行相同的操作(递归)
if root.left:
self.Mirror(root.left)
if root.right:
self.Mirror(root.right)
return root
# 循环法:使用队列
def Mirror2(self, root):
if not root:
return None
queue= [root]
while queue:
tree = queue[-1]
queue.pop()
if tree.left or tree.right:
tree.left, tree.right = tree.right, tree.left
if tree.left: # 交换完后, tree左子树不为空(空就不管)
queue.insert(0, tree.left)
if tree.right: # 交换完后, tree右子树不为空(空就不管)
queue.insert(0, tree.right)
return root
二叉树的路径问题
- 说明:输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
- 思路:使用先序遍历来遍历二叉树(因为路径是从root节点出发)。如果遍历到叶节点后。路径里面的和刚好==expectNumber,则打印该条记录。然后回退到上一root节点,继续遍历。—-递归
# 路径搜索
def FindPath(self, root, expectNumber):
# write code here
if not root:
return []
if not root.left and root.right:
if expectNumber == root.val:
return [[root.val]]
else:
return []
res_paths = [] # 记录所有找到的路径,并返回
current_path = [] # 一个栈,记录当前正在遍历的路径
self.findPathRecursively(root, expectNumber, current_path, res_paths)
return res_paths
# 用递归法来查找路径
def findPathRecursively(self, root, expectNumber, current_path, res_paths):
# 最小问题操作
current_path.append(root.val)
is_leaf = root.left is None and root.right is None # 判断是否到达叶节点
if expectNumber == sum(current_path) and is_leaf:
# res_paths.append(current_path) # 会返回[]
res_paths.append(current_path[:])
if root.left:
self.findPathRecursively(root.left, expectNumber, current_path, res_paths)
if root.right:
self.findPathRecursively(root.right, expectNumber, current_path, res_paths)
# 到达叶节点后,由于要地贵点上一层,需要将当前路径回退
current_path.pop()
二叉树的子结构问题
- 说明:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
- 思路:先确定B的头节点,然后再A中查找是否存在该节点(可能存在多个)。如果A中存在,那么再一次比较是否存在和B完全一样的结构
def HasSubtree(self, pRoot1, pRoot2):
result = False
if pRoot1 and pRoot2:
if pRoot1.val == pRoot2.val: # tree2的头结点在tree1,就比较tree1中是否有和tree2一样的结构
result = self.does_tree1_has_tree2(pRoot1, pRoot2)
# 如果tree1可能含有多个tree2的头结点
if not result: # 在 tree1的左子树中继续寻找
result = self.HasSubtree(pRoot1.left, pRoot2)
if not result: # 在左子树中没有找到,继续在右子树中寻找
result = self.HasSubtree(pRoot1.right, pRoot2)
return result
# 判断tree1中是否含有tree2一样的结构
def does_tree1_has_tree2(self, pRoot1, pRoot2):
# 注意:为什么先要判断pRoot2?后判断pRoot1?为什么pRoot2空时候,return True?
if not pRoot2: # 此时,说明tree2每个节点已经被遍历完
return True
if not pRoot1:
return False
if pRoot1.val != pRoot2.val: # tree1的节点与tree2的不一样
return False
# 分别比较tree1和tree2的左子树和右子树,如果都一样,则返回true
return self.does_tree1_has_tree2(pRoot1.left, pRoot2.left) and self.does_tree1_has_tree2(pRoot1.right, pRoot2.right)
二叉搜索树
二叉搜索树的遍历
二叉搜素树的特点是:左子树的节点值都小于根节点,右子树的节点值都大于根节点。
由于二叉搜索树的特点,所以在遍历时,与二叉树的中序遍历以及层序遍历的原理是一样的。参考上面。
- 说明:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
- 思路:根据root节点来确定左右子树。然后再递归判断左右子树分别是不是二叉搜索树
def VerifySquenceOfBSTByPostOrder(self, sequence):
if not sequence: # 空树
return False
root = sequence[-1] # 最后一个节点为root
if root >= max(sequence) or root <= min(sequence):
return True
index = 0
# 确定左子树
for i in range(len(sequence)-1):
if sequence[i] > root: # 左子树的所有元素都比root小
index = i
break
# 确定右子树
for i in range(index+1, len(sequence)-1):
if sequence[i] < root: # 右子树的所有元素都比root大
return False
# 判断左子树是不是搜索树
left = True
if index > 0: # 有左子树的话
left = self.VerifySquenceOfBSTByPostOrder(sequence[:index])
# 判断左子树是不是搜索树
right = True
if index < len(sequence)-1: # 如果有右子树
right = self.VerifySquenceOfBSTByPostOrder(sequence[index: len(sequence)-1])
return left and right
- 拓展:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果
- 思路:思路同上
def VerifySquenceOfBSTByPreOrder(self, sequence):
if len(sequence) == 0:
return False
root = sequence[0] # root节点
if root >= max(sequence) or root <= min(sequence):
return True
# 确定左子树和右子树
index = 0
for i in range(1, len(sequence)):
if sequence[i] > root:
index = i
break
for i in range(index+1, len(sequence)):
if sequence[i] < root:
return False
# 分别判断左子树和右子树是否为搜索树
left = True
if index > 0:
left = self.VerifySquenceOfBSTByPreOrder(sequence[1: index])
right = True
if index < len(sequence)-1:
right = self.VerifySquenceOfBSTByPreOrder(sequence[index: len(sequence)])
return left and right
二叉搜索树与链表的结合
- 说明:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
- 思路:中序遍历二叉搜索树:递归.在左子树递归时,遍历到左子树中最右节点(最大)max,让root的left指向max, max的right指向root;在右子树递归时,遍历到右子树的最左节点(最小)min, 让root的right指向min,min的left指向root.
def Convert(self, pRootOfTree):
if not pRootOfTree:
return None
if not pRootOfTree.left and not pRootOfTree.right:
return pRootOfTree
converted = self.ConvertNode(pRootOfTree)
while converted.left:
converted = converted.left
return converted
def ConvertNode(self, root):
# 左子树递归
if root.left:
self.ConvertNode(root.left)
left_max_node = root.left
if left_max_node:
while left_max_node.right: # 定位到左子树的最大值
left_max_node = left_max_node.right
root.left = left_max_node
left_max_node.right = root
# 右子树递归
if root.right:
self.ConvertNode(root.right)
right_min_node = root.right
if right_min_node:
while right_min_node.left:
right_min_node = right_min_node.left # 定位到右子树最小的节点
root.right = right_min_node
right_min_node.left = root
return root