美文网首页
二叉树基础

二叉树基础

作者: 范柏柏 | 来源:发表于2020-05-11 20:57 被阅读0次

聊二叉树之前,先看看什么是树。


一些树.png

“树”这种数据结构真的很像我们现实生活中的“树”,这里面每个元素我们叫作“节点”;用来连线相邻节点之间的关系,我们叫作“父子关系”。且子节点只能有一个父节点。

一棵树.png

入上图。B节点是D节点的父节点,D节点是B节点的子节点。D、E、F的父节点是同一个节点,所以他们之间互相称为兄弟节点
我们把没有父节点的节点称为根节点,也就是节点A。
把没有子节点的节点称为叶子节点,也就是I、J、K、F、G、H

关于树,有三个概念:高度深度

高度:节点到叶子节点的最长边数
深度:节点到根节点的边数
层数:节点的深度 + 1
树高:根节点的高度

树的三个概念.png

二叉树

二叉树,顾名思义,每个节点最多有两个叉,也就是两个子节点,分别称为左子节点右子节点

两种特殊的二叉树.png

如图,二叉树里有两种比较特殊的二叉树。
满二叉树:叶子节点全部都在最底层,除叶子节点之外,每个节点都有左右两个子节点。
完全二叉树:叶子节点都在最底下两层,最后一层叶子节点都靠左排列,并且除了最后一层,其他层的节点个数都是满的。

满二叉树比较好理解。为什么把完全二叉树当做一个特殊的二叉树呢??这就要讲到如何存储一棵二叉树了。

二叉树的存储

想要存一棵二叉树,有两种办法,一种是基于指针的链表法,一种是基于数组的顺序存储法。

链式存储法

这个比较简单,直接上图。


链式存储法.png

顺序存储法

基于数组的顺序存储法,我们把根节点存储在下标 i=1的位置,那左子节点存储在下标 2i = 2 的位置,右子节点存储在 2i + 1 = 3 的位置。以此类推。B节点的左子节点存储在 22=4的位置,右子节点存储在22+1 = 5的位置。

顺序存储法.png

如果是完全二叉树,可以发现,数组的格子是被沾满的。如果不是完全二叉树,少哪个节点,哪个下标是空着的。这也是为什么把完全二叉树拎出来的原因。

二叉树的遍历

经典的三种方案:前序遍历中序遍历后序遍历

  • 前序遍历:对于树的任意节点来说,先打印这个节点,然后打印它的左子树,最后打印它的右子树。
  • 中序遍历:对于树的任意节点来说,先打印这个节点的左子树,然后在打印它本身,最后打印它的右子树。
  • 后序遍历:对于树的任意节点来说,先打印这个节点的左子树,然后在打印它的右子树,最后打印它本身。

以上图顺序存储法的图为例

  • 前序遍历:A -> B -> D -> H -> I -> E -> J -> C -> F -> G
  • 中序遍历:H -> D -> I -> B -> J -> E -> A -> F -> C -> G
  • 后序遍历:H -> I -> D -> J -> E -> B -> F -> G -> C -> A

接下来上代码

public class BinaryTree {

    /**
     * 前序遍历
     * 自己 -> 左子树 -> 右子树
     */
    public static void preOrder(TreeNode node) {
        if (node != null) {
            visited(node);
            preOrder(node.leftChild);
            preOrder(node.rightChild);
        }
    }

    /**
     * 非递归前序遍历
     *
     * 自己实现压栈
     */
    public static void noRecPreOrder(TreeNode node) {
        Stack<TreeNode> stack = new Stack();
        TreeNode pNode = node;
        while (pNode != null || stack.size() > 0) {

            while (pNode != null) {

                // 先打印自己
                visited(pNode);
                // 将自己压入栈
                stack.push(pNode);
                // 依次把左子树都执行一遍
                pNode = pNode.leftChild;
            }

            // 此时 最后一个节点的左子树已经都打印完了
            // 开始打印右子树
            if (stack.size() > 0) {
                pNode = stack.pop();
                pNode = pNode.rightChild;
            }
        }
    }

    /**
     * 中序遍历
     * 左子树 -> 自己 -> 右子树
     */
    public static void inOrder(TreeNode node) {
        if (node != null) {
            inOrder(node.leftChild);
            visited(node);
            inOrder(node.rightChild);
        }
    }

    /**
     * 非递归中序遍历
     *
     * 自己实现压栈
     */
    public static void noRecInOrder(TreeNode node) {
        Stack<TreeNode> stack = new Stack();
        TreeNode pNode = node;
        while (pNode != null || stack.size() > 0) {

            while (pNode != null) {

                // 先把左子树压栈
                stack.push(pNode);
                // 一直压倒左子树最后一个节点
                pNode = pNode.leftChild;
            }

            // 此时 最后一个节点的左子树都已经压进来了
            // 打印自己,将自己的右子树压栈,下一轮循环,先出栈的,就是自己的右子树
            if (stack.size() > 0) {
                pNode = stack.pop();
                visited(pNode);
                pNode = pNode.rightChild;
            }
        }
    }

    /**
     * 后序遍历
     * 左子树 -> 右子树 -> 自己
     */
    public static void postOrder(TreeNode node) {
        if (node != null) {
            postOrder(node.leftChild);
            postOrder(node.rightChild);
            visited(node);
        }
    }

    /**
     * 非递归后序遍历
     *
     * 自己实现压栈
     */
    public static void noRecPostOrder(TreeNode node) {
        Stack<TreeNode> stack = new Stack();
        TreeNode pNode = node;
        TreeNode lastVisit = node;
        while (pNode != null || !stack.isEmpty()) {

            while (pNode != null) {

                // 先把左子树压栈
                stack.push(pNode);
                // 一直压倒左子树最后一个节点
                pNode = pNode.leftChild;
            }

            // 查看当前栈顶元素
            pNode = stack.peek();

            // 如果其右子树也为空,或者右子树已经被访问
            // 那么就可以直接打印自己
            if (pNode.rightChild == null || pNode.rightChild == lastVisit) {
                visited(pNode);
                stack.pop();
                lastVisit = pNode;
                pNode = null;
            } else {
                // 否则,继续遍历右子树
                pNode = pNode.rightChild;
            }
        }
    }

    /**
     * 层序遍历
     */
    public static void layerOrder(TreeNode node) {

        ArrayDeque<TreeNode> queue = new ArrayDeque<>();
        queue.addLast(node);

        while (!queue.isEmpty()) {

            TreeNode current = queue.pop();
            // 先打印自己
            visited(current);
            //在将自己的所有节点一次入队列
            if (current.leftChild != null) {
                queue.addLast(current.leftChild);
            }
            if (current.rightChild != null) {
                queue.addLast(current.rightChild);
            }
        }
    }

    /**
     * 打印节点
     */
    private static void visited(TreeNode node) {
        node.isVisited = true;
        System.out.println(node.data+","+node.key);
    }

    /**
     * 计算树的高度
     */
    private int height(TreeNode node){
        if(node == null){
            return 0;
        }else{
            int i = height(node.leftChild);
            int j = height(node.rightChild);
            return (i<j)?j+1:i+1;
        }
    }

    /**
     * 计算树的节点数
     */

    private int size(TreeNode node){
        if(node == null){
            return 0;
        }else{
            return 1+size(node.leftChild)+size(node.rightChild);
        }
    }

    /**
     * 创建二叉树
     */
    public static void createBinaryTree(TreeNode root){
        TreeNode nodeB = new TreeNode(2, "B");
        TreeNode nodeC = new TreeNode(3, "C");
        TreeNode nodeD = new TreeNode(4, "D");
        TreeNode nodeE = new TreeNode(5, "E");
        TreeNode nodeF = new TreeNode(6, "F");
        TreeNode nodeG = new TreeNode(7, "G");
        TreeNode nodeH = new TreeNode(8, "H");
        TreeNode nodeI = new TreeNode(9, "I");
        TreeNode nodeJ = new TreeNode(10, "J");
        root.leftChild = nodeB;
        root.rightChild = nodeC;
        nodeB.leftChild = nodeD;
        nodeB.rightChild = nodeE;
        nodeD.leftChild = nodeH;
        nodeD.rightChild = nodeI;
        nodeE.leftChild = nodeJ;
        nodeC.leftChild = nodeF;
        nodeC.rightChild = nodeG;
    }

    /**
     * 定义根节点
     */
    private TreeNode root;

    public TreeNode getRoot() {
        return root;
    }

    public void setRoot(TreeNode root) {
        this.root = root;
    }

    public BinaryTree() {
        this.root = new TreeNode(1, "A");
    }

    /**
     * 节点数据结构
     */
    private static class TreeNode {
        private int key = 0;
        private String data = null;
        private boolean isVisited = false;
        private TreeNode leftChild = null;
        private TreeNode rightChild = null;


        public TreeNode(){}

        public TreeNode(int key, String data) {
            this.key = key;
            this.data = data;
        }
    }

    public static void main(String[] args) {
        BinaryTree binaryTree = new BinaryTree();
        TreeNode root = binaryTree.root;
        createBinaryTree(root);
        System.out.println("=============前序遍历=================");
        preOrder(root);
        System.out.println("=====================================");
        System.out.println("=============中序遍历=================");
        inOrder(root);
        System.out.println("=====================================");
        System.out.println("=============后续遍历=================");
        postOrder(root);
        System.out.println("=====================================");

        System.out.println("=============前序遍历 非递归=================");
        noRecPreOrder(root);
        System.out.println("=====================================");
        System.out.println("=============中序遍历 非递归=================");
        noRecInOrder(root);
        System.out.println("=====================================");
        System.out.println("=============后续遍历 非递归=================");
        noRecPostOrder(root);
        System.out.println("=====================================");

        System.out.println("=============层序遍历 非递归=================");
        layerOrder(root);
        System.out.println("=====================================");
    }
}

代码github

https://github.com/handsomebai/arithmetic

相关文章

  • LeetCode基础算法-树

    LeetCode基础算法-树 LeetCode 树 基础算法 1. 二叉树的最大深度 给定一个二叉树,找出其最大深...

  • 树数据结构-力扣刷树题需要知道的(Python)

    树是一种重要的数据结构,而二叉树是其中的重点和难点,有关二叉树的基础知识,读者可移步【二叉树基础】查看更多内容。这...

  • 二叉树的基本算法

    二叉树的基本算法 树、二叉树 的基本概念,参考数据结构算法之美-23讲二叉树基础(上):树、二叉树[https:/...

  • Java后端开发算法基础面试题分享,你离大厂也许就差这份面试题!

    一、算法基础 1. 重建二叉树 题目: 输入一棵二叉树前序遍历和中序遍历的结果,请重建该二叉树。 注意: 二叉树中...

  • 二叉树知识(BST) 二叉查找树(Binary Search T

    二叉树基础知识总结 - CSDN博客 二叉树遍历分析 简单二叉树遍历,可分为:先序,中序,后序。 先序: 1.访问...

  • 二叉树非递归遍历

    二叉树的非递归遍历 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的。对于二叉树,有...

  • 数据结构第三次作业

    第一题:二叉树的基础算法 题目 先根据二叉树的先序、中序遍历序列还原该二叉树,求出这棵二叉树的高度和宽度,以及其叶...

  • Avl平衡树--C语言实现

    Avl 平衡树 实现记录 Avl平衡二叉树和搜索二叉树基本实现原理相同,在搜索二叉树的基础上添加树平衡的操作--单...

  • 二叉树的遍历

    关于二叉树的算法问题,一般都以二叉树的遍历为基础,这里给出二叉树的多种遍历方式 树结构: 树结点的定义及其构建: ...

  • 学习清单

    算法、数据结构二叉树、链表、栈... golang基础 swoole mysql websocket

网友评论

      本文标题:二叉树基础

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