文章目录
- 基础篇
- 144. 二叉树的前序遍历 - 12.13
- 94. 二叉树的中序遍历 - 12.13
- 145. 二叉树的后序遍历 - 12.13
- 102. 二叉树的层序遍历 - 12.13
- *107. 二叉树的层序遍历 II - 12.14
- 199. 二叉树的右视图 - 12.14
- *637. 二叉树的层平均值 - 12.14
- 226. 翻转二叉树 - 12.15
- 589. N 叉树的前序遍历 - 12.15
- *101. 对称二叉树 - 12.15
- *104. 二叉树的最大深度 - 12.16
- 111. 二叉树的最小深度 - 12.17
- 543. 二叉树的直径 - 2.14(递归)
- *222. 完全二叉树的节点个数 - 12.18
- 700. 二叉搜索树中的搜索 - 12.19
- 701. 二叉搜索树中的插入操作 - 12.20
- 剑指Offer篇
- 450. 删除二叉搜索树中的节点 - 12.20
- 剑指 Offer 32 - I. 从上到下打印二叉树
- 剑指 Offer 32 - II. 从上到下打印二叉树 II
- 剑指 Offer 32 - III. 从上到下打印二叉树 III
- 剑指 Offer 26. 树的子结构
- 剑指 Offer 27. 二叉树的镜像
- 剑指 Offer 28. 对称的二叉树
- 剑指 Offer 34. 二叉树中和为某一值的路径
- 剑指 Offer 36. 二叉搜索树与双向链表
- 剑指 Offer 54. 二叉搜索树的第k大节点
- *剑指 Offer 55 - I. 二叉树的深度
- 剑指 Offer 55 - II. 平衡二叉树
- 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
- 剑指 Offer 68 - II. 二叉树的最近公共祖先
- 剑指 Offer 07. 根据前序和中序遍历重建二叉树
- 剑指 Offer 33. 二叉搜索树的后序遍历序列
- 算法小抄篇
- 226. 翻转二叉树 - 2.15(迭代 | 递归)
- 116. 填充每个节点的下一个右侧节点指针 - 2.15(递归)
- 114. 二叉树展开为链表 - 2.15(递归)
- 654. 最大二叉树 - 2.16(递归)
- 105. 从前序与中序遍历序列构造二叉树 - 2.16(递归)
- 106. 从中序与后序遍历序列构造二叉树 - 2.16(递归)
- 889. 根据前序和后序遍历构造二叉树 - 2.16(递归)
- 652. 寻找重复的子树 - 2.17(递归)
- 230. 二叉搜索树中第K小的元素 - 2.17
- 538. 把二叉搜索树转换为累加树 - 2.17
- 700. 二叉搜索树中的搜索 - 2.18(递归 | 比较)
- 98. 验证二叉搜索树 - 2.18(递归)
- 96. 不同的二叉搜索树 - 2.18(动态规划 | 递归)
- 95. 不同的二叉搜索树 II - 2.21(递归)
- 236. 二叉树的最近公共祖先 - 2.22(递归 | 后序遍历)
- 1373. 二叉搜索子树的最大键值和 - 2.22(递归 | 后序遍历)
- 297. 二叉树的序列化与反序列化 - 2.22(递归 | 前序遍历)
- 341. 扁平化嵌套列表迭代器 - 2.22(递归)
- 222. 完全二叉树的节点个数 - 2.23(递归)
- NC45 实现二叉树先序,中序和后序遍历 - 3.11
递归算法三要素
以前序遍历为例:
1、确定递归函数的参数和返回值:因为要打印出前序遍历节点的数值,所以参数里需要传入List在放节点的数值,除了这一点就不需要在处理什么数据了也不需要有返回值,所以递归函数返回类型就是void,代码如下:
void traversal(TreeNode root, List<Integer> result)
2、确定终止条件:在递归的过程中,如何算是递归结束了呢,当然是当前遍历的节点是空了,那么本层递归就要要结束了,所以如果当前遍历的这个节点是空,就直接return,代码如下:
if (root == NULL) return;
3、确定单层递归的逻辑:前序遍历是中左右的循序,所以在单层递归的逻辑,是要先取中节点的数值,代码如下:
result.add(root.val); // 注意这一句的位置
preOrder(root.left, result);
preOrder(root.right, result);
二叉树理论基础
二叉树的种类
在我们解题过程中二叉树有两种主要的形式:满二叉树
和完全二叉树
。
满二叉树
满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节点的二叉树。
完全二叉树
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^h -1 个节点。
优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系。
二叉搜索树
前面介绍的树,都没有数值的,而二叉搜索树是有数值的了,二叉搜索树是一个有序树。
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
下面这两棵树都是搜索树
平衡二叉搜索树
平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
最后一棵 不是平衡二叉树,因为它的左右两个子树的高度差的绝对值超过了1。
二叉树的存储方式
二叉树可以链式存储,也可以顺序存储。
那么链式存储方式就用指针, 顺序存储的方式就是用数组。
顾名思义就是顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在散落在各个地址的节点串联一起。
链式存储如图:
顺序存储的方式如图:
用数组来存储二叉树如何遍历的呢?
如果父节点的数组下表是i,那么它的左孩子就是i * 2 + 1,右孩子就是 i * 2 + 2。
但是用链式表示的二叉树,更有利于我们理解,所以一般我们都是用链式存储二叉树。
二叉树的遍历方式
二叉树主要有两种遍历方式:
- 深度优先遍历:先往深走,遇到叶子节点再往回走。
- 广度优先遍历:一层一层的去遍历。
那么从深度优先遍历和广度优先遍历进一步拓展,才有如下遍历方式:
二叉树的定义
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
如何遍历顺序?
-
涉及到二叉树的构造,无论普通二叉树还是二叉搜索树一定前序,都是先构造中节点。
-
求普通二叉树的属性,一般是后序,一般要通过递归函数的返回值做计算。
-
求二叉搜索树的属性,一定是中序了,要不白瞎了有序性了。
基础篇
144. 二叉树的前序遍历 - 12.13
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
解法一:递归实现,前序遍历:根左右
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
preOrder(root,result);
return result;
}
//前序遍历·递归
private void preOrder(TreeNode root, List<Integer> result){
if(root == null){
return;
}
result.add(root.val);
preOrder(root.left, result);
preOrder(root.right, result);
}
}
解法二:迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
// 前序遍历顺序:中-左-右,入栈顺序:中-右-左
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);
if (node.right != null){
stack.push(node.right);
}
if (node.left != null){
stack.push(node.left);
}
}
return result;
}
}
94. 二叉树的中序遍历 - 12.13
给定一个二叉树的根节点 root ,返回它的 中序 遍历。
解析:递归,中序遍历:左根右
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
inOrder(root,result);
return result;
}
//中序遍历
private void inOrder(TreeNode root, List<Integer> result){
if(root == null){
return;
}
inOrder(root.left,result);
result.add(root.val);
inOrder(root.right,result);
}
}
解法二:中序遍历,迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if(root == null) return result;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.isEmpty()){
if(cur != null){ // 通过指针来访问节点,访问到最底层
stack.push(cur); // 将访问的节点放进栈
cur = cur.left; // 左
}else{
cur = stack.pop(); // 从栈里弹出的数据,就是要处理的数据
result.add(cur.val); // 根
cur = cur.right; // 右
}
}
return result;
}
}
145. 二叉树的后序遍历 - 12.13
给定一个二叉树,返回它的 后序 遍历。
解法一:递归,后序遍历:左右根
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
postOrder(root,result);
return result;
}
//后序遍历
private void postOrder(TreeNode root, List<Integer> result){
if(root == null){
return;
}
postOrder(root.left,result);
postOrder(root.right,result);
result.add(root.val);
}
}
解法二:迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if(root == null) return result;
Stack<TreeNode> stack = new Stack<>();
//添加根节点
stack.push(root);
//循环
while(!stack.isEmpty()){
//取栈顶元素
TreeNode node = stack.pop(); //根
result.add(node.val);
if(node.left != null){ //左
stack.push(node.left);
}
if(node.right != null){ //右
stack.push(node.right);
}
}
//将结果反转
Collections.reverse(result);
return result;
}
}
102. 二叉树的层序遍历 - 12.13
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
解法一:BFS(广度优先遍历),迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
//定义返回的集合
List<List<Integer>> resList = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
BFS(root,0);
return resList;
}
//BFS(广度优先遍历)--迭代方式--借助队列
private void BFS(TreeNode node, Integer deep){
if(node == null) return;
Queue<TreeNode> queue = new LinkedList<>();
//添加根节点
queue.offer(node);
//循环
while(!queue.isEmpty()){
//保存每一层的数据
List<Integer> itemList = new ArrayList<>();
//计算队列的大小
int len = queue.size();
//循环
while(len > 0){
//获取队列的元素
TreeNode tempNode = queue.poll();
//添加进集合
itemList.add(tempNode.val);
//如果当前节点有左节点,就添加进队列
if(tempNode.left != null) queue.offer(tempNode.left);
//如果当前节点有右节点,就添加进队列
if(tempNode.right != null) queue.offer(tempNode.right);
//长度减一
len--;
}
//把每一层的结果添加进总集合
resList.add(itemList);
}
}
}
解法二:DFS(深度优先遍历),递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
//定义返回的集合
List<List<Integer>> resList = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
DFS(root,0);
return resList;
}
//DFS(深度优先遍历)--递归方式
private void DFS(TreeNode node, Integer deep){
if(node == null) return;
//深度加一
deep++;
if(resList.size() < deep){
//当层级增加时,list的Item也增加,利用list的索引值进行层级界定
List<Integer> itemList = new ArrayList<>();
resList.add(itemList);
}
//根据索引值获取元素
resList.get(deep-1).add(node.val);
DFS(node.left, deep); //左
DFS(node.right, deep); //右
}
}
*107. 二叉树的层序遍历 II - 12.14
给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
解析:使用BFS广度优先遍历,迭代实现,最后将集合反转再返回。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> resList = new ArrayList<>();
if(root == null) return resList;
//队列
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
List<Integer> itemList = new ArrayList<>();
int len = queue.size();
while(len > 0){
TreeNode node = queue.poll();
itemList.add(node.val);
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue.offer(node.right);
len--;
}
resList.add(itemList);
}
//反转集合
Collections.reverse(resList);
return resList;
}
}
199. 二叉树的右视图 - 12.14
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
解析:添加元素的时候,判断一下是否是每层的最后一个元素
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> resList = new ArrayList<>();
if(root == null) return resList;
//队列
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
//循环
while(!queue.isEmpty()){
int len = queue.size();
while(len > 0){
TreeNode node = queue.poll();
if(node.left != null){
queue.offer(node.left); //添加左
}
if(node.right != null){
queue.offer(node.right); //添加右
}
//如果是每一层中的最后一个元素,就添加
if(len == 1){
resList.add(node.val);
}
len--;
}
}
return resList;
}
}
*637. 二叉树的层平均值 - 12.14
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
解析:广度遍历时,将每层的元素累加,然后再算出平均值。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> resList = new ArrayList<>();
if(root == null) return resList;
//队列
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
double sum = 0.0;
int len = queue.size();
int num = len; //每层的个数
while(len > 0){
TreeNode node = queue.poll();
sum += node.val; //累加
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue.offer(node.right);
len--;
}
resList.add((double)(sum / num));
}
return resList;
}
}
226. 翻转二叉树 - 12.15
翻转一棵二叉树。
解法一:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null) return null;
//后序遍历
invertTree(root.left);
invertTree(root.right);
swapChildren(root);
return root;
}
//左右节点交换
private void swapChildren(TreeNode root){
TreeNode tempNode = root.left;
root.left = root.right;
root.right = tempNode;
}
}
解法二:迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null) return null;
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
while(len > 0){
TreeNode temp = que.poll();
swapChildren(temp); //交换
if(temp.left != null) que.offer(temp.left);
if(temp.right != null) que.offer(temp.right);
len--;
}
}
return root;
}
//左右节点交换
private void swapChildren(TreeNode root){
TreeNode tempNode = root.left;
root.left = root.right;
root.right = tempNode;
}
}
589. N 叉树的前序遍历 - 12.15
给定一个 N 叉树,返回其节点值的 前序遍历 。
N 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔(请参见示例)。
解法一:递归
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public List<Integer> preorder(Node root) {
List<Integer> resList = new ArrayList<>();
preOrder(root,resList);
return resList;
}
//前序遍历·递归
private void preOrder(Node root, List<Integer> result){
if(root == null){
return;
}
result.add(root.val); //根
for(Node node: root.children){ //各孩子节点
preOrder(node,result);
}
}
}
解法二:迭代
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public List<Integer> preorder(Node root) {
List<Integer> list = new ArrayList<>();
if(root == null) return list;
Stack<Node> stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
Node tempNode = stack.pop();
list.add(tempNode.val); //节点值
//栈 需要先添加右边的值
for(int i=tempNode.children.size()-1; i>=0; i--){
if(tempNode.children.get(i) != null){
stack.push(tempNode.children.get(i));
}
}
}
return list;
}
}
*101. 对称二叉树 - 12.15
给定一个二叉树,检查它是否是镜像对称的。
解法一:遍历根左右,根右左,再比较这两个是否完成相等。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
preOrder1(root,list1);
preOrder2(root,list2);
//遍历比较两个树
for(int i=0; i<list1.size(); i++){
if(list1.get(i) != list2.get(i)){
return false;
}
}
return true;
}
//根左右
private void preOrder1(TreeNode root, List<Integer> list){
if(root == null){
list.add(null);
return;
}
preOrder1(root.left,list);
preOrder1(root.right,list);
list.add(root.val);
}
//根右左
private void preOrder2(TreeNode root, List<Integer> list){
if(root == null){
list.add(null);
return;
}
preOrder2(root.right,list);
preOrder2(root.left,list);
list.add(root.val);
}
}
解法二:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
return compare(root.left,root.right);
}
private boolean compare(TreeNode left, TreeNode right) {
if (left == null && right != null) {
return false;
}
if (left != null && right == null) {
return false;
}
if (left == null && right == null) {
return true;
}
if (left.val != right.val) {
return false;
}
// 比较外侧
boolean compareOutside = compare(left.left, right.right);
// 比较内侧
boolean compareInside = compare(left.right, right.left);
return compareOutside && compareInside;
}
}
解法三:迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
Queue<TreeNode> que = new LinkedList<>();
que.offer(root.left);
que.offer(root.right);
while(!que.isEmpty()){
TreeNode leftNode = que.poll();
TreeNode rightNode = que.poll();
if(leftNode == null && rightNode == null){
continue;
}
if(leftNode == null && rightNode != null){
return false;
}
if(leftNode != null && rightNode == null){
return false;
}
if(leftNode.val != rightNode.val ){
return false;
}
//注意这里的顺序
que.offer(leftNode.left);
que.offer(rightNode.right);
que.offer(leftNode.right);
que.offer(rightNode.left);
}
return true;
}
}
*104. 二叉树的最大深度 - 12.16
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
解法一:迭代时,记录深度。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
int n = 0;
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
n++; //记录深度
int len = que.size();
while(len > 0){
TreeNode tempNode = que.poll();
if(tempNode.left != null) que.offer(tempNode.left);
if(tempNode.right != null) que.offer(tempNode.right);
len--;
}
}
return n;
}
}
解法二:后序遍历,递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
int leftDepth = maxDepth(root.left); //左
int rightDepth = maxDepth(root.right); //右
int rootDepth = 1 + Math.max(leftDepth,rightDepth); //中
return rootDepth;
}
}
111. 二叉树的最小深度 - 12.17
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
解析:层序遍历,迭代,判断是叶子节点时,就返回。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int minDepth(TreeNode root) {
if(root == null) return 0;
Deque<TreeNode> deque = new LinkedList<>();
deque.offer(root);
int depth = 0;
while (!deque.isEmpty()) {
int size = deque.size();
depth++; //记录深度
for (int i = 0; i < size; i++) {
TreeNode poll = deque.poll();
if (poll.left == null && poll.right == null) {
// 是叶子结点,直接返回depth,因为从上往下遍历,所以该值就是最小值
return depth;
}
if (poll.left != null) {
deque.offer(poll.left);
}
if (poll.right != null) {
deque.offer(poll.right);
}
}
}
return depth;
}
}
543. 二叉树的直径 - 2.14(递归)
解析:递归,求出每个节点的左子树+右子树深度之和,取最大值,就是直径长度最大值。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
//求出每个节点的左子树+右子树深度之和,取最大值,就是直径长度最大值
class Solution {
int res = 0; //最大长度
public int diameterOfBinaryTree(TreeNode root) {
maxDepth(root);
return res;
}
private int maxDepth(TreeNode root){
if(root == null) return 0;
int left = maxDepth(root.left); //左子树深度
int right = maxDepth(root.right); //右子树深度
int sum = left + right; //计算直径长度
res = Math.max(res, sum); //更新最大值
return 1 + Math.max(left, right); //获取最大深度
}
}
*222. 完全二叉树的节点个数 - 12.18
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
解法一:层序遍历时,记录节点的个数。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int countNodes(TreeNode root) {
int n = 0;
if(root == null) return n;
Deque<TreeNode> deque = new LinkedList<>();
deque.offer(root);
while(!deque.isEmpty()){
int len = deque.size();
while(len > 0){
TreeNode tempNode = deque.poll();
n++; //统计节点个数
if(tempNode.left != null) deque.offer(tempNode.left);
if(tempNode.right != null) deque.offer(tempNode.right);
len--;
}
}
return n;
}
}
解法二:递归,采用后序遍历。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int n = 0;
public int countNodes(TreeNode root) {
if(root == null) return n;
countNodes(root.left);
countNodes(root.right);
n++;
return n;
}
}
解法三:基于完全二叉树的性质
对 root 节点的左右子树进行高度统计,分别记为 left 和 right,有以下两种结果:
left == right。这说明,左子树一定是满二叉树,因为节点已经填充到右子树了,左子树必定已经填满了。所以左子树的节点总数我们可以直接得到,是 2^left - 1,加上当前这个 root 节点,则正好是 2^left。再对右子树进行递归统计。
left != right。说明此时最后一层不满,但倒数第二层已经满了,可以直接得到右子树的节点个数。同理,右子树节点 +root 节点,总数为 2^right。再对左子树进行递归查找。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int countNodes(TreeNode root) {
if(root == null) {
return 0;
}
int leftDepth = getDepth(root.left);
int rightDepth = getDepth(root.right);
if (leftDepth == rightDepth) {// 左子树是满二叉树
// 2^leftDepth其实是 (2^leftDepth - 1) + 1 ,左子树 + 根结点
return (1 << leftDepth) + countNodes(root.right);
} else {// 右子树是满二叉树
return (1 << rightDepth) + countNodes(root.left);
}
}
private int getDepth(TreeNode root) {
int depth = 0;
while (root != null) {
root = root.left;
depth++;
}
return depth;
}
}
700. 二叉搜索树中的搜索 - 12.19
给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
解法一:递归,利用二叉搜索树的性质,如果给定值 小于 当前节点,就往左字树搜索,否则就往右搜索。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if(root == null || root.val == val) return root;
//如果给定值 小于 当前节点,就往左字树搜索,否则就往右搜索。
if(val < root.val) return searchBST(root.left,val);
else return searchBST(root.right,val);
}
}
解法二:迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
while(root != null){
//如果给定值 小于 当前节点,就往左字树搜索,否则就往右搜索。
if(val < root.val) root = root.left;
else if(val > root.val) root = root.right;
else return root;
}
return root;
}
}
701. 二叉搜索树中的插入操作 - 12.20
给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
解法一:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) return new TreeNode(val); // 找到插入的位置
if (root.val == val) return root; // 节点已存在
// 插入的值比root的值大,插入到右子树上面
if (val > root.val) root.right = insertIntoBST(root.right, val);
// 插入的值比root的值小,插入到左子树上面
if (val < root.val) root.left = insertIntoBST(root.left, val);
return root;
}
}
解法二:迭代,使用迭代查找到待插入节点的父节点,然后比较插入左还是右。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) return new TreeNode(val);
TreeNode cur = root;
TreeNode parent = root; //记录上一个节点
//查找待插入的父节点位置
while(cur != null){
parent = cur; //记录上一个节点
if(val < cur.val) cur = cur.left; //向左查找
else cur = cur.right; //向右查找
}
//新建节点
TreeNode node = new TreeNode(val);
//判断把新节点插在左还是右
if(val < parent.val) parent.left = node;
else parent.right = node;
return root;
}
}
剑指Offer篇
450. 删除二叉搜索树中的节点 - 12.20
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
解析:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null)return null;
if (key > root.val)
root.right = deleteNode(root.right, key); // 去右子树删除
else if(key < root.val)
root.left = deleteNode(root.left, key); // 去左子树删除
else { // 当前节点就是要删除的节点
if (root.left == null) return root.right; // 情况1,欲删除节点无左子
else if (root.right == null) return root.left; // 情况2,欲删除节点无右子
else if (root.left!=null && root.right !=null){ // 情况3,欲删除节点左右子都有
TreeNode node = root.right;
while (node.left != null) // 寻找欲删除节点右子树的最左节点
node = node.left;
node.left = root.left; // 将欲删除节点的左子树成为其右子树的最左节点的左子树
root = root.right; // 欲删除节点的右子顶替其位置,节点被删除
}
}
return root;
}
}
剑指 Offer 32 - I. 从上到下打印二叉树
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
解析:层序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int[] levelOrder(TreeNode root) {
int[] arr = {};
if(root == null) return new int[0];
List<Integer> list = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
//遍历
while(!queue.isEmpty()){
int len = queue.size();
while(len > 0){
//获取元素
TreeNode node = queue.poll();
//添加元素的值
list.add(node.val);
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue.offer(node.right);
len--;
}
}
//list转int数组
int[] res = new int[list.size()];
for(int i = 0; i < list.size(); i++)
res[i] = list.get(i);
return res;
}
}
剑指 Offer 32 - II. 从上到下打印二叉树 II
剑指 Offer 32 - II. 从上到下打印二叉树 II
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
解析:层序遍历时添加每一层
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if(root == null) return res;
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
//保存每一层的结果
List<Integer> list = new ArrayList<>();
int len = que.size();
while(len > 0){
TreeNode node = que.poll();
list.add(node.val);
if(node.left != null) que.offer(node.left);
if(node.right != null) que.offer(node.right);
len--;
}
//添加每一层
res.add(list);
}
return res;
}
}
剑指 Offer 32 - III. 从上到下打印二叉树 III
剑指 Offer 32 - III. 从上到下打印二叉树 III
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
解析:层序遍历时,统计层数,如果当前是奇数层,就直接添加list,偶数层则先反转list后再添加。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if(root == null) return res;
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
int num = 1; //记录层数
while(!que.isEmpty()){
//保存每一层的结果
List<Integer> list = new ArrayList<>();
int len = que.size();
while(len > 0){
TreeNode node = que.poll();
list.add(node.val);
if(node.left != null) que.offer(node.left);
if(node.right != null) que.offer(node.right);
len--;
}
//添加每一层
if(num % 2 != 0){
//奇数行直接添加
res.add(list);
}else{
//如果是偶数行,先反转后再添加
Collections.reverse(list);
res.add(list);
}
num++;//层数加1
}
return res;
}
}
剑指 Offer 26. 树的子结构
解析:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
//约定空树不是任意一个树的子结构
if(A==null || B==null) return false;
//在A树中查找与B树一样的子结构
//1.直接考虑A和B是否是一样的
if(isSame(A, B)){
//如果直接在根节点处,就找到A和B一样的子结构,那直接返回true
return true;
}
//2.考虑A的左子树是否和B一样,或者A的右子树是否和B一样
return isSubStructure(A.left, B) || isSubStructure(A.right, B);
}
public boolean isSame(TreeNode t1, TreeNode t2){
//假如t1和t2是两棵树的根节点
//当t2为空时,说明已经查找完子树t2了,返回true
if(t2==null) return true;
//当t2不为空,t1为空时,说明t1不含有t2子树,返回false
if(t1==null) return false;
//当子树的节点不相同,说明不相等,返回false
if(t1.val!=t2.val) return false;
//否则,继续往下比较,注意这里是 &&
return isSame(t1.left, t2.left) && isSame(t1.right, t2.right);
}
}
剑指 Offer 27. 二叉树的镜像
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
解法一:后序遍历-递归,遍历左右节点后,反转左右节点。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null) return null;
//后序遍历
mirrorTree(root.left); //左
mirrorTree(root.right); //右
reverseTree(root); //根 反转
return root;
}
//反转左右节点
private void reverseTree(TreeNode root){
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
}
}
剑指 Offer 28. 对称的二叉树
剑指 Offer 28. 对称的二叉树
解析:递归分别比较里外两侧是否相等。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
return compare(root.left, root.right);
}
private boolean compare(TreeNode left, TreeNode right) {
if (left == null && right != null) {
return false;
}
if (left != null && right == null) {
return false;
}
if (left == null && right == null) {
return true;
}
if (left.val != right.val) {
return false;
}
// 比较外侧
boolean compareOutside = compare(left.left, right.right);
// 比较内侧
boolean compareInside = compare(left.right, right.left);
return compareOutside && compareInside;
}
}
剑指 Offer 34. 二叉树中和为某一值的路径
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
解析:回溯
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
LinkedList<List<Integer>> res = new LinkedList<>(); //结果
LinkedList<Integer> path = new LinkedList<>(); //路径
public List<List<Integer>> pathSum(TreeNode root, int target) {
recall(root,target);
return res;
}
public void recall(TreeNode root, int target){
if(root == null) return;
path.add(root.val); //单条路径添加值
target -= root.val; //做减法
//如果做减法后结果为0 并且 当前为叶子节点,就添加路径
if(target == 0 && root.left == null && root.right == null){
res.add(new LinkedList<>(path)); //添加路径
}
//回溯调用,查找左右子树
recall(root.left, target);
recall(root.right, target);
//撤销已处理的节点
path.removeLast();
}
}
剑指 Offer 36. 二叉搜索树与双向链表
剑指 Offer 36. 二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
解析:
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val,Node _left,Node _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
Node pre, head; //pre为前一个节点,head为头节点
public Node treeToDoublyList(Node root) {
if(root == null) return null;
dfs(root);
//经过dfs处理之后,pre就有指向了,然后进行头尾相连
head.left = pre;
pre.right = head;
//返回头节点
return head;
}
public void dfs(Node cur){
//递归结束条件
if(cur == null) return;
//左
dfs(cur.left);
//如果pre为空,就说明是第一个节点,头结点,然后用head保存头结点,用于之后的返回
if(pre == null) head = cur;
//如果pre不为空,那就说明是中间的节点。并且pre保存的是上一个节点,让上一个节点的右指针指向当前节点
else pre.right = cur;
//再让当前节点的左指针指向父节点,也就连成了双向链表
cur.left = pre;
//保存当前节点,用于下层递归创建
pre = cur;
//右
dfs(cur.right);
}
}
剑指 Offer 54. 二叉搜索树的第k大节点
解法一:先使用中序遍历存储元素,然后返回倒数第k大的节点值。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int kthLargest(TreeNode root, int k) {
if(root == null) return 0;
List<Integer> res = new ArrayList<>();
inOrder(root,res);
return res.get(res.size()-k); //获取第k大的节点值
}
//中序遍历
public void inOrder(TreeNode root, List<Integer> res){
if(root == null) return;
inOrder(root.left,res);
res.add(root.val);
inOrder(root.right,res);
}
}
优化后:遍历右根左,到倒数第k大值时就直接返回。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private int res = 0, n = 0;
public int kthLargest(TreeNode root, int k) {
this.n = k;
inOrder(root);
return res;
}
public void inOrder(TreeNode root){
//注意这里是先遍历右子树
if(root.right != null && n > 0) inOrder(root.right);
n--; //递减
if(n == 0) { //找到倒数第k大的值
res = root.val;
return;
}
if(root.left != null && n > 0) inOrder(root.left);
}
}
*剑指 Offer 55 - I. 二叉树的深度
解析:层序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
int n = 0; //树的深度
while(!que.isEmpty()){
n++;
int len = que.size();
while(len > 0){
TreeNode node = que.poll();
if(node.left != null) que.offer(node.left);
if(node.right != null) que.offer(node.right);
len--;
}
}
return n;
}
}
解法二:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
}
}
剑指 Offer 55 - II. 平衡二叉树
解析:递归计算出左右子树的高度,然后判断是否相差大于1。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
return dfs(root) >= 0;
}
public int dfs(TreeNode root){
if(root == null) return 0;
int leftHeight = dfs(root.left); //左子树高度
int rightHeight = dfs(root.right); //右子树高度
if(leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1){ //如果左右子树高度为-1,或者两者的差值大于1,就返回-1
return -1;
}else{
return Math.max(leftHeight, rightHeight) + 1; //树的深度
}
}
}
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
解析:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null;
if(p.val < root.val && q.val < root.val){ //往左搜索
return lowestCommonAncestor(root.left, p, q);
}
if(p.val > root.val && q.val > root.val){ //往右搜索
return lowestCommonAncestor(root.right, p, q);
}
return root; //如果出现p,q在一左一右,就直接返回当前的根节点
}
}
剑指 Offer 68 - II. 二叉树的最近公共祖先
解析:
三种情况
1、p q 一个在左子树 一个在右子树,那么当前节点即是最近公共祖先。
2、p q 都在左子树
3、p q 都在右子树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null;
//如果当前节点等于p或q,返回当前节点
if(root == p || root == q) return root;
//在左子树找
TreeNode left = lowestCommonAncestor(root.left, p, q);
//在右子树找
TreeNode right = lowestCommonAncestor(root.right, p, q);
// p q 一个在左,一个在右
if(left != null && right != null) return root;
// p q 都在左子树
if(left != null) return left;
// p q 都在右子树
if(right != null) return right;
return null;
}
}
剑指 Offer 07. 根据前序和中序遍历重建二叉树
解析:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
HashMap<Integer, Integer> map = new HashMap<>();//标记中序遍历
int[] preorder;//保留的先序遍历,方便递归时依据索引查看先序遍历的值
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
//将中序遍历的值及索引放在map中,方便递归时获取左子树与右子树的数量及其根的索引
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
//三个索引分别为
//当前根的的索引
//递归树的左边界,即数组左边界
//递归树的右边界,即数组右边界
return recur(0,0,inorder.length-1);
}
TreeNode recur(int pre_root, int in_left, int in_right){
if(in_left > in_right) return null;// 相等的话就是自己
TreeNode root = new TreeNode(preorder[pre_root]);//获取root节点
int idx = map.get(preorder[pre_root]);//获取在中序遍历中根节点所在索引,以方便获取左子树的数量
//左子树的根的索引为先序中的根节点+1
//递归左子树的左边界为原来的中序in_left
//递归左子树的右边界为中序中的根节点索引-1
root.left = recur(pre_root+1, in_left, idx-1);
//右子树的根的索引为先序中的 当前根位置 + 左子树的数量 + 1
//递归右子树的左边界为中序中当前根节点+1
//递归右子树的右边界为中序中原来右子树的边界
root.right = recur(pre_root + (idx - in_left) + 1, idx+1, in_right);
return root;
}
}
剑指 Offer 33. 二叉搜索树的后序遍历序列
解析:
class Solution {
public boolean verifyPostorder(int[] postorder) {
return recur(postorder, 0, postorder.length-1);
}
public boolean recur(int[] postorder, int left, int right){
//说明此子树节点数量≤1 ,无需判别正确性,因此直接返回 truetrue
if(left >= right) return true;
int temp = left;
while(postorder[temp] < postorder[right]) temp++; //找到第一个比根节点大的节点
int m = temp;
while(postorder[temp] > postorder[right]) temp++;
//temp = right : 判断 此树 是否正确。
//recur(postorder, left, m-1): 判断 此树的左子树 是否正确。
//recur(postorder, m, right-1) : 判断 此树的右子树 是否正确。
return temp == right && recur(postorder, left, m-1) && recur(postorder, m, right-1);
}
}
算法小抄篇
226. 翻转二叉树 - 2.15(迭代 | 递归)
翻转一棵二叉树。
解法一:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null) return null;
//后序遍历
invertTree(root.left);
invertTree(root.right);
swapChildren(root);
return root;
}
//左右节点交换
private void swapChildren(TreeNode root){
TreeNode tempNode = root.left;
root.left = root.right;
root.right = tempNode;
}
}
解法二:迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null) return null;
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
while(len > 0){
TreeNode temp = que.poll();
swapChildren(temp); //交换
if(temp.left != null) que.offer(temp.left);
if(temp.right != null) que.offer(temp.right);
len--;
}
}
return root;
}
//左右节点交换
private void swapChildren(TreeNode root){
TreeNode tempNode = root.left;
root.left = root.right;
root.right = tempNode;
}
}
116. 填充每个节点的下一个右侧节点指针 - 2.15(递归)
解析:【递归】传入两个节点,连接相同父节点的两个子节点,以及连接跨越父节点的两个子节点。
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/
class Solution {
public Node connect(Node root) {
if (root == null) return null;
connectTwoNode(root.left, root.right);
return root;
}
private void connectTwoNode(Node node1, Node node2){
if(node1 == null && node2 == null) return ;
//前序遍历,连接相邻两个节点
node1.next = node2;
//递归处理左右子树的节点
connectTwoNode(node1.left, node1.right); // 连接相同父节点的两个子节点
connectTwoNode(node2.left, node2.right); // 连接相同父节点的两个子节点
connectTwoNode(node1.right, node2.left); // 连接跨越父节点的两个子节点
}
}
114. 二叉树展开为链表 - 2.15(递归)
给你二叉树的根结点 root ,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
- 展开后的单链表应该与二叉树 先序遍历 顺序相同。
解析:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public void flatten(TreeNode root) {
if(root == null) return;
flatten(root.left); //拉直左子树
flatten(root.right); //拉直右子树
//保存左右子树
TreeNode leftTree = root.left;
TreeNode rightTree = root.right;
//将左子树设置为null,让左子树作为右子树
root.left = null;
root.right = leftTree;
//将原先的右子树接到当前右子树的末端
TreeNode temp = root;
while(temp.right != null){
temp = temp.right; //走到右子树末端
}
temp.right = rightTree; //拼接
}
}
654. 最大二叉树 - 2.16(递归)
解析:对于每个根节点,只需要找到当前 nums 中的最大值和对应的索引,然后递归调用左右数组构造左右子树即可。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return buildTree(nums, 0, nums.length-1);
}
private TreeNode buildTree(int[] nums, int leftIndex, int rightIndex){
if(leftIndex > rightIndex) return null;
//求最大值和记录最大值的下标
int index = -1;
int maxValue = Integer.MIN_VALUE;
for(int i=leftIndex; i<=rightIndex; i++){
if(maxValue < nums[i]){
maxValue = nums[i]; //更新最大值
index = i; //记录下标
}
}
//用最大值构建根节点
TreeNode root = new TreeNode(maxValue);
//构建左右子树
root.left = buildTree(nums, leftIndex, index-1);
root.right = buildTree(nums, index+1, rightIndex);
return root;
}
}
105. 从前序与中序遍历序列构造二叉树 - 2.16(递归)
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
解析:
int leftSize = index - inStart;
root.left = build(preorder, preStart + 1, preStart + leftSize,
inorder, inStart, index - 1);
root.right = build(preorder, preStart + leftSize + 1, preEnd,
inorder, index + 1, inEnd);
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buile(preorder, 0, preorder.length-1,
inorder, 0, inorder.length-1);
}
private TreeNode buile(int[] preorder, int preStart, int preEnd,
int[] inorder, int inStart, int inEnd) {
if(preStart > preEnd) return null;
int rootVal = preorder[preStart]; //root 节点对应的值就是前序遍历数组的第一个元素
int index = 0; //根节点rootVal在中序遍历的索引
//找到rootVal在中序遍历的索引,记录下来
for(int i=inStart; i<= inEnd; i++){
if(inorder[i] == rootVal){
index = i;
break;
}
}
//计算左子树元素个数
int leftSize = index - inStart;
//先构建根节点
TreeNode root = new TreeNode(rootVal);
//构建左右子树
root.left = buile(preorder, preStart + 1, preStart + leftSize,
inorder, inStart, index - 1);
root.right = buile(preorder, preStart + leftSize + 1, preEnd,
inorder, index + 1, inEnd);
return root;
}
}
106. 从中序与后序遍历序列构造二叉树 - 2.16(递归)
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
解析:
遍历顺序差异,导致了 postorder 和 inorder 数组中的元素分布有如下特点:
这道题和上一题的关键区别是,后序遍历和前序遍历相反,根节点对应的值为 postorder 的最后一个元素。
postoder 和 inorder 对应的状态如下:
int leftSize = index - inStart;
root.left = build(inorder, inStart, index - 1,
postorder, postStart, postStart + leftSize - 1);
root.right = build(inorder, index + 1, inEnd,
postorder, postStart + leftSize, postEnd - 1);
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
return buile(postorder, 0, postorder.length-1,
inorder, 0, inorder.length-1);
}
private TreeNode buile(int[] postorder, int postStart, int postEnd,
int[] inorder, int inStart, int inEnd) {
if(inStart > inEnd) return null;
int rootVal = postorder[postEnd]; //root 节点对应的值就是后序遍历数组的最后一个元素
int index = 0; //根节点rootVal在中序遍历的索引
//找到rootVal在中序遍历的索引,记录下来
for(int i=inStart; i<= inEnd; i++){
if(inorder[i] == rootVal){
index = i;
break;
}
}
//计算左子树元素个数
int leftSize = index - inStart;
//先构建根节点
TreeNode root = new TreeNode(rootVal);
//构建左右子树
root.left = buile(postorder, postStart, postStart + leftSize - 1,
inorder, inStart, index - 1);
root.right = buile(postorder, postStart + leftSize, postEnd - 1,
inorder, index + 1, inEnd);
return root;
}
}
889. 根据前序和后序遍历构造二叉树 - 2.16(递归)
给定两个整数数组,preorder 和 postorder ,其中 preorder 是一个具有 无重复 值的二叉树的前序遍历,postorder 是同一棵树的后序遍历,重构并返回二叉树。
如果存在多个答案,您可以返回其中 任何 一个。
解析:
1、首先把前序遍历结果的第一个元素或者后序遍历结果的最后一个元素确定为根节点的值。
2、然后把前序遍历结果的第二个元素作为左子树的根节点的值。
3、在后序遍历结果中寻找左子树根节点的值,从而确定了左子树的索引边界,进而确定右子树的索引边界,递归构造左右子树即可。
// 左子树的元素个数
int leftSize = index - postStart + 1;
// 先构造出当前根节点
TreeNode root = new TreeNode(rootVal);
// 递归构造左右子树
// 根据左子树的根节点索引和元素个数推导左右子树的索引边界
root.left = build(preorder, preStart + 1, preStart + leftSize,
postorder, postStart, index);
root.right = build(preorder, preStart + leftSize + 1, preEnd,
postorder, index + 1, postEnd - 1);
class Solution {
public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {
return build(preorder, 0, preorder.length - 1,
postorder, 0, postorder.length - 1);
}
// 定义:根据 preorder[preStart..preEnd] 和 postorder[postStart..postEnd]
// 构建二叉树,并返回根节点。
TreeNode build(int[] preorder, int preStart, int preEnd,
int[] postorder, int postStart, int postEnd) {
if (preStart > preEnd) {
return null;
}
if (preStart == preEnd) {
return new TreeNode(preorder[preStart]);
}
// root 节点对应的值就是前序遍历数组的第一个元素
int rootVal = preorder[preStart];
// root.left 的值是前序遍历第二个元素
// 通过前序和后序遍历构造二叉树的关键在于通过左子树的根节点
// 确定 preorder 和 postorder 中左右子树的元素区间
int leftRootVal = preorder[preStart + 1];
// leftRootVal 在后序遍历数组中的索引
int index = 0;
for (int i = postStart; i < postEnd; i++) {
if (postorder[i] == leftRootVal) {
index = i;
break;
}
}
// 左子树的元素个数
int leftSize = index - postStart + 1;
// 先构造出当前根节点
TreeNode root = new TreeNode(rootVal);
// 递归构造左右子树
// 根据左子树的根节点索引和元素个数推导左右子树的索引边界
root.left = build(preorder, preStart + 1, preStart + leftSize,
postorder, postStart, index);
root.right = build(preorder, preStart + leftSize + 1, preEnd,
postorder, index + 1, postEnd - 1);
return root;
}
}
652. 寻找重复的子树 - 2.17(递归)
给定一棵二叉树 root,返回所有重复的子树。
对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。
如果两棵树具有相同的结构和相同的结点值,则它们是重复的。
解析:递归+哈希map+List,后序遍历,将每个节点子树的结果用字符串保存在map中,并统计出现的次数;如果次数为2才添加入结果集中。
需要知道以下两点:
1、以我为根的这棵二叉树(子树)长啥样?
2、以其他节点为根的子树都长啥样?
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
LinkedList<TreeNode> res = new LinkedList<>(); //结果集,记录所有子树以及出现的次数
HashMap<String, Integer> map = new HashMap<>(); //记录重复的子树根节点
public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
traverse(root);
return res;
}
//辅助函数,后序遍历
private String traverse(TreeNode root){
if(root == null) return "#"; //如果节点为空,用“#”代替,便于字符串对比
String left = traverse(root.left);
String right = traverse(root.right);
//拼接子树,左右根
String tree = left + "," + right + "," + root.val;
//获取出现的次数
int freq = map.getOrDefault(tree, 1);
//多次重复也只会被加入结果集一次
if(freq == 2){
res.add(root);
}
//给子树对应的出现次数加1
map.put(tree, freq+1);
return tree;
}
}
230. 二叉搜索树中第K小的元素 - 2.17
解析:二叉搜索树(BST)的中序遍历其实就是升序排序的结果。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
//BST 的中序遍历其实就是升序排序的结果
class Solution {
int res = 0; //记录结果
int n = 0; //记录当前元素的排名,第 n 个最小元素
public int kthSmallest(TreeNode root, int k) {
traverse(root, k);
return res;
}
private void traverse(TreeNode root, int k){
if(root == null) return;
traverse(root.left, k); //左
n++; //第 n 小
if(n == k){
res = root.val; //中
}
traverse(root.right, k); //右
}
}
538. 把二叉搜索树转换为累加树 - 2.17
解析:利用二叉搜索树的中序遍历,先遍历右子树,中,左子树,便是降序排列,从右往左依次累加,然后将累加后的值赋值给每个节点即可。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int sum = 0; //记录结果
public TreeNode convertBST(TreeNode root) {
traverse(root);
return root;
}
private void traverse(TreeNode root){
if(root == null) return;
traverse(root.right); //先遍历右子树,使得结果降序
sum += root.val; //累加
root.val = sum; //将累加的结果赋值给当前节点
traverse(root.left); //左
}
}
700. 二叉搜索树中的搜索 - 2.18(递归 | 比较)
解法一:如果给定值 小于 当前节点,就往左字树搜索,否则就往右搜索。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
while(root != null){
//如果给定值 小于 当前节点,就往左字树搜索,否则就往右搜索。
if(val < root.val) root = root.left;
else if(val > root.val) root = root.right;
else return root;
}
return root;
}
}
解法二:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
//递归解法
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if(root == null) return null;
//如果当前值大于目标值,就到左子树查找
if(root.val > val) return searchBST(root.left, val);
//如果当前值小于目标值,就到右子树查找
if(root.val < val) return searchBST(root.right, val);
//相等返回root
return root;
}
}
98. 验证二叉搜索树 - 2.18(递归)
解析:通过使用辅助函数,增加函数参数列表,在参数中携带额外信息,将这种约束传递给子树的所有节点。
限定以 root 为根的子树节点必须满足 max.val > root.val > min.val
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isValidBST(TreeNode root) {
return isValidBST(root, null, null);
}
//限定以 root 为根的子树节点必须满足 max.val > root.val > min.val
private boolean isValidBST(TreeNode root, TreeNode min, TreeNode max){
if(root == null) return true;
// 若 root.val 不符合 max 和 min 的限制,说明不是合法 BST
if(min != null && root.val <= min.val ) return false;
if(max != null && root.val >= max.val) return false;
// 限定左子树的最大值是 root.val,右子树的最小值是 root.val
return isValidBST(root.left, min, root) &&
isValidBST(root.right, root, max);
}
}
96. 不同的二叉搜索树 - 2.18(动态规划 | 递归)
解法一:动态规划
class Solution {
public int numTrees(int n) {
int[] dp = new int[n+1];
dp[0] = 1;
dp[1] = 1;
for(int i=2; i<=n; i++){
for(int j=1; j<=i; j++){
dp[i] += dp[j-1] * dp[i-j];
}
}
return dp[n];
}
}
解法二:递归
// 备忘录
int[][] memo;
int numTrees(int n) {
// 备忘录的值初始化为 0
memo = new int[n + 1][n + 1];
return count(1, n);
}
int count(int lo, int hi) {
if (lo > hi) return 1;
// 查备忘录
if (memo[lo][hi] != 0) {
return memo[lo][hi];
}
int res = 0;
for (int mid = lo; mid <= hi; mid++) {
int left = count(lo, mid - 1);
int right = count(mid + 1, hi);
res += left * right;
}
// 将结果存入备忘录
memo[lo][hi] = res;
return res;
}
95. 不同的二叉搜索树 II - 2.21(递归)
解析:
1、穷举root节点的所有可能。
2、递归构造出左右子树的所有合法 BST。
3、给root节点穷举所有左右子树的组合。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<TreeNode> generateTrees(int n) {
if(n == 0) return new LinkedList<>();
// 构造闭区间 [1, n] 组成的 BST
return build(1, n);
}
// 构造闭区间 [left, right] 组成的 BST
private List<TreeNode> build(int left, int right){
List<TreeNode> res = new LinkedList<>();
if(left > right){
res.add(null);
return res;
}
// 1、穷举 root 节点的所有可能。
for(int i=left; i<=right; i++){
// 2、递归构造出左右子树的所有合法 BST。
List<TreeNode> leftTree = build(left, i-1);
List<TreeNode> rightTree = build(i+1, right);
// 3、给 root 节点穷举所有左右子树的组合。
for(TreeNode lTree: leftTree){
for(TreeNode rTree: rightTree){
TreeNode root = new TreeNode(i); // i 作为根节点 root 的值
root.left = lTree;
root.right = rTree;
res.add(root);
}
}
}
return res;
}
}
236. 二叉树的最近公共祖先 - 2.22(递归 | 后序遍历)
解析:递归,后序遍历;当p,q分别在根节点的一左一右时,当前根节点就是最近公共祖先。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null;
//如果当前节点等于p或q,返回当前节点
if(root == p || root == q) return root;
//递归左右子树
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
//如果p q 一个在左,一个在右,当前root节点就是最近公共祖先
if(left != null && right != null) return root;
//找不到,返回null
if(left == null && right == null) return null;
//如果p和q只有一个存在于root为根的树中,函数返回该节点
if(left != null){
return left;
}else{
return right;
}
}
}
1373. 二叉搜索子树的最大键值和 - 2.22(递归 | 后序遍历)
解析:递归,后序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int sum = 0;
public int maxSumBST(TreeNode root) {
traverse(root);
return sum;
}
private int[] traverse(TreeNode root){
if(root == null){
return new int[]{1, Integer.MAX_VALUE, Integer.MIN_VALUE, 0};
}
// 递归计算左右子树
int[] left = traverse(root.left);
int[] right = traverse(root.right);
/*
res[0] 记录以 root 为根的二叉树是否是 BST,若为 1 则说明是 BST,若为 0 则说明不是 BST;
res[1] 记录以 root 为根的二叉树所有节点中的最小值;
res[2] 记录以 root 为根的二叉树所有节点中的最大值;
res[3] 记录以 root 为根的二叉树所有节点值之和。
*/
int[] res = new int[4];
// 这个 if 在判断以 root 为根的二叉树是不是 BST
if(left[0] == 1 && right[0] == 1 && root.val > left[2] && root.val < right[1]){
res[0] = 1; // 这个 if 在判断以 root 为根的二叉树是不是 BST
res[1] = Math.min(left[1], root.val); // 计算以 root 为根的这棵 BST 的最小值
res[2] = Math.max(right[2], root.val); // 计算以 root 为根的这棵 BST 的最大值
res[3] = left[3] + right[3] + root.val; // 计算以 root 为根的这棵 BST 所有节点之和
sum = Math.max(sum, res[3]); // 更新全局变量
} else{
res[0] = 0; // 以 root 为根的二叉树不是 BST
}
return res;
}
}
297. 二叉树的序列化与反序列化 - 2.22(递归 | 前序遍历)
解析:
序列化时使用前序遍历,节点为空时用“#”代替,再用“,”分开;
反序列化时,将字符串转为列表,也是使用前序遍历,取出列表第一个新建为根节点,递归左右子树。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
serializeTree(root, sb);
return sb.toString();
}
private void serializeTree(TreeNode root, StringBuilder sb){
if(root == null){
sb.append("#").append(","); //# 代表空指针 null,并用逗号分开
return;
}
sb.append(root.val).append(","); //添加值
serializeTree(root.left, sb); //左
serializeTree(root.right, sb); //右
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
LinkedList<String> nodes = new LinkedList<>();
for(String s : data.split(",")){ // 将字符串转化成列表
nodes.addLast(s);
}
return deserializeTree(nodes);
}
private TreeNode deserializeTree(LinkedList<String> nodes){
if(nodes.isEmpty()) return null;
// 列表最左侧就是根节点
String firstRoot = nodes.removeFirst();
if(firstRoot.equals("#")) return null;
TreeNode root = new TreeNode(Integer.parseInt(firstRoot));
root.left = deserializeTree(nodes);
root.right = deserializeTree(nodes);
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));
341. 扁平化嵌套列表迭代器 - 2.22(递归)
解析: N 叉树的遍历问题
把一个NestedInteger扁平化 等价于遍历一棵 N 叉树的所有「叶子节点」吗?我把所有叶子节点都拿出来,不就可以作为迭代器进行遍历了吗?
/**
* // This is the interface that allows for creating nested lists.
* // You should not implement it, or speculate about its implementation
* public interface NestedInteger {
*
* // @return true if this NestedInteger holds a single integer, rather than a nested list.
* public boolean isInteger();
*
* // @return the single integer that this NestedInteger holds, if it holds a single integer
* // Return null if this NestedInteger holds a nested list
* public Integer getInteger();
*
* // @return the nested list that this NestedInteger holds, if it holds a nested list
* // Return empty list if this NestedInteger holds a single integer
* public List<NestedInteger> getList();
* }
*/
public class NestedIterator implements Iterator<Integer> {
private Iterator<Integer> it;
public NestedIterator(List<NestedInteger> nestedList) {
// 存放将 nestedList 打平的结果
List<Integer> result = new LinkedList<>();
for (NestedInteger node : nestedList) {
// 以每个节点为根遍历
traverse(node, result);
}
// 得到 result 列表的迭代器
this.it = result.iterator();
}
@Override
public Integer next() {
return it.next();
}
@Override
public boolean hasNext() {
return it.hasNext();
}
// 遍历以 root 为根的多叉树,将叶子节点的值加入 result 列表
private void traverse(NestedInteger root, List<Integer> result) {
if (root.isInteger()) {
// 到达叶子节点
result.add(root.getInteger());
return;
}
// 遍历框架
for (NestedInteger child : root.getList()) {
traverse(child, result);
}
}
}
/**
* Your NestedIterator object will be instantiated and called as such:
* NestedIterator i = new NestedIterator(nestedList);
* while (i.hasNext()) v[f()] = i.next();
*/
222. 完全二叉树的节点个数 - 2.23(递归)
解析:先记录左、右子树的高度,如果左右子树的高度相同,则是一棵满二叉树,用公式即可计算;否则就用普通二叉树的递归计算。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int countNodes(TreeNode root) {
TreeNode leftTree = root;
TreeNode rightTree = root;
int leftDepth = 0;
int rightDepth = 0;
//计算左子树的深度
while(leftTree != null){
leftDepth++;
leftTree = leftTree.left;
}
//计算右子树的深度
while(rightTree != null){
rightDepth++;
rightTree = rightTree.right;
}
//如果两者相等,说明是 满二叉树,用公式即可计算
if(leftDepth == rightDepth){
return (int)Math.pow(2, leftDepth) - 1;
}
//否则,就用普通二叉树的计算方式
return 1 + countNodes(root.left) + countNodes(root.right);
}
}
NC45 实现二叉树先序,中序和后序遍历 - 3.11
解析:
import java.util.ArrayList;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* }
*/
public class Solution {
/**
*
* @param root TreeNode类 the root of binary tree
* @return int整型二维数组
*/
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
ArrayList<Integer> list3 = new ArrayList<>();
public int[][] threeOrders (TreeNode root) {
// write code here
// 递归
preOrder(root);
inOrder(root);
postOrder(root);
int n = list1.size();
int[][] ans = new int[3][];
for(int i=0; i<3; i++) {
ArrayList<Integer> list;
switch(i) {
case 0: list = list1;break;
case 1: list = list2;break;
default: list = list3;break;
}
int[] temp = new int[n];
ans[i] = temp;
for(int j=0; j<n; j++) {
temp[j] = list.get(j);
}
}
return ans;
}
// 前序遍历
public void preOrder(TreeNode root) {
if (root == null) {
return;
}
list1.add(root.val);
preOrder(root.left);
preOrder(root.right);
}
// 中序遍历
public void inOrder(TreeNode root) {
if (root == null) {
return;
}
inOrder(root.left);
list2.add(root.val);
inOrder(root.right);
}
// 后序遍历
public void postOrder(TreeNode root) {
if (root == null) {
return;
}
postOrder(root.left);
postOrder(root.right);
list3.add(root.val);
}
}