二叉树系列-题型与概念

二叉树的性质

  1. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有 (i>0)个结点
  2. 若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 (k>=0)
  3. 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1
  4. 具有n个结点的完全二叉树的深度k为 log2^(n+1)上取整
  5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:
    若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
    若2i+1<n,左孩子序号:2i+1,否则无左孩子
    若2i+2<n,右孩子序号:2i+2,否则无右孩子

二叉树的遍历

理解递归序 :
一棵树,如果是以递归的形式去遍历的话,那么每一个节点都会被调用三次.而先序中序后序只是打印操作时机不同而已.
在这里插入图片描述

递归与非递归实现打印

所有递归都可以改成非递归实现-用栈

先序

1先将头结点入栈,
2每次从栈中弹出一个节点打印,
3先压入右孩子,再压入左孩子(没有什么也不干) (重复上述步骤)

 /*先序遍历的递归和非递归打印*/
    public void preOrder (TreeNode root) {
        if (root == null)return;
        System.out.println(root);
        inOrder(root.left);
        inOrder(root.right);
    }
    public void preOrderStack (TreeNode root) {
        if (root == null)return;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty() ){
            root=stack.pop();
            System.out.println(root.val);
           if (root.right != null)stack.push(root.right);
           if (root.left != null) stack.push(root.left);
        }
    }

后序

俩个栈 (下述流程为:头右左,但是不打印,反而放到另一个栈,所以收集栈就是头右左的逆序—左右头)
1先将头结点入栈,
2弹出的元素进入收集栈
3先压入左孩子,再压入右孩子(没有什么也不干)
等所有过程结束后,单独打印收集栈----后序 (重复上述步骤)

 /*后序遍历的递归和非递归打印*/
    public void postOrder (TreeNode root) {
        if (root == null)return;
        inOrder(root.left);
        inOrder(root.right);
        System.out.println(root);
    }
    public void postOrderStack (TreeNode root) {
        if (root == null)return;
        Stack<TreeNode> stack = new Stack<>();
        Stack<TreeNode> cur = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty() ){
            root=stack.pop();
            cur.push(root);
            if (root.left != null) stack.push(root.left);
            if (root.right != null)stack.push(root.right);
        }
        while (!cur.isEmpty()){
            root=cur.pop();
            System.out.println(root.val);
        }
    }

中序

1先将左边节点全入栈,
2依次弹出的过程中打印
3每次弹出的节点(有右树进行就上述步骤)

 /*中序遍历的递归和非递归打印*/
    public void inOrder(TreeNode root) {
        if (root == null)return;
        inOrder(root.left);
        System.out.println(root);
        inOrder(root.right);
    }
    public void inOrderStack(TreeNode root) {
        if (root != null) {
            Stack<TreeNode> stack = new Stack<>();
            while (!stack.isEmpty() && root != null) {
                if (root != null) {
                    stack.push(root);
                    root = root.left;
                } else {
                    root = stack.pop();
                    System.out.println(root.val);
                    root = root.right;
                }
            }
        }
    }

层序遍历(队列)

1:先将头结点放入队列中
2:然后弹出节点,
3:先进左,再进右节点(没有就不放) 重复上述过程-直到队列为空

 //层序遍历
    public void levelOrder(TreeNode root){
        if (root == null )return;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()){
            TreeNode cur=queue.poll();
            System.out.println(cur.val);
            if (cur.left != null)queue.add(cur.left);
            if (cur.right != null)queue.add(cur.right);
        }
    }

求一个二叉树的最大深度

在先序遍历的基础上加上判断深度

 public int maxDepth (TreeNode root) {
        if (root==null)return 0;
        int leftDepth=0;
        int rightDepth=0;
        leftDepth=maxDepth(root.left);
        rightDepth=maxDepth(root.right);
        return leftDepth>rightDepth?leftDepth+1:rightDepth+1;
    }

求一个二叉树的最大宽度

在层序遍历的基础上,要知道每一层的节点个数,并找到最大值
1:设置一个hashMap,从来知道当前节点是第几层
2:定义每一层的节点数
3:定义全局的max,用来获取最大的节点数
可以优化-不使用HashMap
简单说一下思路,
定义TreeNode俩个变量,一个获取当前节点值变量,一个全局变量MAx
1:一个变量为当前这一层的最后一个节点,另一个变量为,下一层的最后一个节点
2:当前节点值变量,只要队列出来的借点值不是最后一个节点就+1(下一层的最后一个节点捕获新入队的节点),等是最后一个节点时,该变量+1,并被MAx捕获
3:这一层的最后一个节点=下一层的最后一个节点,下一层的最后一个节点重新捕获节点,当前节点值变量置位0,
重复上述流程

 //求一棵树的最大宽度-
    public int maxWidth (TreeNode root) {
        if (root == null )return -1;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        HashMap<TreeNode,Integer> levelMap= new HashMap<>();
        levelMap.put(root,1);
        int curLevel=1;//层数
        int curLevelNode =0;//当前层的节点数
        int max= Integer.MIN_VALUE;
        while (!queue.isEmpty()){
            TreeNode cur=queue.poll();
            int curNodeLevel=levelMap.get(cur);
            //获取当前节点的层,是否还在这一层
            if (curNodeLevel == curLevel){
                curLevelNode++;
            }else {
                max = Math.max(max,curLevelNode);
                curLevel++;
                curLevelNode=1;
            }
            if (cur.left != null){
                levelMap.put(cur.left,curNodeLevel+1);
                queue.add(cur.left);
            }
            if (cur.right != null){
                levelMap.put(cur.right,curNodeLevel+1);
                queue.add(cur.right);
            }
        }
        return max;
    }

怎么判断一颗树是否是完全二叉树

解题思路
二叉树按层序遍历,基础上判断
1:任一节点如果只有右孩子,没有左孩子,直接FALSE
2:在1的基础上,如果第一个左右孩子不双全,那么接下来所有的节点都是叶节点

/*判断是否是完全二叉树*/
    public boolean isCBT(TreeNode root){
        if (root == null)return true;
        Queue<TreeNode> queue=new LinkedList<>();
        boolean leaf=false;//判断是否遇到俩个左右孩子不双全的节点
        TreeNode l =null;
        TreeNode r=null;
        queue.add(root);
        while (!queue.isEmpty()){
            root = queue.poll();
            l=root.left;
            r=root.right;
            //判断失败逻辑的俩种情况
            if ((leaf && (l != null || r != null)) || (l == null && r != null)){
                return false;
            }
            if (l !=null){
                queue.add(l);
            }
            if (r != null){
                queue.add(r);
            }
            if (l == null || r == null){
                leaf = true;
            }
        }
        return true;
    }

树形DP的递归套路–很强!-树形面试题目几乎都能写

树形DP:可以通过向左树要信息,可以通过向右树要信息来解决的问题
1找到你需要的所有信息,
2然后直接加工出一个包含所有信息的类,设计一个黑盒,形成子问题递归(树形DP)

判断一颗二叉树是否是搜索二叉树

搜索二叉树,左树节点都比根节点小,右树节点都比二叉树大
中序遍历结果集是有递增的
思路1记录上一次结果的值,然后和当前获取的值进行比较
思路2----递归套路
所需要的信息
左树是不是搜索二叉树
右树是不是搜索二叉树
左树的最大值根小
右树的最小值比根大
注:递归是所有问题都一致,只是规模不同,所以直接返回三个值,是不是搜索二叉树.和这个二叉树的最大值与最小值

思路1

/*判断一棵树是否为搜索二叉树的递归与非递归实现*/
    //思路1
    private int preValue=Integer.MIN_VALUE;
    public boolean isBST(TreeNode root){
        if (root == null)return true;
        boolean isLeftBst=isBST(root.left);
        if (!isLeftBst)return false;
        if (root.val <= preValue ){
            return false;
        }else {
            preValue=root.val;
        }
        return isBST(root.right);
    }
    public boolean isBSTStack(TreeNode root){
        if (root == null) {
            return true;
        }else {
            Stack<TreeNode> stack = new Stack<>();
            while (!stack.isEmpty() || root != null) {
                if (root != null) {
                    stack.push(root);
                    root = root.left;
                } else {
                    root = stack.pop();
                    if (root.val <= preValue ){
                        return false;
                    }else {
                        preValue=root.val;
                    }
                    root = root.right;
                }
            }
        }
        return true;
    }

思路2

public class ReturnData{
        public boolean isBST;
        public int min;
        public int max;

        public ReturnData(boolean isBST, int min, int max) {
            this.isBST = isBST;
            this.min = min;
            this.max = max;
        }
    }
    public boolean isBST3(TreeNode root){
        ReturnData data=processData(root);
        return data.isBST&& data.max < root.val && data.min > root.val;
    }
    public ReturnData processData(TreeNode x){
        if ( x == null)return null;
        ReturnData leftData=processData(x.left);
        ReturnData rightData=processData(x.right);

        int min = x.val;
        int max = x.val;
        if (leftData != null){
               min=Math.min(min,leftData.min);
               max=Math.max(max,leftData.max);
        }
        if (rightData != null){
            min=Math.min(min,rightData.min);
            max=Math.max(max,rightData.max);
        }
        boolean isBST=true;
        /*俩种违规条件-不是平衡二叉树,最大值大于节点值*/
        if (leftData != null && (!leftData.isBST || leftData.max >= x.val)){
            isBST=false;
        }
        if (rightData != null && (!rightData.isBST || rightData.min <= x.val)){
            isBST=false;
        }
        return new ReturnData(true,min,max);
    }

判断一个二叉树是不是平衡二叉树

平衡二叉树条件
1:左子树平衡
2:右子树平衡
3:| 左高-右高 | <= 1

public class ReturnType{
        public boolean isBlanced;
        public int height;

        public ReturnType(boolean isBlanced, int height) {
            this.isBlanced = isBlanced;
            this.height = height;
        }
    }
    public boolean isBalance(TreeNode root){
        if (root == null)return true;
        ReturnType type=processType(root);
        return type.isBlanced;
    }
    public ReturnType processType(TreeNode x){
        if (x == null)return new ReturnType(true,0);
        ReturnType leftData=processType(x.left);
        ReturnType rightdata=processType(x.right);
        int height=Math.max(leftData.height,rightdata.height)+1;

        boolean isBalanced=(leftData.isBlanced&&rightdata.isBlanced)&&(Math.abs(leftData.height-rightdata.height)<=1);
        return new ReturnType(isBalanced,height);
    }

判断一颗二叉树是不是满二叉树

满二叉树:条件 节点数 = 2^二叉树高度 -1

public boolean isFull(TreeNode root){
        if (root == null) return true;
        Info data=processInfo(root);
       return data.nodes == (1 << data.height -1);
    }
    public class Info{
        public int height;
        public int nodes;
        public Info(int h,int n){
            height=h;
            nodes=n;
        }
    }
    public Info processInfo(TreeNode x){
        if (x == null) return new Info(0,0);
        Info leftData=processInfo(x.left);
        Info rightData=processInfo(x.right);
        int height = Math.max(leftData.height,rightData.height)+1;
        int nodes = leftData.nodes+rightData.nodes;
        return new Info(height,nodes);
    }

最近公共祖先-LCA

思路:哈希表,栈,(栈是模拟递归来实现的)
重点就是怎么保存父类的信息
哈希表,生成一个哈希表用来存放每个节点的父节点
然后O1节点只要不是根节点就往上遍历,直到遍历结束-生成一个路径
然后O2节点去这个路径中判断

 /*最近公共祖先--哈希表不抽象*/
    public TreeNode LCA(TreeNode root ,TreeNode n1,TreeNode n2){
        HashMap<TreeNode,TreeNode> fatherMap=new HashMap<>();
        fatherMap.put(root,root);
        processLCA(root,fatherMap);
        HashSet<TreeNode> set=new HashSet<>();
        TreeNode cur=n1;
        while (cur != fatherMap.get(cur)){
            set.add(cur);
            cur=fatherMap.get(cur);
        }
        cur=n2;
        while (cur != fatherMap.get(cur)){
            if (set.contains(cur)){
                return cur;
            }
            cur=fatherMap.get(cur);
        }
        return null;

    }
    private void processLCA(TreeNode root,HashMap<TreeNode,TreeNode> fatherMap){
        if (root == null)return;
        fatherMap.put(root.left,root);
        fatherMap.put(root.right,root);
        processLCA(root.left,fatherMap);
        processLCA(root.right,fatherMap);
    }

    /*最近公共祖先,递归方式-抽象
    * 要分类:n1 是 n2 的最近公共祖先或 n2 是 n1 的最近公共祖先
    * n1 与n2 不为公共组向,要继续往上找*/
    public TreeNode LCA2(TreeNode root ,TreeNode n1,TreeNode n2){
        if (root == null || root == n1 || root == n2){
            return root;
        }
        TreeNode left=LCA2(root.left,n1,n2);
        TreeNode right=LCA2(root.right,n1,n2);
        if (left != null && right != null){
            //这个说明head是最初的汇聚点
            return root;
        }
        /*左右俩个树,谁不为空返回谁*/
        return left!=null?left:right;
    }

寻找二叉树的后继节点

后继节点:中序遍历的下一个节点

最简单思路,肯定是中序遍历得到一个序列,然后找到后继节点
当如果有父亲指针的话,你肯定就不能用中序了要优化形成O(路径)
第一种情况: X 有右树的时候–那他的后继节点就是它的右树的最左节点
第二种情况: X 没有右树的时候,X是不是X父亲的左孩子,如果则这个父亲就是后继(这种情况X一定是Y树最后打印的节点)
第三种情况:整个树的最后一个节点是没有后继的,或者为空

/*找这棵树的后继节点---默认这棵树是有父亲节点的*/
    public TreeNode getSuccessTreeNode(TreeNode node){
        if (node == null)return node;
        *//*第一种情况*//*
        if (node.right != null){
            while (node.left !=null){
                node=node.left;
            }
            return node;
        }else {
            *//*第二种情况: 无右子树*//*
            TreeNode parent=node.parent;
            *//*parent != null这个条件就是判断node是最右节点的情况*//*
            while (parent != null && parent.left != node){
                node=parent;
                parent=node.parent;
            }
            return parent;
        }
    }

二叉树的序列化和反序列化

在这里插入图片描述

序列化很简单
反序列化就有点意思了
序列化,规定以下划线_分割,如果是空则是#
反序列化,形成一个队列,你是怎样序列化的就怎样去递归的去调用

 public String serialByPre(TreeNode root){
        if (root == null)return "#_";
        String res=root.val+"_";
        res+=serialByPre(root.left);
        res+=serialByPre(root.right);
        return res;
    }
    public TreeNode reconByPreString(String preStr){
        String[] values = preStr.split("_");
        Queue<String> queue=new LinkedList<>();
        for (int i = 0; i != values.length ; i++) {
            queue.add(values[i]);
        }
        return reconPreOrder(queue);
    }
    public TreeNode reconPreOrder(Queue<String> queue){
        String value= queue.poll();
        if (value.equals("#")){
            return null;
        }
        TreeNode node=new TreeNode(Integer.valueOf(value));
        node.left=reconPreOrder(queue);
        node.right=reconPreOrder(queue);
        return node;
    }

折纸问题

在这里插入图片描述

当你把这张纸折三次,然后构建成二叉树,你会发现输出规律真好是中序遍历

/*折纸问题*/
    public void printALLFolds(int N){
        printProcess(1,N,true);
    }
    /*递归的过程来到某一个节点
    * i是节点的层数,N是一共的层数,flg==true表示是凹  flg==false表示是凸*/
    private void printProcess(int i,int N,boolean flg){
        if (i > N)return;
        printProcess(i+1,N,true);
        System.out.println(flg?"凹":"凸");
        printProcess(i+1,N,false);
    }

根据前序和中序来构建二叉树

解题思路自然是根据:先序来每次找到根节点,然后在递归的调用

public int i = 0;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        
       return buildTreeChild(preorder,inorder,0,preorder.length-1);
    }

    public TreeNode buildTreeChild(int[] preorder, int[] inorder,
                                   int inbegin,int inend) {
        if(inbegin > inend) {
            return null;
        }
        TreeNode root = new TreeNode(preorder[i]);
//找到当前根,在中序遍历的位置
        int rootIndex = findIndex(inorder,inbegin,inend,preorder[i]);
        i++;
        root.left = buildTreeChild(preorder,inorder,inbegin,rootIndex-1);
        root.right = buildTreeChild(preorder,inorder,rootIndex+1,inend);
        return root;
    }

    private int findIndex( int[] inorder,int inbegin,int end, int key) {
        for (int j = inbegin; j <= end; j++) {
            if (key==inorder[j]){
                return j;
            }
        }
        return -1;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值