常用方法总结:
- 递归遍历:(前序中序后序层序)
层序遍历:层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑 - 迭代遍历:用栈实现二叉树的前后中序遍历
例一:104. 二叉树的最大深度(递归法、层序遍历法、前序遍历法)
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
分别用递归法、层序遍历法、前序遍历法解答如下:
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
#递归法
#1.确定递归函数的参数和返回值(root和depth) 2.确定终止条件:如果为空节点的话,就返回0,表示高度为0。
#3.确定单层递归的逻辑:先求它的左子树的深度,再求的右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
depth=0
def depth(root,d):
if not root:
return 0
ldepth=depth(root.left,depth)
rdepth=depth(root.right,depth)
d=1+max(ldepth,rdepth)
return d
return depth(root,depth)
#层序遍历法
#层序遍历,每到一层加1
if not root:
return 0
from collections import deque
queue=deque([root])
depth=0
while queue:
size=len(queue)
depth+=1
for _ in range(size):
cur=queue.popleft()
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
return depth
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def __init__(self):
root=TreeNode(None)
self.result=0
def getdepth(self,root,depth):
self.result=depth if depth>self.result else self.result #中
# print(result)
if root.left==None and root.right==None:
return
if root.left:
self.getdepth(root.left,depth+1) #左
if root.right:
self.getdepth(root.right,depth+1) #右
return
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
##前序遍历
self.result=0
if not root:
return 0
self.getdepth(root,1)
return self.result
例二:559. N 叉树的最大深度(递归法、层序遍历)
给定一个 N 叉树,找到其最大深度。
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。
"""
# Definition for a Node.
class Node(object):
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution(object):
def maxDepth(self, root):
"""
:type root: Node
:rtype: int
"""
## 递归法
if not root:
return 0
depth=0
for i in range(len(root.children)):
depth=max(depth,self.maxDepth(root.children[i])) #最大深度等于各个子树中最大的深度 最后加1
return depth+1 #回溯时期执行加和操作
##层序遍历
if not root:
return 0
from collections import deque
queue=deque([root])
result=0
while queue:
size=len(queue)
result+=1
for i in range(size):
cur=queue.popleft()
# print(cur)
for i in range(len(cur.children)):
queue.append(cur.children[i])
return result
例三:144. 二叉树的前序遍历(递归遍历、)
前序遍历:中左右,递归调用traversal()函数。
过程简述:每次调用traversal()函数时,先判断是否遍历到的节点是否空了,如果是,返回到上一次traversal()的调用。traversal()函数先把root.val收集到res中,之后先对root的左子节点做同样操作(root.left作为新的root,收集到res中,再将root.left.left作为新的root,如果其存在的话),最后对右子节点做同样操作(从最左侧底部的右子节点开始)
class Solution(object):
def preorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
def traversal(root):
if root==None:
return
res.append(root.val) #中
traversal(root.left) #左
traversal(root.right) #右
res=[]
traversal(root,res)
return res
例四:剑指 Offer 07. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def buildTree(self, preorder, inorder):
"""
:type preorder: List[int]
:type inorder: List[int]
:rtype: TreeNode
"""
def myBuildTree(preorder_left, preorder_right, inorder_left, inorder_right): #用指针在preorder和inorder数组中定位
if preorder_left > preorder_right:
return None
# 前序遍历中的第一个节点就是根节点
preorder_root = preorder_left
# 在中序遍历中定位根节点
inorder_root = index[preorder[preorder_root]] #关键信息:输入的前序遍历和中序遍历的结果中都不含重复的数字
# 先把根节点建立出来
root = TreeNode(preorder[preorder_root])
# 得到左子树中的节点数目
size_left_subtree = inorder_root - inorder_left
# 递归地构造左子树,并连接到根节点
# 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root.left = myBuildTree(preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1) #root.left是建立出来的一个子树
# 递归地构造右子树,并连接到根节点
# 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = myBuildTree(preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right)
return root
n = len(preorder)
# 构造哈希映射,帮助我们快速定位根节点
index = {element: i for i, element in enumerate(inorder)} #key:value
return myBuildTree(0, n - 1, 0, n - 1)
例五:101. 对称二叉树
给你一个二叉树的根节点 root , 检查它是否轴对称。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
# if not root:
# return True
# def symmetric(rootl,rootr):
# if not rootl and not rootr:
# return True
# if not rootl or not rootr:
# return False
# if rootl.val!=rootr.val:
# return False
# outside=symmetric(rootl.left,rootr.right)
# inside=symmetric(rootr.left,rootl.right)
# return outside and inside
# return symmetric(root.left,root.right)
##使用栈
from collections import deque
if not root:
return True
queue = collections.deque()
queue.append(root.left) #将左子树头结点加入队列
queue.append(root.right) #将右子树头结点加入队列
while queue: #接下来就要判断这这两个树是否相互翻转
leftNode = queue.popleft()
rightNode = queue.popleft()
if not leftNode and not rightNode: #左节点为空、右节点为空,此时说明是对称的
continue
#左右一个节点不为空,或者都不为空但数值不相同,返回false
if not leftNode or not rightNode or leftNode.val != rightNode.val:
return False
queue.append(leftNode.left) #加入左节点左孩子
queue.append(rightNode.right) #加入右节点右孩子
queue.append(leftNode.right) #加入左节点右孩子
queue.append(rightNode.left) #加入右节点左孩子
return True
例六:257. 二叉树的所有路径
给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def binaryTreePaths(self, root):
"""
:type root: TreeNode
:rtype: List[str]
"""
##递归法:前序遍历
path=''
result=[]
if not root:
return result
def dfs(root,path,result):
# if not root:
# return
path+=str(root.val)
if not root.left and not root.right:
result.append(path)
return
if root.left:
dfs(root.left,path+"->",result)
if root.right:
dfs(root.right,path+"->",result)
dfs(root,path,result)
return result
##迭代法
# 题目中节点数至少为1
stack, path_st, result = deque([root]), deque(), []
path_st.append(str(root.val))
while stack:
cur = stack.pop()
path = path_st.pop()
# 如果当前节点为叶子节点,添加路径到结果中
if not (cur.left or cur.right):
result.append(path)
if cur.right:
stack.append(cur.right)
path_st.append(path + '->' + str(cur.right.val))
if cur.left:
stack.append(cur.left)
path_st.append(path + '->' + str(cur.left.val))
##递归的底层实现就是栈,这里要注意由于是前序遍历(深度、从根到叶),所以要先压入右节点再压入左节点
return result
分析:递归+回溯
这题的特点是递归的解法中隐藏了回溯的逻辑
貌似表面没有看到回溯的逻辑,其实不然,回溯就隐藏在traversal(cur->left, path + “->”, result);中的 path + “->”。 每次函数调用完,path依然是没有加上"->" 的,这就是回溯了。
当然在函数定义的时候void traversal(TreeNode* cur, string path, vector& result) ,定义的是string path,每次都是复制赋值,不用使用引用,否则就无法做到回溯的效果。它的写法相当于:
if (cur->left) {
path += "->";
traversal(cur->left, path, result); // 左
path.pop_back(); // 回溯,抛掉val
path.pop_back(); // 回溯,抛掉->
}
if (cur->right) {
path += "->";
traversal(cur->right, path, result); // 右
path.pop_back(); // 回溯(非必要)
path.pop_back();
}
例七:404. 左叶子之和
给定二叉树的根节点 root ,返回所有左叶子之和。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def sumOfLeftLeaves(self, root):
"""
:type root: TreeNode
:rtype: int
"""
#本题我们要通过节点的父节点判断本节点的属性。
#判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。
#递归
#前序遍历,把所有的结果暂存在数组中
if not root:
return 0
res=[]
def dfs(root,res):
if root.left and not root.left.left and not root.left.right:
res.append(root.left.val)
if root.left:
dfs(root.left,res)
if root.right:
dfs(root.right,res)
dfs(root,res)
return sum(res)
## 后序遍历(左右中),不暂存结果,通过递归函数的返回值来累加求取左叶子数值之和。。
if not root:
return 0
left_left_leaves_sum = self.sumOfLeftLeaves(root.left) # 左
right_left_leaves_sum = self.sumOfLeftLeaves(root.right) # 右
cur_left_leaf_val = 0
if root.left and not root.left.left and not root.left.right:
cur_left_leaf_val = root.left.val
return cur_left_leaf_val + left_left_leaves_sum + right_left_leaves_sum # 中
例八:513. 找树左下角的值
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
递归法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
//这段代码凭借前序遍历,首先leftLen>maxLen所以最后的答案肯定是最后一层的节点,又由于先遍历的左,所以最后得到的答案是最左边节点的值
//得到最底层、最左边 节点值
class Solution {
public:
int maxLen=INT_MIN;
int maxleftval;
void traversal(TreeNode* root,int leftLen){
if(root->left==nullptr and root->right==nullptr){
if(leftLen>maxLen){
maxLen=leftLen;
maxleftval=root->val;
}
return;
}
if(root->left){
leftLen++;
traversal(root->left,leftLen);
leftLen--;//回溯
}
if(root->right){
leftLen++;
traversal(root->right,leftLen);
leftLen--;
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root,0);
return maxleftval;
}
};
层序遍历法
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def findBottomLeftValue(self, root):
"""
:type root: TreeNode
:rtype: int
"""
#递归法
#在树的最后一行找到最左边的值
#最底层 最左边 节点
#层序遍历,最后一层的第一个
if not root:
return None
from collections import deque
queue=deque([root])
res=0
while queue:
leng=len(queue)
for i in range(leng):
tmp=queue.popleft()
if i==0:
res=tmp.val
if tmp.left:
queue.append(tmp.left)
if tmp.right:
queue.append(tmp.right)
return res
例九:112. 路径总和
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。
递归法:
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def hasPathSum(self, root, targetSum):
"""
:type root: TreeNode
:type targetSum: int
:rtype: bool
"""
def dfs(root,tmp):
if not root:
return False
tmp+=root.val
if not root.left and not root.right and tmp==targetSum:
return True
if dfs(root.left,tmp) or dfs(root.right,tmp): ##找到即时返回,不再做无谓的寻找
return True
return False
return dfs(root,0)
迭代法:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if(root==nullptr) return false;
stack<pair<TreeNode*,int>> st;
st.push(pair<TreeNode*,int>(root,root->val));
while(!st.empty()){
pair<TreeNode*,int> node=st.top();
st.pop();
if(!node.first->left && !node.first->right && targetSum==node.second) return true;
if(node.first->right){
st.push(pair<TreeNode*,int>(node.first->right,node.second+node.first->right->val));
}
if(node.first->left){
st.push(pair<TreeNode*,int>(node.first->left,node.second+node.first->left->val));
}
}
return false;
}
};
例十:113. 路径总和 II
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
class Solution(object):
def pathSum(self, root, targetSum):
"""
:type root: TreeNode
:type targetSum: int
:rtype: List[List[int]]
"""
res=[]
if not root:
return res
def dfs(root,tmp):
# summ+=root.val
tmp.append(root.val)
if not root.left and not root.right and sum(tmp)==targetSum:
res.append(tmp[:])
return
if root.left:
dfs(root.left,tmp)
tmp.pop() ##回溯
if root.right:
dfs(root.right,tmp)
tmp.pop() ##回溯
dfs(root,[])
return res
具体步骤:
例十一:106. 从中序与后序遍历序列构造二叉树
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
(具体步骤的说明见本篇例四)
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def buildTree(self, inorder, postorder):
"""
:type inorder: List[int]
:type postorder: List[int]
:rtype: TreeNode
"""
#inorder 和 postorder 都由 不同 的值组成
def mybuildTree(postorder_left,postorder_right,inorder_left,inorder_right):
if postorder_left>postorder_right:
return None
postorder_root=postorder_right
inorder_root=index[postorder[postorder_root]] ## 在中序遍历中定位根节点
root=TreeNode(postorder[postorder_root])
size_left_subtree = inorder_root - inorder_left
root.left=mybuildTree(postorder_left,postorder_left+size_left_subtree-1,inorder_left,inorder_root-1)
root.right=mybuildTree(postorder_left+size_left_subtree,postorder_right-1,inorder_root+1,inorder_right)
return root
n=len(postorder)
index={element:i for i,element in enumerate(inorder)}## 构造哈希映射,帮助我们快速定位根节点
return mybuildTree(0,n-1,0,n-1)
例十二:654. 最大二叉树
给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
创建一个根节点,其值为 nums 中的最大值。
递归地在最大值 左边 的 子数组前缀上 构建左子树。
递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums 构建的 最大二叉树 。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def constructMaximumBinaryTree(self, nums):
"""
:type nums: List[int]
:rtype: TreeNode
"""
#不重复的整数数组 nums
# 注意类似用数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下标索引直接在原数组上操作,这样可以节约时间和空间上的开销。
def mymaxTree(left,right):
if left>right: ##由于是左闭右闭区间,left==right可以成立,此时区间内只有一个值
return None
rootn=max(nums[left:right+1])
nums_root=index[rootn]
root=TreeNode(rootn)
root.left=mymaxTree(left,nums_root-1) ##这里我定义的是左闭右闭区间,也即[left,nums_root-1]内的下标都取到
root.right=mymaxTree(nums_root+1,right)
return root
n=len(nums)
index={element:i for i,element in enumerate(nums)}
return mymaxTree(0,n-1)
例十三:617. 合并二叉树
给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def mergeTrees(self, root1, root2):
"""
:type root1: TreeNode
:type root2: TreeNode
:rtype: TreeNode
"""
def mergeTree(root11,root22,root3):
if not root11:
return root22
if not root22:
return root11
root3=TreeNode(root11.val+root22.val)
root3.left=mergeTree(root11.left,root22.left,root3)
root3.right=mergeTree(root11.right,root22.right,root3)
return root3
return mergeTree(root1,root2,None)##root3初始设为None
可以简化代码:
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def mergeTrees(self, root1, root2):
"""
:type root1: TreeNode
:type root2: TreeNode
:rtype: TreeNode
"""
if not root1:
return root2
if not root2:
return root1
root3=TreeNode(root1.val+root2.val)
root3.left=self.mergeTrees(root1.left,root2.left)
root3.right=self.mergeTrees(root1.right,root2.right)
return root3
例十四:700. 二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点 root 和一个整数值 val。
你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def searchBST(self, root, val):
"""
:type root: TreeNode
:type val: int
:rtype: TreeNode
"""
if not root:
return None ##返回类型是TreeNode,所以不能返回[],只能返回None
if root.val==val:
return root
elif root.val>val:
res=self.searchBST(root.left,val)
else:
res=self.searchBST(root.right,val)
return res
补充:145
为了解上述代码的具体运行流程,最好debug一遍,比如例一的完整代码如下:
import json
class TreeNode(object):
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution(object):
def preorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
def traversal(root, res):
if root == None:
return
res.append(root.val) # 中
traversal(root.left, res) # 左
traversal(root.right, res) # 右
res = []
traversal(root, res)
return res
def stringToTreeNode(input):
input = input.strip()
input = input[1:-1]
if not input:
return None
inputValues = [s.strip() for s in input.split(',')]
root = TreeNode(int(inputValues[0]))
nodeQueue = [root]
front = 0
index = 1
while index < len(inputValues):
node = nodeQueue[front]
front = front + 1
item = inputValues[index]
index = index + 1
if item != "null":
leftNumber = int(item)
node.left = TreeNode(leftNumber)
nodeQueue.append(node.left)
if index >= len(inputValues):
break
item = inputValues[index]
index = index + 1
if item != "null":
rightNumber = int(item)
node.right = TreeNode(rightNumber)
nodeQueue.append(node.right)
return root
def integerListToString(nums, len_of_list=None):
if not len_of_list:
len_of_list = len(nums)
return json.dumps(nums[:len_of_list])
def main():
import sys
def readlines():
for line in sys.stdin:
yield line.strip('\n')
lines = readlines()
while True:
try:
line = lines.__next__()
root = stringToTreeNode(line)
ret = Solution().preorderTraversal(root)
out = integerListToString(ret)
print(out)
except StopIteration:
break
if __name__ == '__main__':
main()