1. 翻转二叉树
每次对二叉树进行处理时,先想清楚该题要用到哪种遍历方式。就比如本题可以用前后序遍历和逐层遍历。前序遍历也可以用但是要麻烦一些。
遍历每个节点,将其左右节点翻转即可。
可以用前后序遍历和层序遍历。但是不能用中序遍历,因为会将有的节点翻转两次。
- 递归方法
递归三部曲
- 确定函数的参数和返回值
每次要传入的是一个节点,而且并不需要返回值。 - 终止条件
当前节点为空就return。 - 确定递归逻辑
以前序为例,就先处理本节点,翻转左右子节点,然后递归下去。
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 tmp = root.left;
root.left = root.right;
root.right = tmp;
}
}
- 迭代方式
用层序遍历的话,处理每一个节点就可以了。
/**
* 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 root;
}
Deque<TreeNode> deque = new LinkedList<>();
deque.add(root);
while (!deque.isEmpty()) {
int num = deque.size();
while (num > 0) {
TreeNode node = deque.poll();
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
if (node.left != null) {
deque.add(node.left);
}
if (node.right != null) {
deque.add(node.right);
}
num--;
}
}
return root;
}
}
- 补充
也可以用中序遍历,不过要将处理右节点改为处理左节点,因为处理中的时候将二者翻转了。
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == NULL) return root;
invertTree(root->left); // 左
swap(root->left, root->right); // 中
invertTree(root->left); // 注意 这里依然要遍历左孩子,因为中间节点已经翻转了
return root;
}
};
2. 对称二叉树
递归:每次处理的是根节点的两个子树,看这两个子树能不能对称。每次处理时先排除掉一些简单的情况。然后比较对称位置节点的左右子树。
遍历顺序只能是后序遍历,因为要先收集两个子树的对称情况,返回给父节点做判断。
迭代:使用层序遍历,用队列或栈记录每一层的结果,比较结果是否对称。
- 递归
递归三部曲
- 函数参数和返回值
每次比较的是两个子树,要传入两个节点。要返回boolean值。 - 终止条件
要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚。否则后面比较数值的时候就会操作空指针了。一共有四种情况:左空,右空,左右空,左右值不等。换句话说,终止条件是可以得出一个结果的时候。
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;
}
- 每层处理逻辑
先看子树的内外侧是否对称,然后返回给本节点。
class Solution {
public boolean isSymmetric(TreeNode root) {
return compare(root.left, root.right);
}
public boolean compare(TreeNode left, TreeNode right){
if(left != null && right == null){
return false;
}else if(left == null && right != null){
return false;
}else if(left == null && right == null){
return true;
}else if(left.val != right.val){
return false;
}
boolean compareOutside = compare(left.left,right.right);
boolean compareInside = compare(left.right,right.left);
return compareOutside && compareInside;
}
}
- 迭代
用队列或者栈记录每一层的节点,比较是否对称。
public boolean isSymmetric3(TreeNode root) {
Queue<TreeNode> deque = new LinkedList<>();
deque.offer(root.left);
deque.offer(root.right);
while (!deque.isEmpty()) {
TreeNode leftNode = deque.poll();
TreeNode rightNode = deque.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;
// }
// 以上三个判断条件合并
if (leftNode == null || rightNode == null || leftNode.val != rightNode.val) {
return false;
}
// 这里顺序与使用Deque不同
deque.offer(leftNode.left);
deque.offer(rightNode.right);
deque.offer(leftNode.right);
deque.offer(rightNode.left);
}
return true;
}
3. 最大深度
本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
根节点的高度就是二叉树的最大深度。
- 递归
- 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
- 确定终止条件:如果为空节点的话,就返回0,表示高度为0。
- 确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加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 int maxDepth(TreeNode root) {
return compareHeight(root);
}
public int compareHeight(TreeNode node){
if(node == null){
return 0;
}
int leftHeight = compareHeight(node.left);
int rightHeight = compareHeight(node.right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
}
- 迭代
层序遍历,每一层将depth+1即可。
class Solution {
/**
* 迭代法,使用层序遍历
*/
public int maxDepth(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 node = deque.poll();
if (node.left != null) {
deque.offer(node.left);
}
if (node.right != null) {
deque.offer(node.right);
}
}
}
return depth;
}
}
4. 二叉树最小深度
力扣
和最大深度差不多,但是要注意一点:左子节点为空时不算子树高度为0。
如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。
- 迭代
迭代的时候,遇到叶子节点返回depth即可。
class Solution {
public int minDepth(TreeNode root) {
if(root == null){return 0;}
Deque<TreeNode> deque = new LinkedList<>();
int depth = 0;
deque.push(root);
while(!deque.isEmpty()){
int size = deque.size();
depth++;
while(size > 0){
TreeNode node = deque.pop();
if (node.left != null) {
deque.offer(node.left);
}
if (node.right != null) {
deque.offer(node.right);
}
if(node.left == null && node.right == null){
return depth;
}
size --;
}
}
return depth;
}
}
- 递归
class Solution {
/**
* 递归法,相比求MaxDepth要复杂点
* 因为最小深度是从根节点到最近**叶子节点**的最短路径上的节点数量
*/
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
if (root.left == null) {
return rightDepth + 1;
}
if (root.right == null) {
return leftDepth + 1;
}
// 左右结点都不为null
return Math.min(leftDepth, rightDepth) + 1;
}
}