CONTENTS
- LeetCode 101. 对称二叉树(简单)
- LeetCode 102. 二叉树的层序遍历(中等)
- LeetCode 103. 二叉树的锯齿形层序遍历(中等)
- LeetCode 104. 二叉树的最大深度(简单)
- LeetCode 105. 从前序与中序遍历序列构造二叉树(中等)
LeetCode 101. 对称二叉树(简单)
【题目描述】
给你一个二叉树的根节点 root
,检查它是否轴对称。
【示例 1】
输入:root = [1,2,2,3,4,4,3]
输出:true
【示例 2】
输入:root = [1,2,2,null,3,null,3]
输出:false
【提示】
树中节点数目在范围
[
1
,
1000
]
[1, 1000]
[1,1000] 内
−
100
<
=
N
o
d
e
.
v
a
l
<
=
100
-100 <= Node.val <= 100
−100<=Node.val<=100
进阶:你可以运用递归和迭代两种方法解决这个问题吗?
【分析】
对称的树具有以下属性之一:
- 左右子节点均为空;
- 左右子节点的值相同,且左子节点的左子树与右子节点的右子树相同,左子节点的右子树与右子节点的左子树相同。
因此我们可以递归判断左右子树是否对称。
题目提到用递归和迭代实现,那么如何用迭代实现?
可以用队列维护对称的相对关系,首先将根节点的左右子节点入队,然后不断循环每次从队列中取两个节点,判断这两个节点是否对称,然后将其中一个节点的左/右子节点与另一个节点的右/左子节点对应成两组分别加入到队列中,如果队列为空遍历完整棵树还没发现非对称的节点说明整棵树就是对称的。
【代码】
【递归方法】
/**
* 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 isSymmetric(TreeNode* root) {
return dfs(root->left, root->right);
}
bool dfs(TreeNode* l, TreeNode* r) {
if (!l && !r) return true; // 两个节点均为空
if (!l || !r || l->val != r->val) return false; // 只有一个节点为空或两个节点值不同
return dfs(l->left, r->right) && dfs(l->right, r->left); // 左节点的左/右子树要和右节点的右/左子树对称
}
};
【迭代方法】
/**
* 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 isSymmetric(TreeNode* root) {
queue<TreeNode*> Q;
Q.push(root->left), Q.push(root->right);
while (!Q.empty()) {
auto p = Q.front(); Q.pop();
auto q = Q.front(); Q.pop();
if (!p && !q) continue;
if (!p || !q || p->val != q->val) return false; // 不对称
Q.push(p->left), Q.push(q->right); // p 的左子节点需要和 q 的右子节点对称
Q.push(p->right), Q.push(q->left); // p 的右子节点需要和 q 的左子节点对称
}
return true; // 全遍历完了说明树是对称的
}
};
LeetCode 102. 二叉树的层序遍历(中等)
【题目描述】
给你二叉树的根节点 r o o t root root,返回其节点值的层序遍历。(即逐层地,从左到右访问所有节点)。
【示例 1】
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
【示例 2】
输入:root = [1]
输出:[[1]]
【示例 3】
输入:root = []
输出:[]
【提示】
树中节点数目在范围
[
0
,
2000
]
[0, 2000]
[0,2000] 内
−
1000
<
=
N
o
d
e
.
v
a
l
<
=
1000
-1000 <= Node.val <= 1000
−1000<=Node.val<=1000
【分析】
层序遍历就用一个队列按 BFS 序遍历就行,由于答案需要将每一层的节点分别形成一组值,因此每轮都从队列中出队当前层的所有节点。
【代码】
/**
* 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:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> Q;
if (root) Q.push(root);
while (!Q.empty()) {
vector<int> v; // 当前层的所有节点值
int cnt = Q.size(); // 当前层的节点数
while (cnt--) { // 逐个出队当前层的所有节点
TreeNode* t = Q.front();
Q.pop();
v.push_back(t->val);
if (t->left) Q.push(t->left);
if (t->right) Q.push(t->right);
}
res.push_back(v);
}
return res;
}
};
LeetCode 103. 二叉树的锯齿形层序遍历(中等)
【题目描述】
给你二叉树的根节点 root
,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
【示例 1】
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]
【示例 2】
输入:root = [1]
输出:[[1]]
【示例 3】
输入:root = []
输出:[]
【提示】
树中节点数目在范围
[
0
,
2000
]
[0, 2000]
[0,2000] 内
−
100
<
=
N
o
d
e
.
v
a
l
<
=
100
-100 <= Node.val <= 100
−100<=Node.val<=100
【分析】
和上一题一样,只需要额外记录一下当前层的结果是否需要翻转即可。
【代码】
/**
* 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:
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> Q;
if (root) Q.push(root);
for (bool i = false; !Q.empty(); i ^= true) { // i 表示当前层是否需要翻转
vector<int> v;
int cnt = Q.size();
while (cnt--) {
TreeNode* t = Q.front();
Q.pop();
v.push_back(t->val);
if (t->left) Q.push(t->left);
if (t->right) Q.push(t->right);
}
if (i) reverse(v.begin(), v.end());
res.push_back(v);
}
return res;
}
};
LeetCode 104. 二叉树的最大深度(简单)
【题目描述】
给定一个二叉树 root
,返回其最大深度。
二叉树的最大深度是指从根节点到最远叶子节点的最长路径上的节点数。
【示例 1】
输入:root = [3,9,20,null,null,15,7]
输出:3
【示例 2】
输入:root = [1,null,2]
输出:2
【提示】
树中节点的数量在
[
0
,
1
0
4
]
[0, 10^4]
[0,104] 区间内。
−
100
<
=
N
o
d
e
.
v
a
l
<
=
100
-100 <= Node.val <= 100
−100<=Node.val<=100
【分析】
用 BFS 也就是类似前两题的做法能够求出层数,也可以直接用递归求解,从根节点往下递归求解每个节点的高度,到空节点了高度就为 0,否则当前节点的高度就是左子树与右子树中的最大高度加一(当前节点的高度比子树多 1)。
【代码】
/**
* 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:
int maxDepth(TreeNode* root) { // 返回 root 的最大高度
if (!root) return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
LeetCode 105. 从前序与中序遍历序列构造二叉树(中等)
【题目描述】
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历,inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
【示例 1】
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
【示例 2】
输入: preorder = [-1], inorder = [-1]
输出: [-1]
【提示】
1
<
=
p
r
e
o
r
d
e
r
.
l
e
n
g
t
h
<
=
3000
1 <= preorder.length <= 3000
1<=preorder.length<=3000
i
n
o
r
d
e
r
.
l
e
n
g
t
h
=
=
p
r
e
o
r
d
e
r
.
l
e
n
g
t
h
inorder.length == preorder.length
inorder.length==preorder.length
−
3000
<
=
p
r
e
o
r
d
e
r
[
i
]
,
i
n
o
r
d
e
r
[
i
]
<
=
3000
-3000 <= preorder[i], inorder[i] <= 3000
−3000<=preorder[i],inorder[i]<=3000
preorder
和 inorder
均无重复元素
inorder
均出现在 preorder
preorder
保证为二叉树的前序遍历序列
inorder
保证为二叉树的中序遍历序列
【分析】
根据前序或后序遍历与中序遍历构建二叉树是必须要掌握的基础知识,详细讲解可以转到:【UCB CS 61B SP24】Lecture 22 & 23: Tree and Graph Traversals, DFS, BFS。
简单概括思路就是前序遍历的第一个节点就是根节点,然后在中序遍历中找到根节点就能够区分出根节点的左右子树区间,根据左右子树节点数量再递归创建左右子树。
【代码】
/**
* 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:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return build(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1);
}
TreeNode* build(vector<int>& pre, int preSt, int preEd, vector<int>& in, int inSt, int inEd) {
if (preSt > preEd) return nullptr;
TreeNode* root = new TreeNode(pre[preSt]); // 根节点就是前序遍历的第一个节点
int k = inSt;
while (in[k] != pre[preSt]) k++; // 在中序遍历中找到根节点
int leftChildNum = k - inSt; // 左子树节点数
root->left = build(pre, preSt + 1, preSt + leftChildNum, in, inSt, k - 1);
root->right = build(pre, preSt + leftChildNum + 1, preEd, in, k + 1, inEd);
return root;
}
};