算法Day14|二叉树专题三 226.翻转二叉树,101. 对称二叉树,104.二叉树的最大深度,111.二叉树的最小深度,222.完全二叉树的节点个数,110.平衡二叉树,257. 二叉树的所有路径

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.解题思路

首先想清楚,判断对称二叉树要比较的是哪两个节点,要比较的可不是左右节点!

对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。

那么如果比较呢?

比较的是两个子树的里侧和外侧的元素是否相等。如图所示:

101. 对称二叉树1

  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.二叉树的最小深度   完整代码如下:

    //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.解题思路

        这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。

        在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一一个路径在进入另一个路径。

        前序遍历以及回溯的过程如图:

257.二叉树的所有路径

 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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Road_slow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值