这篇博客讲的很好:二叉树题目汇总
关于解树的题目总的就一条,使用递归(递归的本质其实也是编译器帮你维护的栈结构)或者是栈,找好递归的终止条件
所有的题目都经过调试,基本没问题。
Talk is cheap, show my code to you.
一.前序遍历,中序遍历,后序遍历
法一:递归实现(其实递归就是一种栈)
法二:栈+迭代
递归与非递归
递归版(只要改变后面的return中的东西即可)
前序遍历(根左右)
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)
中序遍历(左 根 右)
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
后序遍历(左 右 根)
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]
非递归版就是用栈来进行即可
前序
# 先序打印二叉树(非递归)
def preOrderTravese(node):
stack = [node]
while len(stack) > 0:
print(node.val)
if node.right is not None:
stack.append(node.right)
if node.left is not None:
stack.append(node.left)
node = stack.pop()
中序
# 中序打印二叉树(非递归)
def inOrderTraverse(node):
stack = []
pos = node
while pos or len(stack) > 0:
if pos:
stack.append(pos)
pos = pos.left
else:
pos = stack.pop()
print(pos.val)
pos = pos.right
后序
# 后序打印二叉树(非递归)
# 使用两个栈结构
# 第一个栈进栈顺序:左节点->右节点->跟节点
# 第一个栈弹出顺序: 跟节点->右节点->左节点(先序遍历栈弹出顺序:跟->左->右)
# 第二个栈存储为第一个栈的每个弹出依次进栈
# 最后第二个栈依次出栈
def postOrderTraverse(node):
stack = [node]
stack2 = []
while len(stack) > 0:
node = stack.pop()
stack2.append(node)
if node.left is not None:
stack.append(node.left)
if node.right is not None:
stack.append(node.right)
while len(stack2) > 0:
print(stack2.pop().val)
二.层次遍历
宽度优先搜索BFS,迭代,使用先进先出的队列queue或两端都可以进出的deque,要让队列有进有出。
def layerTraverse(node):
if not node:
return None
queue = []
queue.append(node)
while len(queue) > 0:
tmp = queue.pop(0)
print(tmp.val)
if node.left:
queue.append(tmp.left)
if node.right:
queue.append(tmp.right)
三.求树的节点数
遍历。return 1+左子树的个数+右子树的个数
# 求二叉树节点个数
def treeNodenums(node):
if node is None:
return 0
nums = treeNodenums(node.left)
nums += treeNodenums(node.right)
return nums + 1
四.求树的叶子数
遍历。return 左子树的叶结点数+右子树的叶结点数
def leave(self,root): #递归求叶子节点个数
if root==None:
return 0
elif root.lchild ==None and root.rchild == None :
return 1
else:
return (self.leave(root.lchild)+self.leave(root.rchild))
五.求二叉树第k层的结点数
遍历。k作为递归函数的参数,每降低一层,k就要-1。
return KNodeCount(pRoot->left,k-1)+KNodeCount(pRoot->right,k-1);
def KNodeCount(node,k):
if node==None or k<0:
return 0
if k==0:
return 1
return KNodeCount(node.left,k-1)+KNodeCount(node.right,k-1)
六.求树的深度
遍历。对根结点求深度,就是求其左右子树的深度中最大的那个深度+1
# 二叉树的最大深度
def bTreeDepth(node):
if node is None:
return 0
ldepth = bTreeDepth(node.left)
rdepth = bTreeDepth(node.right)
return (max(ldepth, rdepth) + 1)
七.树的最长路径
#求解树的最长路径左边最长+右边最长咯 使用闭包就可以了
def largePath(node):
ans=0
def getHeight(node):
nonlocal ans #函数内部使用函数外的变量,使用闭包
if node==None:
return 0
l=getHeight(node.left)
r=getHeight(node.right)
ans=max(ans,l+r+1)
return max(l,r)+1
getHeight(node)
return ans
八.树的宽度
用python写一下求二叉树深度和宽度的代码,求深度用递归;求宽度用队列,然后把每层的宽度求出来,找出最大的就是二叉树的宽度
def treeWidth(node):
if node==None:
return 0
curwidth=1
maxwidth=0
stack=[]
stack.append(node)
while len(stack):
n=curwidth
for _ in range(n):
node=stack.pop(0)
curwidth-=1#这里是减一
if node.left:
stack.append(node.left)
curwidth+=1
if node.right:
stack.append(node.right)
curwidth+=1
maxwidth=max(maxwidth,curwidth)
return maxwidth
九.判断两颗二叉树是否结构相同
判断两个树是否相等和判断tree1是否包含tree2 python实现
(不用考虑val是否相同。结构相同:两颗二叉树的两个根结点对应的的左子树和右子树结构相同。)
遍历。return 判断两棵树的左子树是否相同&&判断两棵树的右子树是否相同;
什么叫相同?两个对应的结点同时为空
什么叫不同?两个对应的结点中只有一个结点为空
那都不为空时呢?去递归呗。
def equal(node_a, node_b):
"""
判断两个树是否相等
:param node_a:
:param node_b:
:return:
"""
if not node_a and not node_b:
return True
elif not node_a and node_b or node_a and not node_b or node_a.val != node_b.val:
return False
else:
return equal(node_a.left, node_b.left) and equal(node_a.right, node_b.right)
def tree1_have_tree2(tree1, tree2):
"""
判断tree1是否包含tree2
:param tree1:
:param tree2:
:return:
"""
if not tree2:
return True
if not tree1:
return False
if tree1.val != tree2.val:
return False
return tree1_have_tree2(tree1.left, tree2.left) and tree1_have_tree2(tree1.right, tree2.right)
十.求二叉树的镜像(翻转二叉树)
思路:判断本身是否不为空,然后将左子树和右子树相互交换。然后判断是否有左子树,有的话递归函数;同理,判断是否有右子树,有的话递归函数。
class TreeNode():
def __init__(self,x):
self.val = x
self.left = None
self.right = None
def function(root):
if root:
root.left,root.right = root.right,root.left
if root.left:
function(root.left)
if root.right:
function(root.right)
十一.求两个节点的最近公共祖先结点(LCA)
用到了分治法的思想,想一想,若是对于一个树结点,在左子树中找到了A,在右子树中找到了B,那说明此结点是公共节点
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
class Solution:
"""
@param: root: The root of the binary search tree.
@param: A: A TreeNode in a Binary.
@param: B: A TreeNode in a Binary.
@return: Return the least common ancestor(LCA) of the two nodes.
"""
def lowestCommonAncestor(self, root, A, B):
# A&B=>LCA
# !A&!B=>None
# A&!B=>A
# B&!A=>B
if(root is None or root==A or root==B):
return root #若root为空或者root为A或者root为B,说明找到了A和B其中一个
left=self.lowestCommonAncestor(root.left,A,B)
right=self.lowestCommonAncestor(root.right,A,B)
if(left is not None and right is not None):
return root #若左子树找到了A,右子树找到了B,说明此时的root就是公共祖先
if(left is None): #若左子树是none右子树不是,说明右子树找到了A或B
return right
if(right is None): #同理
return left
return None
a=Tree = TreeNode(2)
b=Tree.left = TreeNode(1)
c=Tree.right = TreeNode(3)
d=b.left=TreeNode(4)
s = Solution()
print(s.lowestCommonAncestor(a,b,d).val)
十二.求任意两个结点的距离
leetcode 找到树中距离最大的两个结点,Python实现
遍历。先找到两个结点的最低公共祖先结点,然后分别计算最低公共祖先结点(根结点)与它们的距离,最后相加即可。也就是两个函数,第二个函数用于求距离:遍历,先在左子树中找,if没找到的话,再在右子树中找,找到了则return res+1;最后都没找到就return -1。
其实就是求深度和最近公共父节点的结合,不难
def LCA(root,nodeA,nodeB):
if root==None:
return root
if nodeA==root or nodeB==root:
return nodeA
left=LCA(root.left,nodeA,nodeB)
right=LCA(root.right,nodeA,nodeB)
if left is not None and right is not None:
return root
if left==None:
return right
if right==None:
return left
return None
#print(LCA(a,f,g).val)
def get_depth(node):
if node is None:
return 0
else:
return max(1+get_depth(node.left),1+get_depth(node.right))
def node_distance(root,nodeA,nodeB):
node=LCA(root,nodeA,nodeB)
A_depth=get_depth(nodeA)
B_depth=get_depth(nodeB)
node_depth=get_depth(node)
return (node_depth-A_depth)+(node_depth-B_depth)
十三.找出二叉树中某个结点的所有祖先结点
遍历。bool递归函数。if(找出该结点的左子树中某个结点的所有祖先结点||找出该结点的右子树。。。),如果为if条件为true,则输出该结点。
res=[]
def findAllAnc(root,node):
if root is None:
return None
if node==root:
return True
if findAllAnc(root.left,node) or findAllAnc(root.right,node):
res.append(root.val)
return True
return False
十四.判断二叉树是否为平衡二叉树
扩展:二叉搜索树,在中序遍历中是依次增大的
判断二叉树是否为二叉搜索树、完全二叉树、平衡二叉树 Python实现
平衡二叉树:左右两个子树的高度差不超过1。左右子树也为平衡二叉树
遍历。if(判断左子树是否是平衡二叉树&&判断右子树是否是平衡二叉树),再在这个if中,if(判断深度差left-right的绝对值是否小于1)。。。挺有挑战的一道题。
# 判断二叉树是否为平衡二叉树
# 先判断该节点是否平衡
# 再递归去判断左节点和右节点是否平衡
# 递归求当前节点的深度
def getdepth(node):
if not node:
return 0
ld = getdepth(node.left)
rd = getdepth(node.right)
return max(ld, rd) + 1
def isB(head):
if not head:
return True
ld = getdepth(head.left)
rd = getdepth(head.right)
if abs(ld - rd) > 1:
return False
return isB(head.left) and isB(head.right)
十五.二叉树中和为某一值的路径
思路:首先要理解题意,是从根节点往子节点连。
1、如果只有根节点或者找到叶子节点,我们就把其对应的val值返回
2、如果不是叶子节点,我们分别对根节点的左子树、右子树进行递归,直到找到叶子结点。然后遍历把叶子结点和父节点对应的val组成的序列返回上一层;如果没找到路径,其实也返回了序列,只不过是[]
# -*- coding:utf-8 -*-
class TreeNode():
def __init__(self,x):
self.val = x
self.left = None
self.right = None
def function(root,target_number):
result = []
if not root:
return result
# 如果只有根节点或者找到叶子节点,我们就把其值返回
if not root.left and not root.right and root.val == target_number:
return [[root.val]]
else:
# 如果不是叶子节点,我们分别对根节点的左子树、右子树进行递归,注意修改变量:
left = function(root.left,target_number - root.val)
right = function(root.right,target_number - root.val)
for item in left+right:
result.append([root.val]+item)
return result
十六.根据前序和中序重建二叉树
构建 左子树的前序中序 和 右子树的前序中序,再分左右子树递归。
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
由前序遍历可知,1肯定是根节点,有中序遍历可知,1前面的三个数 (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):
#如果排列长度为0则二叉树为空
if (len(pre)==0 or len(tin)==0):
return None
else:
#把前序排列序列的第一个节点拿出来
root1 = TreeNode(pre.pop(0))
index1 = tin.index(root1.val)
left_pre_next = pre #这个时候的pre 已经没了第一个节点
left_tin_next = tin[:index1]
root1.left = self.reConstructBinaryTree(left_pre_next,left_tin_next)
R_pre_next = pre
R_tin_next = tin[index1+1:]
root1.right = self.reConstructBinaryTree(R_pre_next,R_tin_next)
return root1
十七.二叉树与双向链表
中序遍历。简单题。定义两个变量,一个变量用于记录头结点,一个变量用于改变指向。
1.核心算法依旧是中序遍历
2.不是从根节点开始,而是从中序遍历得到的第一个节点开始
3.定义两个辅助节点listHead(链表头节点)、listTail(链表尾节点)。事实上,二叉树只是换了种形式的链表;listHead用于记录链表的头节点,用于最后算法的返回;listTail用于定位当前需要更改指向的节点。了解了listHead和listTail的作用,代码理解起来至少顺畅80%。
4.提供我画的算法的过程图,有点丑,但有助于理解(帮你们画了,你们就不用画啦),另外图中右上角步骤三应该是“2”标红,“2”和“1”中间的连接为单线黑~~~
class Solution:
def __init__(self):
self.listHead = None
self.listTail = None
def Convert(self, pRootOfTree):
if pRootOfTree==None:
return
self.Convert(pRootOfTree.left)
if self.listHead==None:
self.listHead = pRootOfTree
self.listTail = pRootOfTree
else:
self.listTail.right = pRootOfTree
pRootOfTree.left = self.listTail
self.listTail = pRootOfTree
self.Convert(pRootOfTree.right)
return self.listHead