前言
记录 LeetCode 刷题中遇到的二叉树相关题目,第六篇
222.完全二叉树的节点个数
递归函数计算以某个节点为根的树的节点个数:如果节点为空,节点数自然为 0;否则节点数就应等于当前的节点 1 加上左右子树的节点树之和
public int countNodes(TreeNode root) {
if(root == null) return 0;
return 1 + countNodes(root.left) + countNodes(root.right);
}
剑指 Offer 32 - III. 从上到下打印二叉树 III
在层次遍历二到基础上,加一个当前层次要从左往右还是从右往左的布尔标记,如果是从左往右,就要按照尾插的方式插入列表;如果是从右往左,就按照头插的方式插入列表,得到的列表就是当前这一层的遍历序列
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null) return new LinkedList<>();
List<List<Integer>> res = new LinkedList<>();
Deque<TreeNode> queue = new LinkedList<>();
queue.addLast(root);
//right标记,true时为从左向右;false时从右往左
boolean right = true;
while(!queue.isEmpty()){
int size = queue.size();
LinkedList<Integer> l = new LinkedList<>();
for(int i = 0;i < size;i++){
TreeNode poll = queue.poll();
if(poll.left != null) queue.addLast(poll.left);
if(poll.right != null) queue.addLast(poll.right);
//从左往右时尾插,否则头插
if(right) l.addLast(poll.val);
else l.addFirst(poll.val);
}
res.add(l);
//right取反
right = !right;
}
return res;
}
226. 翻转二叉树
递归翻转左子树右子树,然后再将当前节点的左右儿子交换即可
public TreeNode invertTree(TreeNode root) {
if(root == null) return null; //递归终点
invertTree(root.left);
invertTree(root.right);
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
return root;
}
987. 二叉树的垂序遍历
思路来自官方题解
public List<List<Integer>> verticalTraversal(TreeNode root) {
//存储每个节点的信息,node[0]表示节点的row,node[1]表示节点的col,node[2]表示节点的值
List<int[]> nodes = new ArrayList<int[]>();
dfs(root,0,0,nodes);
//对于每个节点的信息,优先按照列升序排序,其次是行,最后是节点值
Collections.sort(nodes,(node1,node2) -> {
if (node1[1] != node2[1]) {
return node1[1] - node2[1];
}else if (node1[0] != node2[0]) {
return node1[0] - node2[0];
}else {
return node1[2] - node2[2];
}
}
);
List<List<Integer>> ans = new ArrayList<List<Integer>>();
int index = -1; //记录ans中最新的元素
int lastcol = Integer.MAX_VALUE; //记录ans中最新的元素所对应的列值
for (int[] node : nodes) {
int row = node[0], col = node[1], value = node[2];
//如果当前节点的列跟ans中最新的元素对应的列值相等,则将该节点值也放入这个元素,否则创建一个新的列表
//ans的元素即为每个列对应的节点的列表
if (col != lastcol){
lastcol = col;
ans.add(new ArrayList<>());
index++;
}
ans.get(index).add(value);
}
return ans;
}
//dfs搜索并记录每个节点的信息
public void dfs(TreeNode node, int row, int col, List<int[]> nodes) {
if (node == null) {
return;
}
nodes.add(new int[]{row, col, node.val});
dfs(node.left, row + 1, col - 1, nodes);
dfs(node.right, row + 1, col + 1, nodes);
}
297. 二叉树的序列化与反序列化
将二叉树先序遍历得到序列串作为序列化的结果,每个节点之间使用 ‘,’ 分隔,null 用 “null” 表示
在反序列化时,使用 String::split 方法将序列串以 ‘,’ 分隔开,按照先序遍历的顺序把每个节点的值重构为二叉树即可
public class Codec {
public String serialize(TreeNode root) {
if(root == null){
return "null";
}
//先序遍历
return root.val + "," + serialize(root.left) + "," + serialize(root.right);
}
public TreeNode deserialize(String data) {
LinkedList<String> queue = new LinkedList<>(Arrays.asList(data.split(",")));
return preorderToTree(queue);
}
//将先序遍历序列重构为二叉树
private TreeNode preorderToTree(Queue<String> queue) {
String val = queue.poll();
if("null".equals(val)){
return null;
}
TreeNode root = new TreeNode(Integer.parseInt(val));
root.left = preorderToTree(queue);
root.right = preorderToTree(queue);
return root;
}
}
129. 求根节点到叶节点数字之和
这道题使用回溯的做法:使用 res 记录当前得到的数字之和,StringBuilder 变量 cur 记录当前得到的数字。当当前节点是叶子节点时,就把得到的 cur 转化为 int 然后累加到 res 中
遍历下一层的节点后回到当前节点时要回退,把 cur 最后一位数字删掉
class Solution {
Integer res;
StringBuilder cur;
public int sumNumbers(TreeNode root) {
res = 0;
cur = new StringBuilder();
backTrack(root);
return res;
}
public void backTrack(TreeNode root){
if(root == null) return;
cur.append(root.val);
if(root.left == null && root.right == null){
res += Integer.valueOf(cur.toString());
return;
}
if(root.left != null) {
backTrack(root.left);
//回退
cur.deleteCharAt(cur.length() - 1);
}
if(root.right != null) {
backTrack(root.right);
//回退
cur.deleteCharAt(cur.length() - 1);
}
}
}
543. 二叉树的直径
看完题意很直观地可以想到跟节点的深度有关:每个节点左儿子的最大深度跟右儿子的最大深度的和就是这个节点所对应的最大直径。
所以我们需要计算每个节点左右儿子的最大深度。而在计算某个节点的最大深度的时候,又不可避免地需要计算左右儿子的最大深度才能得到该节点的最大深度,所以我们可以使用一个递归函数来计算节点的最大深度,在其中计算出左右儿子的最大深度时,维护更新最大直径的值。当所有节点的最大深度都计算完了,也就能得到最大直径的值了
class Solution {
int res;
public int diameterOfBinaryTree(TreeNode root) {
maxDepth(root);
return res;
}
public int maxDepth(TreeNode root) {
if(root == null) return 0;
//得到左右子树的最大深度
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
//然后就可以更新整个子树最大直径的值
res = Math.max(res,leftDepth + rightDepth);
//最后再返回当前节点的最大深度
return 1 + Math.max(leftDepth,rightDepth);
}
}