二叉树拔高训练

1.判断是否是完全二叉树

  • 思路:
  1. 确定一个队列,只要树的根非空就进队列;
  2. 队列只要非空,就弹出对头元素给cur节点;
  3. cur非空就将cur的左子树节点和右边节点入栈;
  4. 只要cur为空,直接跳出循环;
  5. 接着判断队列是否为空,非空就弹出对头元素;
    弹出队头只会出现俩种情况:
    • 对于完全二叉树,队列所有元素都是null;
    • 对于不是完全二叉树,队列里面的元素就是非null的元素;
  6. 因此接下来的条件就是只要对头弹出非null的元素,之间返回false;
  7. 否则就一直到队列为空,说明弹出的队列元素就是null,就返回true;
    • 完全二叉树的情况,队列,最后全是null
      在这里插入图片描述
      2.非完全二叉树,队列里面出现了非null的元素
      在这里插入图片描述
// 判断一棵树是不是完全二叉树
    boolean isCompleteTree(Node root) {
    
    
        if(root == null) {
    
    
            return true;
        }
        Queue<Node> queue = new LinkedList<>();//队列
        queue.offer(root);
        while (!queue.isEmpty()) {
    
    
            Node cur = queue.poll();
            if(cur != null){
    
    
                queue.offer(cur.left);
                queue.offer(cur.right);
            }else{
    
    
                break;
            }
        }
        //判断队列剩下的元素  是否有非空的元素
        while(!queue.isEmpty()){
    
    
            Node cur = queue.peek();
            if(cur != null) {
    
    
                return false;
            }else {
    
    
                queue.poll();
            }
        }
            return true;
    }

//完整测试代码

import java.lang.Math;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/*
孩子表示法
 */
class Node {
    
    
    char val;
    Node left;
    Node right;
    //构造方法
    public Node(char val) {
    
    
        this.val = val;
    }
}
public class BinaryTree {
    
    
    //构造一颗树
    public  Node createTree(){
    
    
          Node A = new Node('A');
          Node B = new Node('B');
          Node C = new Node('C');
          Node D = new Node('D');
          Node E = new Node('E');
          Node F = new Node('F');
          Node G = new Node('G');
          //Node H = new Node('H');
          A.left = B;
          A.right = C;
          B.left = D;
          B.right = E;
          //E.right = H;
          C.left = F;
          C.right = G;
          return A;
      }
      
      // 判断一棵树是不是完全二叉树
    boolean isCompleteTree(Node root) {
    
    
        if(root == null) {
    
    
            return true;
        }
        Queue<Node> queue = new LinkedList<>();//队列
        queue.offer(root);
        while (!queue.isEmpty()) {
    
    
            Node cur = queue.poll();
            if(cur != null){
    
    
                queue.offer(cur.left);
                queue.offer(cur.right);
            }else{
    
    
                break;
            }
        }
        //判断队列剩下的元素  是否有非空的元素
        while(!queue.isEmpty()){
    
    
            Node cur = queue.peek();
            if(cur != null) {
    
    
                return false;
            }else {
    
    
                queue.poll();
            }
        }
            return true;
    }
}

//测试类
class TestDemo{
    
    
    public static void main(String[] args) {
    
    
        BinaryTree binaryTree = new BinaryTree();//创建对象
        //构造一棵树,通过调用BinaryTree类里面的createTree()方法
        Node root = binaryTree.createTree();
    System.out.println("是否是完全二叉树: "+binaryTree.isCompleteTree(root));
    }
}

2.二叉树的最近公共祖先

  • 公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。

  • 思路:
    1.对于一棵二叉树,如果它的根都为空,那么也就不存在什么公共祖先的说法,直接返回null;
    2.如果p,q都是指向根节点(同一个节点),那么祖先就是这个根节点;
    3.那么接下来根的情况已经考虑完,就利用子问题的思想,同样遍历根的左子树和右子树;
    4.那么就会有下面图片的几种情况

    • p,q一个在左子树,一个在右边子树,他们的公共祖先肯定是root;
    • p,q都在左子树,祖先就是左节点它本身§,也就是图片第二种情况,返回root.eft;
    • 同样它俩全在右子树,也就是第三种情况返回root.right;
    • 最后遍历完毕,都有没有找到p,q,直接返回null;
  • p,q可能的情况
    在这里插入图片描述

//二叉树的最近公共祖先
public TreeNode lowestCommonAncestor(Node root, Node p, Node q) {
    
    
        if(root == null){
    
    
            return null;
        }
        if(root == p || root == q){
    
    
            return root;
        }
     
        Node l = lowestCommonAncestor(root.left,p,q);
        Node r = lowestCommonAncestor(root.right,p,q);
        if (l != null && r != null){
    
    
            return root;
        }
        if (l != null){
    
    
            return l;        }
        if (r != null){
    
    
            return r;
        }
        return null;
    }

3.二叉搜索树转为双向链表(中序遍历的方式)

//二叉搜索树转为双向链表
    public Node prev = null;
    public  void ConvertChild(Node root) {
    
    
        if(root == null) return ;
        ConvertChild(root.left);
        root.left = prev;
        if(prev != null) {
    
    
            prev.right = root;
        }
        prev = root;
        ConvertChild(root.right);
    }

    public  Node Convert(Node pRootOfTree) {
    
    
        if(pRootOfTree == null) return null;
        ConvertChild(pRootOfTree);
        Node head = pRootOfTree;
        while (head.left != null) {
    
    
            head = head.left;
        }
        return head;
    }

4.二叉树创建字符串

//前序遍历的方式,二叉树转字符串
   //被调用方法
    public void tree2strChild(Node t,StringBuilder sb) {
    
    
        if(t == null) return;
        sb.append(t.val);
        if(t.left == null) {
    
    
            if(t.right == null) {
    
    
                return;
            }else {
    
    
                sb.append("()");
            }
        }else {
    
    
            sb.append("(");
            tree2strChild(t.left,sb);
            sb.append(")");
        }

        if(t.right == null) {
    
    
            return;
        }else {
    
    
            sb.append("(");
            tree2strChild(t.right,sb);
            sb.append(")");
        }
    }
//主调方法
    public String tree2str(Node t) {
    
    
        if(t == null) return null;
        StringBuilder sb = new StringBuilder();
        tree2strChild(t,sb);
        return sb.toString();
    }

5.根据前序遍历和中序遍历确定二叉树

//根据前序遍历和中序遍历确定二叉树
    public int preIndex = 0;
    public Node buildTreeChild(char[] preorder,
                               char[] inorder,int inbegin,int inend) {
    
    
        if(inbegin > inend) {
    
    
            return null;
        }
        Node root = new Node(preorder[preIndex]);
        int inorderIndex = findInoderIndexOfRoot(inorder,
                inbegin,inend,preorder[preIndex]);
        preIndex++;
        root.left = buildTreeChild(preorder,
                inorder,inbegin,inorderIndex-1);
        root.right = buildTreeChild(preorder,
                inorder,inorderIndex+1,inend);
        return root;
    }

    public int findInoderIndexOfRoot(char[] inorder,
                                     int inbegin,int inend,char val) {
    
    
        for (int j = inbegin; j <= inend; j++) {
    
    
            if(inorder[j] == val) {
    
    
                return j;
            }
        }
        return -1;
    }


    public Node buildTree(char[] preorder, char[] inorder) {
    
    
        if(preorder == null || inorder == null) {
    
    
            return null;
        }
        if(preorder.length == 0 || inorder.length == 0) {
    
    
            return null;
        }
        return buildTreeChild(preorder,inorder,
                0,inorder.length-1);
    }

7.层次遍历二叉树

  • 思路
    1.首先用一个队列,来保证数据的顺序性;
    2.判断树是否为空
    3.如果非空,将根就入队,接着判断队列是否为空,非空,就弹出队头元素,赋值给cur节点,然后判断左子树和右边子树,非空就入栈, 并且将cur节点打印;
    4.然后直到队列为空,就层次遍历完成;
    在这里插入图片描述
//层次遍历(加入一个队列)
    void levelOrderTraversal(Node root) {
    
    
        if(root == null) {
    
    
            return;
        }
        Queue<Node> queue = new LinkedList<>();//队列对象
        queue.offer(root);
        while (!queue.isEmpty()) {
    
    
            Node cur = queue.poll();
            System.out.print(cur.val);
            if (cur.left != null){
    
    
                queue.offer(cur.left);
            }
            if (cur.right != null){
    
    
                queue.offer(cur.right);
            }
        }
    }
  • 第二种方法思路
    1.首先创建一个List<List> ret = new ArrayList<>();//相当于二维数组,里面放的是字符型数据(Character),见下图;
    2.然后在建立一个rowlist,相当于每一行,这样树的每一层就会对应一行,这样就可以按层打印;
    在这里插入图片描述
//第二种方法,进行层次遍历
//二叉树的层次遍历(输出是按照一层一层输出的)
    public List<List<Character>> levelOrder(Node root) {
    
    
        List<List<Character>> ret = new ArrayList<>();//相当于二维数组,里面放的是字符型数据(Character)
        if(root == null) {
    
    
            return ret;
        }
        //队列用来保证遍历当前顺序性
        Queue<Node> queue = new LinkedList<>();//队列
        queue.offer(root);
        while (!queue.isEmpty()){
    
    
            //用来存放每一层的数据
            List<Character> rowlist = new ArrayList<>();
            int count = queue.size();
            while(count != 0){
    
    
                //出栈,cur存放出栈的节点,并且判断后续是否有其他树节点需要进队列
                Node cur = queue.poll();//出栈
                rowlist.add(cur.val);
                if (cur.left != null){
    
    
                    queue.offer(cur.left);
                }
                if (cur.right != null){
    
    
                    queue.offer(cur.right);
                }
                count--;//用来分层,当一层弹出完毕,count就变为0
            }
            ret.add(rowlist);   
        }
        return ret;
    }

8.输出二叉树最多的层的节点个数

//第一种情况,宽度不包括null节点

  • 思路
    1.上面第7题第二种情况我们已经进行了层次遍历,
    2.我们将每一层都放在了一个rowlist里面,那么我们现在要找节点最多的,不就相当于只要比较出来,rowlist长度最大的,不就是我们需要的结果吗,因此我们在上面代码的基础上,增加一个max变量,每次得到一个rowlist就计算出来长度和max比较,然后比max大,则将它的长度作为新的max;
    3.直到每一次都比较完毕,这个时候我们max里面放的就是最大的;

:题的扩展,1.那么我们想要知道每一次最左边的是不是也很容易,直接输出每一个rowlist的第一个元素即可;
2.同理,最右边的就是最后一个元素;(大家有兴趣,可以尝试一下,加油!)

  • 对于下图的的二叉树来说,最多的层的节点个数是4,因为第一层是节点是1个,第二层节点数是2个,第三层是4个,第四层只有一个;
    在这里插入图片描述
import java.lang.Math;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/*
孩子表示法
 */
class Node {
    
    
    char val;
    Node left;
    Node right;
    //构造方法
    public Node(char val) {
    
    
        this.val = val;
    }
}
public class BinaryTree {
    
    
    //构造一颗树
    public  Node createTree(){
    
    
          Node A = new Node('A');
          Node B = new Node('B');
          Node C = new Node('C');
          Node D = new Node('D');
          Node E = new Node('E');
          Node F = new Node('F');
          Node G = new Node('G');
          Node H = new Node('H');
          A.left = B;
          A.right = C;
          B.left = D;
          B.right = E;
          E.right = H;
          C.left = F;
          C.right = G;
          return A;
      }
//二叉树的层次遍历(输出是按照一层一层输出的)https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
    public List<List<Character>> levelOrder(Node root) {
    
    
        List<List<Character>> ret = new ArrayList<>();//相当于二维数组,里面放的是字符型数据(Character)
        if(root == null) {
    
    
            return ret;
        }
        //队列用来保证遍历当前顺序性
        Queue<Node> queue = new LinkedList<>();//队列
        queue.offer(root);
        int max = 0;//用来实现另外一个功能,求出哪层所拥有的节点最多少;
        while (!queue.isEmpty()){
    
    
            //用来存放每一层的数据
            List<Character> rowlist = new ArrayList<>();
            int count = queue.size();
            while(count != 0){
    
    
                //出栈,cur存放出栈的节点,并且判断后续是否有其他树节点需要进队列
                Node cur = queue.poll();//出栈
                rowlist.add(cur.val);
                if (cur.left != null){
    
    
                    queue.offer(cur.left);
                }
                if (cur.right != null){
    
    
                    queue.offer(cur.right);
                }
                count--;//用来分层,当一层弹出完毕,count就变为0
            }
            ret.add(rowlist);
            if (rowlist.size() > max){
    
    
                max = rowlist.size();
            }
        }
        System.out.println("层的最多节点数目: "+max);
        return ret;
    }}

//测试类
class TestDemo{
    
    
    public static void main(String[] args) {
    
    
        BinaryTree binaryTree = new BinaryTree();//创建对象
        //构造一棵树,通过调用BinaryTree类里面的createTree()方法
        Node root = binaryTree.createTree();
        binaryTree.levelOrder(root);//调用求最大节点的方法
        
    }
}

在这里插入图片描述
//第二种情况,宽度包括null节点

  • 例如下面这颗树,它的层上面节点最多的是在第三层,有四个节点[5,3,null,9];
    在这里插入图片描述
  • 思路:将根节点的的下标定为i,那么它的左节点的下标就是2i,右节点的下标就是2i+1;那么最后的宽度,就是一层最右边的下标减去一层最左边的下标,再加1;
int max = 0;
    public int widthOfBinaryTree(Node root) {
    
    
        isCompleteTree(root);
        return max;
    }
    
    public void isCompleteTree(Node root) {
    
    
        if(root == null) {
    
    
            return;
        }
        Queue<Node > q =new LinkedList<>();//队列用来保证数据存放的顺序性
        LinkedList<Integer> list = new LinkedList<>();//链表用来存储层次遍历过后的二叉树
        q.offer(root);//如果根非空,直接入栈
        list.add(1);//并且将根的下标弄为1,并且进入链表中
        int res = 1;
        //开始进入队列,只要队列非空,就弹出队头元素
        while (!q.isEmpty()) {
    
    
            //为了能够将一层数据弹出,因此设置一个count来存储一层的个数
            int count = q.size();
            //进入循环
            for(int i =0;i < count;i++){
    
    
                Node cur = q.poll();//开始弹出队头元素
                Integer curIndex = list.removeFirst();//因为之前已经直接将根的下标放为1,加入到了链表里面,将它移出来,用来标记它子树的下标
                if(cur.left != null){
    
    
                    q.offer(cur.left);
                    list.add(curIndex * 2);
                }
                if(cur.right != null) {
    
    
                    q.offer(cur.right);
                    list.add(curIndex * 2 +1);
                }
            }
            //从for中出来,一层已经遍历完毕
            //然后计算这一层的宽度,就是链表的最后一个元素减去第一个元素
            max = res;
            if(list.size() >= 2) {
    
    
                res = Math.max(res, list.getLast() - list.getFirst() + 1);
                max = res;
            }
        }
}

猜你喜欢

转载自blog.csdn.net/qq_45665172/article/details/109585321
今日推荐