二叉树复习

作者: 盼旺 | 来源:发表于2019-08-15 19:27 被阅读12次

现实生活中树

数据结构中树长这样子


二叉树的意思就是说:每个节点不能多于有两个儿子,上面的图就是一颗二叉树。

关键知识点

  • 一棵树至少会有一个节点(根节点)
  • 树由节点组成,每个节点的数据结构是这样的:

    节点的定义就是:一个数据、两个指针(如果有节点就指向节点、没有节点就指向null)
  • 二叉树第i层上的结点数目最多为2的(i-1)次方2^(i-1)(i>=1)
  • 深度为k的二叉树至多2k-1(k>=1)个结点

二叉树的遍历

四种主要的遍历思想为:
前序遍历:根结点 ---> 左子树 ---> 右子树
中序遍历:左子树---> 根结点 ---> 右子树
后序遍历:左子树 ---> 右子树 ---> 根结点
层次遍历:只需按层次遍历即可

静态创建二叉树

树是由若干个节点组成,节点连接起来就成了树,而节点由一个数据、两个指针组成
因此,创建树实际上就是创建节点,然后连接节点

举个栗子:这个二叉树为例来构建

定义节点类
package tree;
public class TreeNode {
    // 左节点(儿子)
    private TreeNode lefTreeNode;
    
    // 右节点(儿子)
    private TreeNode rightNode;
    
    // 数据
    private int value;
    
    public TreeNode() {
        super();
    }
    public TreeNode(int value) {
        super();
        this.value = value;
    }
    public TreeNode getLefTreeNode() {
        return lefTreeNode;
    }
    public void setLefTreeNode(TreeNode lefTreeNode) {
        this.lefTreeNode = lefTreeNode;
    }
    public TreeNode getRightNode() {
        return rightNode;
    }
    public void setRightNode(TreeNode rightNode) {
        this.rightNode = rightNode;
    }
    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value;
    }   
}
创建上面二叉树代码
package tree;
public class TwoTreeDemo {
    public static void main(String[] args) {
        //先创建好这5个节点
        //根节点-->10
        TreeNode treeNode1 = new TreeNode(10);
        //左孩子-->9
        TreeNode treeNode2 = new TreeNode(9);
        //右孩子-->20
        TreeNode treeNode3 = new TreeNode(20);
        //20的左孩子-->15
        TreeNode treeNode4 = new TreeNode(15);
        //把他们连接起来
        //20的右孩子-->35
        TreeNode treeNode5 = new TreeNode(35);
      //根节点的左右孩子
        treeNode1.setLefTreeNode(treeNode2);
        treeNode1.setRightNode(treeNode3);
        //20节点的左右孩子
        treeNode3.setLefTreeNode(treeNode4);
        treeNode3.setRightNode(treeNode5);
    }
}

遍历代码

前序遍历10->9->20->15->35

public static void preTraverseBTree(TreeNode rootTreeNode) {
            if (rootTreeNode != null) {
                //访问根节点
                System.out.println(rootTreeNode.getValue());
                //访问左节点
                preTraverseBTree(rootTreeNode.getLefTreeNode());
                //访问右节点
                preTraverseBTree(rootTreeNode.getRightNode());
            }
        }
前序遍历
中序遍历9->10->15->20->35
      public static void inTraverseBTree(TreeNode rootTreeNode) {
            if (rootTreeNode != null) {
                //访问左节点
                inTraverseBTree(rootTreeNode.getLefTreeNode());
                //访问根节点
                System.out.println(rootTreeNode.getValue());
                //访问右节点
                inTraverseBTree(rootTreeNode.getRightNode());
            }
        }

后序遍历9->15->35->20->10

public static void endTraverseBTree(TreeNode rootTreeNode) {
            if (rootTreeNode != null) {
                //访问左节点
                endTraverseBTree(rootTreeNode.getLefTreeNode());
                //访问右节点
                endTraverseBTree(rootTreeNode.getRightNode());
                //访问根节点
                System.out.println(rootTreeNode.getValue());
            }
        }

层序遍历10->9->20->15->35

public static void cengTraverseBTree(TreeNode rootTreeNode){
          //声明一个队列
          //LinkedBlockingQueue的容量是没有上限的,也可以选择指定其最大容量,它是基于链表的队列,此队列按 FIFO(先进先出)排序元素。
          //ArrayBlockingQueue在构造时需要指定容量
          Queue<TreeNode> queue = new LinkedBlockingQueue<>();      
            if (rootTreeNode == null) {
                return;
            }
            queue.add(rootTreeNode);
            while(!queue.isEmpty()) {
                TreeNode tempNode = queue.poll();
                System.out.println(tempNode.getValue());
                if(tempNode.getLefTreeNode()!=null)queue.add(tempNode.getLefTreeNode());
                if(tempNode.getRightNode()!=null)queue.add(tempNode.getRightNode());    
            }
        }

动态创建二叉树查找树

上面我们是手动创建二叉树的,一般地:都是给出一个数组给你,让你将数组变成一个二叉树,此时就需要我们动态创建二叉树了。
二叉树中还有一种特殊的二叉树:
二叉查找树(binary search tree)定义:
当前根节点的左边全部比根节点小,当前根节点的右边全部比根节点大。
假设我们有一个数组:int[] arrays = {3, 2, 1, 4, 5};

1.首先将3作为根节点
2.随后2进来了,我们跟3做比较,比3小,那么放在3的左边
3.随后1进来了,我们跟3做比较,比3小,那么放在3的左边,此时3的左边有2了,因此跟2比,比2小,放在2的左边
4.随后4进来了,我们跟3做比较,比3大,那么放在3的右边
5.随后5进来了,我们跟3做比较,比3大,那么放在3的右边,此时3的右边有4了,因此跟4比,比4大,放在4的右边
#无论任何一颗子树,左边都比根要小,右边比根要大
代码实现

因为是动态创建的,因此我们得用一个类来表示根节点

package tree;
public class RootNode {
    private TreeNode treeRoot;
    public TreeNode getTreeRoot() {
        return treeRoot;
    }
    public void setTreeRoot(TreeNode treeRoot) {
        this.treeRoot = treeRoot;
    }
}

创建二叉搜索树代码

package tree;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
public class SerrchTwoTree {
    public static void main(String[] args) {
        int[] arrays = {3, 2, 1, 4, 5};
        RootNode root = new RootNode();
        for(int value:arrays) {
            createtree(root, value);
        }
        System.out.println("前序遍历");
        preTraverseBTree(root.getTreeRoot());
        System.out.println("");
        System.out.println("中序遍历");
        inTraverseBTree(root.getTreeRoot());
        System.out.println("");
        System.out.println("后序遍历");
        endTraverseBTree(root.getTreeRoot());
        System.out.println("");
        System.out.println("层序遍历");
        cengTraverseBTree(root.getTreeRoot());
    }
    //rootNode 根节点 v需要创造的节点的值
    public static void createtree(RootNode rootNode,int v) {
        //如果树根为空(第一次访问),将第一个值作为根节点
        if(rootNode.getTreeRoot()==null) {
            TreeNode treeNode = new TreeNode(v);
            rootNode.setTreeRoot(treeNode);
        }else {
             //当前树根
            TreeNode tempNode = rootNode.getTreeRoot();
            while(tempNode!=null) {
                //当前值大于根值,往右边走
                if(v>tempNode.getValue()) {
                    //右边没有树根,那就直接插入 并且返回 记着返回哦
                    if(tempNode.getRightNode()==null) {
                        tempNode.setRightNode(new TreeNode(v));
                        return;
                    }else {
                        //如果右边有树根,到右边的树根去
                        tempNode = tempNode.getRightNode();
                    }
                }else {
                    if(tempNode.getLefTreeNode()==null) {
                        tempNode.setLefTreeNode(new TreeNode(v));
                        return;
                    }else {
                        tempNode=tempNode.getLefTreeNode();
                    }
                }
            }
        }
    }
    //前序遍历
      public static void preTraverseBTree(TreeNode rootTreeNode) {
            if (rootTreeNode != null) {
                //访问根节点
                System.out.print(rootTreeNode.getValue()+" ");
                //访问左节点
                preTraverseBTree(rootTreeNode.getLefTreeNode());
                //访问右节点
                preTraverseBTree(rootTreeNode.getRightNode());
            }
        }
      //中序遍历
      public static void inTraverseBTree(TreeNode rootTreeNode) {
            if (rootTreeNode != null) {
                //访问左节点
                inTraverseBTree(rootTreeNode.getLefTreeNode());
                //访问根节点
                System.out.print(rootTreeNode.getValue()+" ");
                //访问右节点
                inTraverseBTree(rootTreeNode.getRightNode());
            }
        }
    //后序遍历
      public static void endTraverseBTree(TreeNode rootTreeNode) {
            if (rootTreeNode != null) {
                //访问左节点
                endTraverseBTree(rootTreeNode.getLefTreeNode());
                //访问右节点
                endTraverseBTree(rootTreeNode.getRightNode());
                //访问根节点
                System.out.print(rootTreeNode.getValue()+" ");
            }
        }
      //层序遍历
      public static void cengTraverseBTree(TreeNode rootTreeNode){
          //声明一个队列
          //LinkedBlockingQueue的容量是没有上限的,也可以选择指定其最大容量,它是基于链表的队列,此队列按 FIFO(先进先出)排序元素。
          //ArrayBlockingQueue在构造时需要指定容量
          Queue<TreeNode> queue = new LinkedBlockingQueue<>();
            if (rootTreeNode == null) {
                return;
            }
            queue.add(rootTreeNode);
            while(!queue.isEmpty()) {
                TreeNode tempNode = queue.poll();
                System.out.print(tempNode.getValue()+" ");
                if(tempNode.getLefTreeNode()!=null)queue.add(tempNode.getLefTreeNode());
                if(tempNode.getRightNode()!=null)queue.add(tempNode.getRightNode());
            }
        }
}
结果

查询二叉树的深度

      public static int getHeight(TreeNode treeNode) {
            if (treeNode == null) {
                return 0; // 达到叶子返回0的孩子 然后递归回去加1
            } else {
                //左边的子树深度
                int left = getHeight(treeNode.getLefTreeNode());
                //右边的子树深度
                int right = getHeight(treeNode.getRightNode());
                int max = left;
                if (right > max) {
                    max = right;
                }
                return max + 1;
            }
        }
     // System.out.println(getHeight(treeNode1));

查询二叉树的最大值

如果是二叉查找树就是中序遍历的最后一个
步骤:

左边找最大值->递归
右边找最大值->递归
然后分别再与根节点的值比较
  public static int  getMax(TreeNode rootTreeNode) {
            if (rootTreeNode == null) {
                return -1;
            } else {
                //找出左边的最大值
                int left = getMax(rootTreeNode.getLefTreeNode());
                //找出右边的最大值
                int right = getMax(rootTreeNode.getRightNode());
                //与当前根节点比较
                int currentRootValue = rootTreeNode.getValue();
                //假设左边的最大
                int max = left;
                if (right > max) {
                    max = right;
                }
                if (currentRootValue > max) {
                    max = currentRootValue;
                }
                return max ;
            }
        }

前中/中后序重建二叉树

已知前序和中序遍历,可以确定一棵二叉树。
已知中序和后序遍历,可以确定一棵二叉树。
但是,已知前序和后序遍历,不能确定一棵二叉树。

已知前序和中序

步骤过程:

  • 前序数组中最左边的值就是树的头节点值,记为h,并用h生成头节点,记为head,然后在中序数组中找到h,假设位置是i,那么在中序数组中,i左边的数组就是头节点左子树的中序数组,假设长度为l,则左子树的先序数组就是先序数组中h往右长度也为l的数组。

  • 用左子树的先序和中序数组,递归整个过程建立左子树,返回的头节点记为left。

  • i右边的数组就是头节点右子树的中序数组,假设长度为r,先序数组中右侧等长的部分就是头节点右子树的先序数组。

  • 用右子树的先序和中序数组,递归整个过程建立右子树,返回的头节点记为right。

  • 把head的左孩子和右孩子分别设置left和right,返回head,过程结束。

你肯定一脸懵逼

看看例子
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}

  • 首先,根节点 是{ 1 }
    左子树是:前序{ 2,4,7 },中序{ 4,7,2 }
    右子树是:前序{ 3,5,6,8 } ,中序{ 5,3,8,6 }

  • 这时,如果我们把左子树和右子树分别作为新的二叉树(即左子树是:前序{ 2,4,7 },中序{ 4,7,2 }看成新的需要重构的二叉树),一直递归,则可以求出其根节点,左子树和右子树。

  • 这样,一直用这个方式,就可以实现重建二叉树。

假设有如下图二叉树


肉眼可得它的
前序:1,2,4,7,3,5,6,8
中序:4,7,2,1,5,3,8,6
后序:7,4,2,5,8,6,3,1
核心测试代码
      //主功能函数重构二叉树 根据前中序 返回根节点
        public static TreeNode CreateTreeByPreAndIn(int [] pre,int [] in) {
            if(pre == null || in == null){
                return null;
            }
            TreeNode root = PreAndIn(pre, in, 0, pre.length-1, 0, in.length-1);
            return root;
        }
      //核心递归
        public static TreeNode PreAndIn(int[] pre, int[] in, int preStart, int preEnd, int inStart, int inEnd) {
            TreeNode tree = new TreeNode(pre[preStart]);
            if (preStart == preEnd && inStart == inEnd) {
                return tree;
            }
            int root = 0;//root表示根节点在中序的位置
            //找到前序的第一个元素(即根元素)在中序的位置
            for(root= inStart; root <= inEnd; root++){
                if (pre[preStart] == in[root]) {
                    break;
                }
            }
            //左子树的长度
            int leftLength = root - inStart;
            //右子树的长度
            int rightLength = inEnd - root;
            if (leftLength > 0) {
                tree.setLefTreeNode(PreAndIn(pre, in, preStart+1, preStart+leftLength, inStart, root-1));
            }
            if (rightLength > 0) {
                tree.setRightNode(PreAndIn(pre, in, preStart+1+leftLength, preEnd, root+1, inEnd));
            }
            return tree;
        }

已知中序和后序一样思路

只不过后序是根节点在最后
例如输入后序遍历序列{7,4,2,5,8,6,3,1}和中序遍历序列{4,7,2,1,5,3,8,6}

  • 首先,根节点 是{ 1 }
    左子树是:后序{ 7,4,2 },中序{ 4,7,2 }
    右子树是:后序{ 5,8,6,3 } ,中序{ 5,3,8,6 }
    核心测试代码
 
        //主功能函数重构二叉树 根据中后序 返回根节点
        public static TreeNode CreateTreeByPrInAndEnd(int [] end,int [] in) {
            if(end == null || in == null){
                return null;
            }
            TreeNode root = EndAndIn(end, in, 0, end.length-1, 0, in.length-1);
            return root;
        }
        
      //核心递归
        public static TreeNode EndAndIn(int[] end, int[] in, int endStart, int endEnd, int inStart, int inEnd) {
            TreeNode tree = new TreeNode(end[endEnd]);
          
            if (endStart == endEnd && inStart == inEnd) {
                return tree;
            }
            int root = 0;//root表示根节点在中序的位置
            //找到前序的第一个元素(即根元素)在中序的位置
            for(root= inStart; root <= inEnd; root++){
                if (end[endEnd] == in[root]) {
                    break;
                }
            }
            //左子树的长度
            int leftLength = root - inStart;
            //右子树的长度
            int rightLength = inEnd - root;
            System.out.println(leftLength+" wg "+rightLength);
            
            if (leftLength > 0) {
                tree.setLefTreeNode(EndAndIn(end, in, endStart, endStart+leftLength-1, inStart, root-1));
            }
            if (rightLength > 0) {
                tree.setRightNode(EndAndIn(end, in, endStart+leftLength, endEnd-1, root+1, inEnd)); 
            }
            return tree;
        }
懵逼学习,有错欢迎指正!Thanks

文章参考:https://juejin.im/post/5ab5a01d518825555c1d9a24#heading-8

相关文章

  • 二叉树就是这么简单

    一、二叉树就是这么简单 本文撇开一些非常苦涩、难以理解的概念来讲讲二叉树,仅入门观看(或复习).... 首先,我们...

  • acm2

    acm2 复习上acm2 复习下树状数组线段树根据前序中序创建二叉树以及层次遍历输出镜像树c++ string

  • 二叉树复习

    现实生活中树 数据结构中树长这样子 关键知识点 一棵树至少会有一个节点(根节点) 树由节点组成,每个节点的数据结构...

  • 【金三银四】2022 Android面经实录

    复习点 具体链接 1 算法部分 打遍天下二叉树 https://github.com/xfhy/Android-N...

  • 二叉树的遍历-递归和非递归

    前言 最近准备面试 ,复习了一下数据结构 中的二叉树,整理了二叉树的前序、中序、后序、深度和广度遍历以及递归和非递...

  • 平衡二叉树复习

    定义 一棵,对于每一个节点,它的左右子树的高度差不超过1 树空呢?左右子树为0,当然也是平衡二叉树,而且高度定义为...

  • 先序中序后序遍历

    看到这个的时候又忘记该怎么思考数据结构的问题了,于是打算来复习一波。 顺便也复习了一下二叉树的遍历规则,写一下学习...

  • [树] 二叉树的复习

    Prerequisites log运算法则 满二叉树 vs 完全二叉树 深度为k的满二叉树的节点总数:2^0 + ...

  • 2018-11-20

    数据结构 复习了森林转换成二叉树,并写出先序中序序列和后续线索,学习哈夫曼树的构造

  • 数据结构与算法-二叉树02

    二叉树的定义 二叉树的特点 二叉树的五中基本形态 其他二叉树 斜二叉树 满二叉树 完全二叉树图片.png满二叉树一...

网友评论

    本文标题:二叉树复习

    本文链接:https://www.haomeiwen.com/subject/kkczjctx.html