Java二叉树经典进阶OJ题解

目录

一、判断一颗二叉树是否为对称二叉树

1.题目描述:

2.代码示例:

3.通过演示与分析:

二、根据先序遍历结果构造二叉树

1.题目描述:

2.代码示例:

3.通过演示与分析:

三、层序遍历的非递归实现

1.题目描述:

2.代码示例:

3.通过演示与分析:

四、判断是否为完全二叉树

1.题目描述:

2.代码示例:

3.通过演示与分析:

五、寻找二叉树的最近公共祖先

1.题目描述:

2.代码示例:

3.通过演示与分析:


   

     二叉树由于其结构的递归特性(即一棵树的左右子树仍然可以看作一棵树),使得其许多操作可以通过递归算法实现,下面给出五道java二叉树的经典进阶OJ题及题解供读者参考。

一、判断一颗二叉树是否为对称二叉树

1.题目描述:

2.代码示例:

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 isSymmetricChild(root.left,root.right);

    }
    //判断一棵树是否对称的底层方法
    public boolean isSymmetricChild(TreeNode left,TreeNode right){
        //两棵树根节点都为空则对称,直接返回true
        if(left==null&&right==null){
            return true;
        }
        //两棵树一棵根节点为空,另一颗不为空,不对称直接返回false
        if(left==null&&right!=null||left!=null&&right==null){
            return false;
        }
        //两棵树均不为空,比较节点的值,若不同则不对称,直接返回false
        if(left.val!=right.val){
            return false;
        }
        //递归判断左树的左子树与右树的右子树是否对称,左树的右子树与右树的左子树是否对称
        return isSymmetricChild(left.left,right.right)&&isSymmetricChild(left.right,right.left);
    }
}

3.通过演示与分析:

假设这棵树的节点个数为n,则该算法的时间复杂度为O(n)。

二、根据先序遍历结果构造二叉树

1.题目描述:

2.代码示例:

import java.util.Scanner;
class TreeNode{
    public char val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(char val){
        this.val=val;
    }
}

public class Main {
    public static int i=0;
    public static TreeNode createTree(String str){
        TreeNode root=null;
        //遍历字符串
        if(str.charAt(i)!='#'){
            //字符表示不为空则创建节点
            root=new TreeNode(str.charAt(i));
            i++;
            //递归创建左子树
            root.left=createTree(str);
            //递归创建右子树
            root.right=createTree(str);
        }else{
            //字符表示为空则不创建节点
            i++;
        }
        return root;

    }
    //中序遍历方法
    public static void inorder(TreeNode root){
        if(root==null){
            return;
        }
        inorder(root.left);
        System.out.print(root.val+" ");
        inorder(root.right);
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextLine()) { // 注意 while 处理多个 case
        String str=in.nextLine();
        TreeNode root=createTree(str);
        inorder(root);
        }
    }
}

3.通过演示与分析:

假设这棵树的节点个数为n,则该算法的时间复杂度为O(n)。

三、层序遍历的非递归实现

1.题目描述:

2.代码示例:

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>> levelOrder(TreeNode root) {
        //设定返回值(这里有向上转型)
        List<List<Integer>> ret=new ArrayList<>();
        //根节点为空直接返回空
        if(root==null){
            return ret;
        }
        //创建一个新的队列(这里有向上转型)
        Queue<TreeNode> queue=new LinkedList<>();
        //根节点入队
        queue.offer(root);
        //队列不为空则进入循环
        while(!queue.isEmpty()){
            //创建本一层的List
            List<Integer>list=new ArrayList<>();
            //获取当前队列大小(本层节点个数)
            int size=queue.size();
            //对队列所有节点执行以下操作
            while(size>0){
                //设定cur记录出队节点
                TreeNode cur=queue.poll();
                //将该节点的值放入本层List
                list.add(cur.val);
                //该节点左子树不为空则左子树根节点入队
                if(cur.left!=null){
                    queue.offer(cur.left);
                }
                //该节点右子树不为空则右子树根节点入队
                if(cur.right!=null){
                    queue.offer(cur.right);
                }
                size--;
            }
            //返回值中添加新一层List的引用
            ret.add(list);
        }
        return ret;
    }
}

借助创建队列实现层序遍历的非递归操作。

3.通过演示与分析:

假设这棵树的节点个数为n,则该算法的时间复杂度为O(n)。 

四、判断是否为完全二叉树

1.题目描述:

判断一棵树是否为完全二叉树(定义如下)

完全二叉树是由满二叉树而引出来的,若设二叉树的深度为h除第 h 层外其它各层 (1~h-1) 的结点数都达到最大个数(即1~h-1层为一个满二叉树),第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

2.代码示例:

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




public class IsCompleteTree {
    public boolean isCompleteTree(TreeNode root){
        //新建一个队列(这里发生了向上转型)
        Queue<TreeNode> queue=new LinkedList<>();
        //根节点为空认为是完全二叉树
        if(root==null){
            return true;
        }
        //根节点入队
        queue.offer(root);
        //队列不为空则进入循环
        while(!queue.isEmpty()){
            //设定cur记录出队元素
            TreeNode cur=queue.poll();
            //遇到空节点中止循环
            if(cur==null){
                break;
            }
            //将cur左子树根节点入队
            queue.offer(cur.left);
            //将cur右子树根节点入队
            queue.offer(cur.right);
        }
        //循环结束后判断队列中是否仍然存在非空节点
        while(!queue.isEmpty()){
            TreeNode cur=queue.peek();
            if(cur!=null){
                //若有非空节点则说明不为完全二叉树
                return false;
            }else{
                queue.poll();
            }
        }
        //程序全部运行完毕说明此树为完全二叉树
        return true;
    }
}

本题思路类似于层序遍历的非递归实现,同样是 借助创建队列实现层序遍历的非递归操作。

3.通过演示与分析:

演示请读者自行尝试。

假设这棵树的节点个数为n,则该算法的时间复杂度为O(n)。 

五、寻找二叉树的最近公共祖先

1.题目描述:

2.代码示例:

方法一:

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 lowestCommonAncestor1(TreeNode root, TreeNode p, TreeNode q) {
        //根节点为空则直接返回空
        if(root==null){
            return null;
        }
        //如果其中有一个根节点等于root,则最近公共祖先即为root
        if(root==p||root==q){
            return root;
        }
        //开始递归判断
        TreeNode leftTreeLowestCommonAncestor=lowestCommonAncestor1(root.left,p,q);
        TreeNode rightTreeLowestCommonAncestor=lowestCommonAncestor1(root.right,p,q);
        if(leftTreeLowestCommonAncestor!=null&&rightTreeLowestCommonAncestor!=null){
            //左右子树返回值都不为空,说明p与q分别位于根节点两侧, 根节点为最近公共祖先
            return root;
        }else if(leftTreeLowestCommonAncestor!=null){
            //左子树有最近公共祖先
            return leftTreeLowestCommonAncestor;
        }else{
            //右子树有最近公共祖先
            return rightTreeLowestCommonAncestor;
        }
    }
}

方法二:

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 lowestCommonAncestor2(TreeNode root,TreeNode p,TreeNode q){
        //根节点为空则直接返回
        if(root==null){
            return root;
        }
        //为两个指定节点分别创建两个存储其各自到根节点路径的栈
        Stack<TreeNode>stackp=new Stack<>();
        getPath(root,p,stackp);
        Stack<TreeNode>stackq=new Stack<>();
        getPath(root,p,stackq);
        //计算两个栈的大小并比较
        int sizep=stackp.size();
        int sizeq=stackq.size();
        //将较长的栈的元素出栈直到元素个数相等
        if(sizep>sizeq) {
            int size = sizep - sizeq;
            while (size != 0) {
                stackp.pop();
                size--;
            }
        }else{
                int size=sizeq-sizep;
                while(size!=0){
                    stackq.pop();
                    size--;
            }
        }
        //将剩余元素全部出栈直到寻找到相同的节点,此节点即为最近公共祖先
        while(!stackp.isEmpty()&&!stackq.isEmpty()){
            if(stackp.peek()==stackq.peek()){
                return stackp.peek();
            }else{
                stackp.pop();
                stackq.pop();
            }
        }
        return null;
    }
    //将指定节点到根节点的路径存放到指定栈的底层方法
    public boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack){
        //为空返回false
        if(root==null){
             return false;
         }
        //将根节点入栈
        stack.push(root);
        //根节点为指定节点则直接返回true
        if(root==node){
            return true;
        }
        //递归左子树,获取路径
        boolean leftFlg=getPath(root.left,node,stack);
        //成功获取到路径,直接返回标志true
        if(leftFlg==true){
            return true;
        }
        //递归右子树,获取路径
        boolean rightFlg=getPath(root.right,node,stack);
        //成功获取到路径,直接返回标志true
        if(rightFlg==true){
            return true;
        }
        //程序全部执行完毕,说明未找到路径,将当前根节点弹出并返回标志false
        stack.pop();
        return false;
    }
}

3.通过演示与分析:

假设这棵树的节点个数为n,则方法一算法的时间复杂度为O(n),方法二的时间复杂度也为O(n)。

以上便是 Java二叉树经典进阶OJ题及题解的全部内容,如有不当,敬请斧正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值