226.翻转二叉树
1.题目描述
给你一棵二叉树的根节点
root
,翻转这棵二叉树,并返回其根节点。
2.解题思路
可以发现想要翻转它,其实就把每一个节点的左右孩子交换一下就可以了。
3.代码实现
3.1递归写法
class Solution {
//226. 翻转二叉树(递归写法)
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return root;
}
invertTree(root.left);
invertTree(root.right);
swap(root);
return root;
}
public void swap(TreeNode root) {
TreeNode tempNode = root.left;
root.left = root.right;
root.right = tempNode;
}
}
3.2非递归写法(层序遍历BFS)
class Solution {
//226. 翻转二叉树(BFS)
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return root;
}
Deque<TreeNode> deque = new LinkedList<>();
deque.add(root);
while (!deque.isEmpty()) {
int size = deque.size();
while (size > 0) {
TreeNode node = deque.pop();
if (node.left != null) {
deque.add(node.left);
}
if (node.right != null) {
deque.add(node.right);
}
//只需要交换移除节点的左右节点即可
swap(node);
size--;
}
}
return root;
}
public void swap(TreeNode root) {
TreeNode tempNode = root.left;
root.left = root.right;
root.right = tempNode;
}
}
3.3非递归写法(深度遍历DFS)
class Solution {
//226. 翻转二叉树(DFS)前序遍历
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return root;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
//swap(node);在这里进行交互也可
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
swap(node);
}
return root;
}
public void swap(TreeNode root) {
TreeNode tempNode = root.left;
root.left = root.right;
root.right = tempNode;
}
}
101. 对称二叉树
1.题目描述
给你一个二叉树的根节点
root
, 检查它是否轴对称。
2.解题思路
首先想清楚,判断对称二叉树要比较的是哪两个节点,要比较的可不是左右节点!
对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
那么如果比较呢?
比较的是两个子树的里侧和外侧的元素是否相等。如图所示:
3.代码实现
3.1递归写法
1.确定递归函数的参数和返回值
因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。返回值自然是bool类型。
public boolean symmetric(TreeNode left, TreeNode right){}
2.递归的终止条件:
节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点)
- 左节点为空,右节点不为空,不对称,return false
- 左不为空,右为空,不对称 return false
- 左右都为空,对称,返回true
此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:
- 左右都不为空,比较节点数值,不相同就return false
此时左右节点不为空,且数值也不相同的情况我们也处理了。
//递归终止条件(4个if)
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;
}
3.确定单层递归的逻辑
此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内测是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
//递归的单层逻辑
boolean leftSymmetric = symmetric(left.left, right.right);
boolean rightSymmetric = symmetric(left.right, right.left);
return leftSymmetric && rightSymmetric;
完整代码如下:
class Solution {
//101. 对称二叉树(递归版本)
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
return symmetric(root.left, root.right);
}
//递归函数的定义:判断两个节点及它们所在的子树是否对称,返回true为对称
public boolean symmetric(TreeNode left, TreeNode right) {
//递归终止条件(4个if)
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 leftSymmetric = symmetric(left.left, right.right);
boolean rightSymmetric = symmetric(left.right, right.left);
return leftSymmetric && rightSymmetric;
}
}
3.2非递归写法(层序遍历BFS)
使用队列
通过队列来判断根节点的左子树和右子树的内侧和外侧是否相等,如动画所示:
class Solution {
//101. 对称二叉树(非递归版本)
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
//使用双端队列,一侧放左节点,另外一侧放右节点
Deque<TreeNode> deque = new LinkedList<>();
deque.addFirst(root.left);
deque.addLast(root.right);
while (!deque.isEmpty()) {
TreeNode leftNode = deque.pollFirst();
TreeNode rightNode = deque.pollLast();
//判断两者都为null之后,不能返回true,继续进while中
if (leftNode == null && rightNode == null) {
continue;
} else if (leftNode == null || rightNode == null || leftNode.val != rightNode.val) {
return false;
}
deque.addFirst(leftNode.right);
deque.addFirst(leftNode.left);
deque.addLast(rightNode.left);
deque.addLast(rightNode.right);
}
return true;
}
}
104.二叉树的最大深度
1.题目描述
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
2.代码实现(递归法)
1.确定递归函数的参数和返回值:
参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
//递归函数的定义:输入一个节点,返回它的深度
public int maxDepth(TreeNode root) {}
2.确定终止条件:
如果为空节点的话,就返回0,表示高度为0。
//递归终止条件
if (root == null) {
return 0;
}
3.确定单层递归的逻辑:
先求它的左子树的深度,再求的右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
//递归的单层逻辑
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
完整代码如下:
class Solution {
//104. 二叉树的最大深度
//递归函数的定义:输入一个节点,返回它的深度
public int maxDepth(TreeNode root) {
//递归终止条件
if (root == null) {
return 0;
}
//递归的单层逻辑
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
}
111.二叉树的最小深度
1.题目描述
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
2.代码实现(递归法)
1.确定递归函数的参数和返回值:
参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
//递归函数的定义:输入一个节点,返回它的深度
public int minDepth(TreeNode root) {}
2.确定终止条件:
如果为空节点的话,就返回0,表示高度为0。
//递归终止条件
if (root == null) {
return 0;
}
3.确定单层递归的逻辑:
如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。
最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。
//递归的单层逻辑(正确判断)
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
if (root.left == null && root.right != null) {
return rightDepth + 1;
}
if (root.right == null && root.left != null) {
return leftDepth + 1;
}
return Math.min(leftDepth, rightDepth) + 1;
注意:如果不判断左右节点不为空的话,没有左孩子的分支会算为最短深度。就会犯下图中的错误。
//递归的单层逻辑(错误判断)
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
return Math.min(leftDepth, rightDepth) + 1;
完整代码如下:
//111. 二叉树的最小深度(递归法)
//递归函数的定义:输入一个节点,返回它的深度
public int minDepth(TreeNode root) {
//递归终止条件
if (root == null) {
return 0;
}
//递归的单层逻辑
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
if (root.left == null && root.right != null) {
return rightDepth + 1;
}
if (root.right == null && root.left != null) {
return leftDepth + 1;
}
return Math.min(leftDepth, rightDepth) + 1;
}
222.完全二叉树的节点个数
1.题目描述
给你一棵完全二叉树的根节点 root ,求出该树的节点个数。
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
2.解题思路
这道题目的递归法和求二叉树的深度写法类似, 而迭代法,在层序遍历模板稍稍修改一下,记录遍历的节点数量就可以了。
3.代码实现
3.1递归写法
1.确定递归函数的参数和返回值:
参数就是传入树的根节点,返回就返回这棵树的节点数,所以返回值为int类型。
//递归函数的定义:输入一个节点,返回它的节点数
public int countNodes(TreeNode root) {}
2.确定终止条件:
如果为空节点的话,就返回0,表示高度为0。
//递归终止条件
if (root == null) {
return 0;
}
3.确定单层递归的逻辑:
先求它的左子树的节点数量,再求的右子树的节点数量,最后取总和再加一 (加1是因为算上当前中间节点)就是目前节点为根节点的节点数量。
//递归的单层逻辑
int leftCount = countNodes(root.left);
int rightCount = countNodes(root.right);
return leftCount + rightCount + 1;
完整代码如下:
class Solution {
//222.完全二叉树的节点个数(递归)
//递归函数的定义:输入一个节点,返回它的节点数
public int countNodes(TreeNode root) {
//递归终止条件
if (root == null) {
return 0;
}
//递归的单层逻辑
int leftCount = countNodes(root.left);
int rightCount = countNodes(root.right);
return leftCount + rightCount + 1;
}
}
3.2非递归写法(层序遍历BFS)
class Solution {
//222.完全二叉树的节点个数(BFS)
public int countNodes(TreeNode root) {
if (root == null) {
return 0;
}
Deque<TreeNode> deque = new LinkedList<>();
deque.add(root);
int count = 0;//表示最终的结果
while (!deque.isEmpty()) {
//获取size,这个size是每层的节点个数
int size = deque.size();
//每次加上size的值,便是结果
count += size;
while (size > 0) {
TreeNode node = deque.pop();
if (node.left != null) {
deque.add(node.left);
}
if (node.right != null) {
deque.add(node.right);
}
size--;
}
}
return count;
}
}
110.平衡二叉树
1.题目描述
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
2.解题思路
这道题目的递归法和求二叉树的深度写法类似。
3.代码实现
1.确定递归函数的参数和返回值:
参数就是传入树的根节点,返回以输入节点为根节点树的高度,所以返回值为int类型。
//递归函数的定义:输入一个节点,返回以当前传入节点为根节点的树的高度。
public int getNodeHeight(TreeNode root) {}
2.确定终止条件:
如果为空节点的话,就返回0,表示高度为0。
//递归终止条件
if (root == null) {
return 0;
}
3.确定单层递归的逻辑:
分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。
//递归的单层逻辑
//1.分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度。
//2.否则则返回-1,表示已经不是二叉平衡树了。
int leftNodeHeight = getNodeHeight(root.left);
if (leftNodeHeight == -1) {
return -1;
}
int rightNodeHeight = getNodeHeight(root.right);
if (rightNodeHeight == -1) {
return -1;
}
if (Math.abs(leftNodeHeight - rightNodeHeight) > 1) {
return -1;
}
return Math.max(leftNodeHeight, rightNodeHeight) + 1;
完整代码如下:
class Solution {
//110. 平衡二叉树(递归写法)
public boolean isBalanced(TreeNode root) {
if (getNodeHeight(root) != -1) {
return true;
}
return false;
}
//递归函数的定义:输入一个节点,返回以当前传入节点为根节点的树的高度。
public int getNodeHeight(TreeNode root) {
//递归终止条件
if (root == null) {
return 0;
}
//递归的单层逻辑
//1.分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度。
//2.否则则返回-1,表示已经不是二叉平衡树了。
int leftNodeHeight = getNodeHeight(root.left);
if (leftNodeHeight == -1) {
return -1;
}
int rightNodeHeight = getNodeHeight(root.right);
if (rightNodeHeight == -1) {
return -1;
}
if (Math.abs(leftNodeHeight - rightNodeHeight) > 1) {
return -1;
}
return Math.max(leftNodeHeight, rightNodeHeight) + 1;
}
}
257. 二叉树的所有路径
1.题目描述
给你一个二叉树的根节点
root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。叶子节点 是指没有子节点的节点。
2.解题思路
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一一个路径在进入另一个路径。
前序遍历以及回溯的过程如图:
3.代码实现
3.1递归写法
1.确定递归函数的参数和返回值:
参数要传入根节点,记录每一条路径的path,和存放结果集的res,这里递归不需要返回值,代码如下。
//递归函数的定义:传入根节点,记录每一条路径的path,和存放结果集的res
public void traversal(TreeNode root, List<Integer> path, List<String> res) {}
2.确定终止条件:
如果为叶子节点的话,就把对应路径的数值进行拼接,转换为String放进结果集res中。
//递归终止条件,到达叶子节点
if (root.left == null && root.right == null) {
//1.把对应路径的数值进行拼接,
int size = path.size();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size - 1; i++) {
sb.append(path.get(i)).append("->");
}
sb.append(path.get(size - 1));
//2.转换为String放进结果集res中
res.add(sb.toString());
}
3.确定单层递归的逻辑:
递归完,要做回溯啊,因为path 不能一直加入节点,它还要删节点,然后才能加入新的节点。
注意:回溯和递归是一一对应的,有一个递归,就要有一个回溯。所以回溯要和递归永远在一起,世界上最遥远的距离是你在花括号里,而我在花括号外!
if (root.left != null) {
traversal(root.left, path, res);
path.remove(path.size() - 1);// 回溯
}
if (root.right != null) {
traversal(root.right, path, res);
path.remove(path.size() - 1);// 回溯
}
完整代码如下:
class Solution {
//257. 二叉树的所有路径(递归法)
public List<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList<>();
if (root == null) {
return res;
}
List<Integer> path = new ArrayList<>();
traversal(root, path, res);
return res;
}
//递归函数的定义:传入根节点,记录每一条路径的path,和存放结果集的result
public void traversal(TreeNode root, List<Integer> path, List<String> res) {
path.add(root.val);
//递归终止条件,到达叶子节点
if (root.left == null && root.right == null) {
//1.把对应路径的数值进行拼接,
int size = path.size();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size - 1; i++) {
sb.append(path.get(i)).append("->");
}
sb.append(path.get(size - 1));
//2.转换为String放进结果集res中
res.add(sb.toString());
}
//递归的单层逻辑
if (root.left != null) {
traversal(root.left, path, res);
path.remove(path.size() - 1);// 回溯
}
if (root.right != null) {
traversal(root.right, path, res);
path.remove(path.size() - 1);// 回溯
}
}
}
3.2非递归写法(前序遍历DFS)
class Solution {
//257. 二叉树的所有路径(迭代法-前序遍历)
public List<String> binaryTreePaths(TreeNode root) {
Stack<Object> stack = new Stack<>();
// 节点和路径同时入栈
stack.push(root);
stack.push(root.val + "");
ArrayList<String> res = new ArrayList<>();
while (!stack.isEmpty()) {
// 节点和路径同时出栈
String path = (String) stack.pop();
TreeNode node = (TreeNode) stack.pop();
// 若找到叶子节点
if (node.left == null && node.right == null) {
res.add(path);
}
//右子节点不为空
if (node.right != null) {
stack.push(node.right);
stack.push(path + "->" + node.right.val);
}
//左子节点不为空
if (node.left != null) {
stack.push(node.left);
stack.push(path + "->" + node.left.val);
}
}
return res;
}
}