树及二叉树

一、基本概念

  1. 节点的度 :节点拥有的子树数成为节点的度。

    • 叶子节点:度为0的节点称为叶子节点;
    • 分支节点:度不为0的节点称为分支节点;
    • 树的度:树内各节点的度的最大值;


      度的图解
  2. 树的层次与深度:树中节点的最大层次称为树的深度(高度)


    树的层次与深度

    注意:根节点为第一层;

二、树的存储

存储方式(存储思想):利用顺序存储链式存储共同来实现树的存储;

(一)双亲表示法

  1. 定义:在每个节点中,附设一个指示器指示其双亲节点在链表中的位置(即孩子节点指向父节点);(开发中常用做法)
    • 优点:从孩子找父节点容易;
    • 缺点:从父节点找孩子难;
  2. 节点的数据模型
模型图解
  • 举例:

    存储方式图解

(二)孩子表示法

  1. 特点:用父节点表示自己
    • 优点:找每个节点的孩子容易;
    • 缺点:
      1. 找到每个节点的父节点难;
      2. 存在数据冗余,内存浪费;
      3. 数组扩容难;
  2. 举例
    方案一:创建固定长度的数组,使得每个节点都有若干个指针指向自己的每一个孩子
    [图片上传失败...(image-21a424-1517935019947)]
    方案二:先用一个标记记录每个节点有几个孩子,然后根据这个标记创建数组,减少数据冗余;
    [图片上传失败...(image-2dec98-1517935019947)]

(三)孩子双亲表示法

  1. 做法:把每个节点的孩子节点排列起来,以单链表作为存储结构,则n个节点有n个孩子链表,如果是叶子节点则此单链表为空,然后n个头指针又组成一个线性表,采用顺序存储结构,存放在一个一维数组中;


    孩子兄弟表示法


二叉树

一、概念

  1. 完全二叉树:对一棵有n个节点的二叉树按层序编号,如果编号为i(1<=i<=n)的节点与同样深度的满二叉树中编号为i的节点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树;
完全二叉树举例

二、二叉树的链式存储方式

二叉树的链式存储方式

三、二叉树的遍历

一、前序遍历

  1. 步骤

    1. 如果二叉树为空,则空操作返回
    2. 否则先访问跟节点
    3. 然后前序遍历左子树
    4. 再前序遍历右子树
  2. 代码实现

     public void preOrderTraverse(Tree tree){
         if(tree == null){
             return;
         }
         System.out.println(tree.data);
         preOrderTraverse(tree.leftChild);
         preOrderTraverse(tree.rightChild);
     }
    

二、中序遍历

  1. 步骤

    1. 如果二叉树为空,则返回空操作;
    2. 否则前序遍历左子树
    3. 然后访问跟节点
    4. 再中序遍历右子树
  2. 代码实现

     public void midOrderTraverse(Tree tree){
           if(tree==null){
                return;
           }
           midOrderTraverse(tree.leftChild);
           System.out.println(tree.data);
           midOrderTraverse(tree.rightChild);
     }
    

三、后序遍历

  1. 步骤

    1. 如果二叉树为空,则返回空操作;
    2. 否则后序遍历左子树
    3. 然后后序遍历右子树
    4. 再遍历根节点
  2. 代码实现

     public void proOrderTraverse(Tree tree){
         if(tree==null){
             return;
         }
         proOderTraverse(tree.leftChild);
         proOderTraverse(tree.rightChild);
         System.out.println(tree.data);
     }
    

四、二叉树的创建以及遍历

public class BinaryTree<T> {

    private TreeNode root;//根节点,这个节点是一定存在的,而且对二叉树的操作就是从这里开始的

    public TreeNode getRoot() {
        return root;
    }

    /**
     * @param t 根节点的初始值
     */
    public BinaryTree(T t) {
        root = new TreeNode(1, t);
    }

    /**
     * 通过二叉树的前序序列集合创建二叉树
     * @param list 集合必须是二叉树对应的完全二叉树序列,没有节点的地方用<code>#</code>代替
     *             算法思想:每次从list中取出一个元素来构建二叉树,同时从集合中移除这个元素,如果list的长度为0,表示构建完成
     *             如果遇到<code>#</code>,就不输出,然后递归调用这个方法,得到二叉树序列
     * @return
     */
    public TreeNode createBinaryTree(int size, List<T> list) {
        if (list == null) {
            return null;
        } else if (list.size() == 0) {
            return null;
        } else {
            //构建节点的准备工作,数据域和角标index
            T t = list.get(0);
            TreeNode node = null;
            int index = size - list.size();
            if (t.equals("#")) {
                //如果是#,说明这里就没有节点,就不用构建,但是要把这个元素从list中移除
                node = null;
                list.remove(t);
                return node;
            }
            //如果代码走到这里,就表示有数据,需要构造节点
            node = new TreeNode(index, t);
            //此外一定要把二叉树的根节点赋值,否则整棵二叉树就没有和root关联起来,会报npe
            if (index == 0) {
                root = node;
            }
            list.remove(t);
            //以上构造完一个节点,接下来构造该节点的左右子树,
            //因为输入的是前序遍历的二叉树集合,所以要先构建左子树,然后是右子树
            //由递归的特点可知,这里一定是先构造完左子树,然后再构造右子树
            node.leftChild = createBinaryTree(size, list);

            node.rightChild = createBinaryTree(size, list);
            return node;
        }
    }

    /**
     * 这里有两种实现思路:
     * <li>
     * 有个成员变量<code>size</code>,每次添加一个节点size++,这样就得到了节点的个数
     * </li>
     * 思路二:通过遍历二叉树来计算节点个数
     * @return 二叉树中节点的个数
     */
    public int size() {
        return getSize(root);
    }

    /**
     * 获取任意节点的子节点个数
     * note:在二叉树中,递归(迭代)是很重要的思想
     * @param node 指定的节点
     * @return
     */
    public int getSize(TreeNode node) {
        return 1 + getSize(node.leftChild) + getSize(node.rightChild);
    }

    /**
     * 对指定的节点前序遍历
     * @param node
     */
    public void preOrderTraverse(TreeNode node) {
        if (node == null) {
            return;
        } else {
            System.out.print(node.getData() + "  ");
            preOrderTraverse(node.leftChild);
            preOrderTraverse(node.rightChild);
        }
    }

    /**
     * 中序遍历指定二叉树节点
     * @param node
     */
    public void midOrderTraverse(TreeNode node) {
        if (node == null) {
            return;
        } else {
            midOrderTraverse(node.getLeftChild());
            System.out.print(node.getData() + "  ");
            midOrderTraverse(node.getRightChild());
        }
    }

    /**
     * 对指定二叉树结点后序遍历
     * @param node
     */
    public void proOrderTraverse(TreeNode node) {
        if (node == null) {
            return;
        } else {
            proOrderTraverse(node.getLeftChild());
            proOrderTraverse(node.getRightChild());
            System.out.print(node.getData() + "  ");
        }
    }

    private static final class TreeNode<T> {
        private int index;//每个节点的标记,根节点默认是1
        private T data;
        private TreeNode leftChild;
        private TreeNode rightChild;
        private TreeNode parent;//父节点,这样我们就可以找到每个节点的孩子节点和父节点了

        /**
         * 构造方法,因为在创建节点时候并不知道左右孩子以及父节点信息,
         * 所以在构造方法里面无法通过构造方法对这些节点信息赋值,只能在创建对象后进行赋值
         * @param index 节点在整个二叉树中的标记,
         * @param data
         */
        public TreeNode(int index, T data) {
            this.index = index;
            this.data = data;
            this.leftChild = null;
            this.rightChild = null;
            this.parent = null;
        }

        public int getIndex() {
            return index;
        }

        public void setIndex(int index) {
            this.index = index;
        }

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }

        public TreeNode getLeftChild() {
            return leftChild;
        }

        public void setLeftChild(TreeNode leftChild) {
            this.leftChild = leftChild;
        }

        public TreeNode getRightChild() {
            return rightChild;
        }

        public void setRightChild(TreeNode rightChild) {
            this.rightChild = rightChild;
        }

        public TreeNode getParent() {
            return parent;
        }

        public void setParent(TreeNode parent) {
            this.parent = parent;
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 树的概述 树是一种非常常用的数据结构,树与前面介绍的线性表,栈,队列等线性结构不同,树是一种非线性结构 1.树的定...
    Jack921阅读 4,442评论 1 31
  • 树: 1 概念 定义:树是节点的有限集合 度:当前双亲的直接孩子个数 叶子:终端节点(度为0)就是叶子 根:非终端...
    Carrism阅读 270评论 0 1
  • 一直以来,我都很少使用也避免使用到树和图,总觉得它们神秘而又复杂,但是树在一些运算和查找中也不可避免的要使用到,那...
    24K男阅读 6,739评论 5 14
  • 读完影响力第六章,深刻感觉到权威在我们周边生活的影响力,看病要找最好的,最有权威的医生,上学要找最权威的学校,交友...
    I_DO蕾阅读 544评论 0 0
  • LM25:早上好,我是耐心,今天有点困,昨天加班和去面试的同学吃饭,回来十一点多了,过了平时睡觉的时间,一夜没休息...
    心羽暖姐姐阅读 108评论 2 1