前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >数据结构与算法(八)-二叉树(斜二叉树、满二叉树、完全二叉树、线索二叉树)

数据结构与算法(八)-二叉树(斜二叉树、满二叉树、完全二叉树、线索二叉树)

作者头像
发布于 2018-10-11 03:20:34
发布于 2018-10-11 03:20:34
10.7K00
代码可运行
举报
文章被收录于专栏:F_AlexF_Alex
运行总次数:0
代码可运行

前言:前面了解了树的概念和基本的存储结构类型及树的分类,而在树中应用最广泛的种类是二叉树

一、简介

  在树型结构中,如果每个父节点只有两个子节点,那么这样的树被称为二叉树(Binary tree)。其中,一个父结点的两个字节点分别叫做“左子节点”和“右子节点”。不过也不是所有父节点都有两个子节点,只有左子节点或者只有右子节点的情况也存在。另外,也会存在叶子结点,也就是一个子节点都没有的节点,唯一的限制就是每一个节点的子节点不能超过两个。

  之前谈过的单向链表,是一种通过“指向下一个元素的指针”来连接各个数据的数据结构,而二叉树则为每一个元素都有两个指向下一个元素的指针。

  所以二叉树可以使用链表的形式来存储,当然也可以使用数组来表示,可以定义某一个父结点的元素索引是i,并且其左子节点的元素索引为(i×2+1)、右子节点的元素索引为(i×2+2)。例如:将根节点存在了0位置,那么它的左子节点位置为1,右子节点的位置为2,而1位置元素的左子节点为3,右子节点为4,以此类推,可以完整的把二叉树表示出来;

  二叉树是n(n>=0)个节点的有限集合,该集合可以为空(空二叉树),或由一个根节点和两颗互不相交的、分别称为根节点的左子树和右子树的二叉树组成;

  这样看来,二叉树可以使用递归来创建。

  特点:

  • 每个节点最多有两个子节点;
  • 二叉树中最大的度为2;
  • 无论有几个分支,都需要区分是左子树还是右子树;

二、分类及实现

2.1 分类

斜二叉树:只有左子节点或只有右子节点的二叉树称为斜二叉树;

  斜二叉树特点:

  • 度为1;
  • 只有左子节点或右子节点;

满二叉树:所有的分支要么左右子节点都有,要么没有子节点,且所有叶子结点都在同一层上;

  满二叉树特点:

  • 叶子结点只能;
  • 非叶子节点的结点的度为2;

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

  完全二叉树特点:

  • 叶子结点只能出现在最下两层;
  • 最下层的叶子结点一定集中在左边并且连续;
  • 若结点度为1,则该节点只有左子节点;

注:满二叉树一定是完全二叉树,而完全二叉树不一定是满二叉树;

线索二叉树:在空节点中加入了线索(即在某种遍历顺序下指向的下一个节点)指引的二叉树称为线索二叉树;

  线索二叉树特点:

  • 节省空间,按一定的遍历规则,将空结点的leftNode指向前驱,rightNode指向后继结点;

  这里先介绍这几种特殊的二叉树,对于平衡二叉树、二叉排序树、红黑树、哈夫曼树想要单独开一篇随笔。

2.2 普通二叉树

2.2.1 二叉树的遍历分类

  二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中所有节点,使得每个 节点被访问依次且仅被访问一次。

  二叉树的遍历次序不同于线性结构,线性结构最多也就是分为顺序、循环、双向等简单的遍历方式。而树不存在唯一的后继节点,在访问一个节点后,下一个被访问的节点面临着不同的选择,所以我们需要规范遍历方式:

  为了学习遍历方式,我们先创建一个二叉树 ,如图:

  ①、前序遍历:

  定义:先访问根节点,然后访问左子树,再访问右子树

  按照定义遍历的顺序遍历结果为:A B D H I E J C F K G

  访问顺序如下图:

  ②、中序遍历:

  定义:先访问左子树,再访问根节点,最后访问右子树

  按照定义遍历的顺序遍历结果为:H D I B E J A F K C G

  访问顺序如下图:

  ③、后序遍历:

  定义:先访问左子树,再访问右子树,最后访问根节点

  按照定义遍历的顺序遍历结果为:H I D J E B K F G C A

  访问顺序如下图:

  ④、层次遍历:

  定义:逐层的从根节点开始,每层从左至右遍历;

  按照定义遍历的顺序遍历结果为:A B C D E F G H I J K

  访问顺序如下图:

2.2.2 递归

  程序直接或间接的调用自身的编程技巧称为递归。他通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题方程所需要的多次重复计算,大大地减少了程序的代码量。

  递归的能力在于用有线的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

  例如很有名的斐波那契数列(又称兔子数列,感兴趣的可以了解一下)就可以用递归来解决:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static Integer getNums(Integer index) {
    //边界条件 
    if (index==1) { 
        return 1; 
    } else if (index==2) { 
        return 1; 
    } else {
        //递归前进 
        return getNums(index - 1) + getNums(index - 2); 
    } 
}                

2.2.3 二叉树实现

  二叉树API

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class BinaryTree   
    BinaryTree()     创建一个空背包   
    print()          遍历树
    size()       获取二叉树元素大小   
    isEmpty()        是否为空树        

  二叉树的实现需要借助遍历的方式和迭代算法,我们选择前序遍历的方式,并以空格为空叶子节点将字符串转换为二叉树例如:ABDH空格空格I空格空格E空格J空格空格CF空格K空格空格G空格空格(空格代表的是“ ”)。

  结点Node内部类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Node {

        String str;

        Node leftNode;

        Node rightNode;

}

  创建二叉树代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class BinaryTree {

    private char[] strs;

    private int index;

    private Node root;

    BinaryTree(String str) {
        strs = str.toCharArray();
        root = new Node();
        createBinaryTree(root);
        System.out.println("11");
    }

    private void createBinaryTree(Node node) {
        int currentIndex = index;
        if (currentIndex<strs.length) {
            char str = strs[currentIndex];
            index++;
            if (String.valueOf(str).isEmpty()||str==' ') {
                node.str = null;
            } else {
                node.leftNode = new Node();
                node.rightNode = new Node();
                node.str = String.valueOf(strs[currentIndex]);
                createBinaryTree(node.leftNode);
                createBinaryTree(node.rightNode);
            }
        }
    }
}

  前序遍历代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public void print() {
        int level = 1;
        iterator(root,level,"根节点");
    }

    private void iterator(Node node, int level, String str) {
        if (node.str==null||node.str.isEmpty()) {

        } else {
            System.out.println(node.str + "在" + level + "层的"+str);
            iterator(node.leftNode,level+1,"左子节点");
            iterator(node.rightNode,level+1,"右子节点");
        }

    }

  判空和长度方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public Boolean isEmpty() {
        return strs.length < 1;
    }
    
    public int size() {
        return strs.length;
    }

  书写测试方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree("ABDH  I  E J  CF K  G  ");
        tree.print();
    }

测试结果如图:

2.3 线索二叉树

2.3.1 介绍

  在上面普通二叉树的代码实现里,有没有发现叶子节点的leftNode和rightNode为空,而且叶子结点不在少数,那么怎么避免空叶子节点的空间浪费呢?

  而线索二叉树是在上面null的位置放入遍历时的前驱结点和后继结点,如下图:

2.3.2 线索二叉树的实现

  线索二叉树API:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class BinaryTree
  BinaryTree()            创建一个空背包
  InOrderTraverse()       中序遍历二叉树
  boolean isEmpty()       是否为空树

  首先先要改变Node结点的属性结构,增加ltag和rtag来标记是否为线索。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ThreadBinaryTread {

    private char[] strs;

    private int index;

    private Node root;

    private Node pre;

    ThreadBinaryTread(String str) {
        strs = str.toCharArray();
        root = new Node();
        createBinaryTree(root);
        //创建完二叉树后,进行二叉树线索化
        Node p = new Node();
        p.ltag = 0;
        p.rtag = 1;
        pre = p;
        inThreading(root);
        pre.rightNode = p;
        pre = p;
    }

    //中序遍历线索化
    private void inThreading(Node node) {
        if (node.str!=null&&!node.str.isEmpty()) {
            inThreading(node.leftNode);
            //如果该节点没有左子节点,则将前一个遍历的结点放入该左子节点的位置并将标志改为线索
            if (node.leftNode.str == null || node.leftNode.str.isEmpty()) {
                node.ltag = 1;
                node.leftNode = pre;
            }

            //如果前一个遍历结点没有右子节点,则将本节点放到前一个节点的右子节点的位置上并将标志改为线索
            if (pre.rightNode==null||pre.rightNode.str==null) {
                pre.rtag = 1;
                pre.rightNode = node;
            }

            pre = node;

            inThreading(node.rightNode);
        }
    }

    private void createBinaryTree(Node node) {
        int currentIndex = index;
        if (currentIndex<strs.length) {
            char str = strs[currentIndex];
            index++;
            if (String.valueOf(str).isEmpty()||str==' ') {
                node.str = null;
            } else {
                node.rightNode = new Node();
                node.leftNode = new Node();
                node.str = String.valueOf(strs[currentIndex]);
                createBinaryTree(node.leftNode);
                createBinaryTree(node.rightNode);
            }
        }
    }

    public void InOrderTraverse() {
        Node node = pre.rightNode;
        while (node!=pre) {
            while (node.ltag==0) {
                node = node.leftNode;
            }
            System.out.println(node.str);
            while (node.rtag==1&&node.rightNode!=pre) {
                node = node.rightNode;
                System.out.println(node.str);
            }
            node = node.rightNode;
        }
    }

    public Boolean isEmpty() {
        return strs.length < 1;
    }

    class Node {

        String str;

        Node leftNode;

        Node rightNode;

        //是否为线索,1:前驱线索;0:左子节点;
        int ltag = 0;

        //是否为线索,1:后继线索;0:右子节点;
        int rtag = 0;

    }

}

  创建测试方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public static void main(String[] args) {
        ThreadBinaryTread tree = new ThreadBinaryTread("ABDH  I  E J  CF K  G  ");
        tree.InOrderTraverse();
    }

运行结果如图:

三、总结

  二叉树的特点:

  • 每个节点下最多有两个子节点;
  • 二叉树的度和节点的度最大为2;

  二叉树遍历的方式:

  • 前序遍历:根节点->左子节点->右子节点;
  • 中序遍历:左子节点->根节点->右子节点;
  • 后序遍历:左子节点->右子节点->根节点;
  • 层次遍历:逐层从左向右遍历;

  线索二叉树需要线索话才能使用链表的方式遍历;

本系列参考书籍:

  《写给大家看的算法书》

  《图灵程序设计丛书 算法 第4版》

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-09-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
线索二叉树怎么画-先序线索二叉树和中序线索二叉树有什么区别 最好图解
  if(!(*Thrt=()malloc(sizeof()))) exit(1);
宜轩
2022/12/26
4940
为实习准备的数据结构(7)--线索二叉树
在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。
看、未来
2021/09/18
3820
数据结构 线索二叉树
    通过考察各种二叉链表,不管儿叉树的形态如何,空链域的个数总是多过非空链域的个数。准确的说,n各结点的二叉链表共有2n个链域,非空链域为n-1个,但其中的空链域却有n+1个。如下图所示。
Twcat_tree
2022/11/29
3310
数据结构 线索二叉树
二叉树详解
什么是树,什么又是二叉树?我知道大家都听过,但对于具体的概念,应该还是比较模糊的吧?
半月无霜
2023/03/03
2K0
二叉树详解
图解线索二叉树与双向线索二叉树(附源码)
  当我们对普通的二叉树进行遍历时需要使用栈结构做重复性的操作。线索二叉树不需要如此,在遍历的同时,使用二叉树中空闲的内存空间记录某些结点的前趋和后继元素的位置(不是全部)。这样在算法后期需要遍历二叉树时,就可以利用保存的结点信息,提高了遍历的效率。使用这种方法构建的二叉树,即为“线索二叉树”。
嵌入式与Linux那些事
2021/05/20
1.3K0
图解线索二叉树与双向线索二叉树(附源码)
数据结构:线索二叉树(Threaded Binary Tree)
该文介绍了二叉树及其各种类型,重点讲解了二叉搜索树、平衡二叉树、堆、线索二叉树的概念、特点、存储结构和算法,并通过示例展示了这些概念在编程中的应用。
s1mba
2018/01/04
2.1K0
数据结构:线索二叉树(Threaded Binary Tree)
[数据结构与算法] 树结构之二叉树
在学习树结构之前, 我们首先来复习一下线性存储结构的两种方式: 线性存储(包括数组)和链式存储
时间静止不是简史
2020/07/25
4830
[数据结构与算法] 树结构之二叉树
我的思念像满二叉树般疯长,每个空指针都指向你的方向——全程动画可视化数据结构算法之二叉树
盛透侧视攻城狮
2025/03/02
830
我的思念像满二叉树般疯长,每个空指针都指向你的方向——全程动画可视化数据结构算法之二叉树
PHP数据结构-完全二叉树、线索二叉树及树的顺序存储结构
在上篇文章中,我们学习了二叉树的基本链式结构以及建树和遍历相关的操作。今天我们学习的则是一些二叉树相关的概念以及二叉树的一种变形形式。
硬核项目经理
2021/05/11
4720
PHP数据结构-完全二叉树、线索二叉树及树的顺序存储结构
线索二叉树 —C语言王道
从根结点出发,重新进行一次中序遍历,指针q记录当前访问的结点,指针pre记录上一个被访的结点 ①当 q == p 时,pre为前驱
莫浅子
2022/11/18
7770
线索二叉树 —C语言王道
线索二叉树
前驱节点:对一棵二叉树进行中序遍历,遍历后的顺序,当前节点的前一个节点为该节点的前驱节点; 后继节点:对一棵二叉树进行中序遍历,遍历后的顺序,当前节点的后一个节点为该节点的后继节点; 线索中序二叉树实现的代码 下面这种方法错误,因为同级指针修饰是值传递 中序线索二叉树的建立 void Tree::inThread(node* p) { static node* pre = NULL; //传入的前驱指针p一开始指向根节点A if (p == NULL) return; //先通过不断对左子树的递
大忽悠爱学习
2021/03/17
4570
线索二叉树
【Python数据结构系列】☀️《树与二叉树-基础知识》——知识点讲解+代码实现☀️
树(Tree)是n(n≥0)个结点的有限集,它或为空树(n=0);或为非空树,对于非空树T:
天道Vax的时间宝藏
2021/08/24
1.1K0
【数据结构】C语言实现线索二叉树
不过我相信肯定还是有朋友不太理解为什么要有线索二叉树,如果只是需要获取一个二叉树的遍历序列,我们直接对二叉树进行遍历不就好了吗?何必多此一举呢?
蒙奇D索隆
2025/03/16
410
【数据结构】C语言实现线索二叉树
当Kotlin遇见数据结构丨实现中序线索化二叉树并遍历
n个节点的二叉树含有n+1个空指针域。利用这些空指针域,存放指向节点的在某种遍历次序下的前驱节点及后继节点的指针,这种附加的指针称为"线索",加上了线索的二叉树就是"线索二叉树"。
码脑
2019/04/11
4780
当Kotlin遇见数据结构丨实现中序线索化二叉树并遍历
线索二叉树
在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。 对于n个结点的二叉树,在二叉链存储结构中有n+1个空链域,利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树。 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。 注意:线索链表解决了无法直接找到该结点在某种遍历序列中的前驱和后继结点的问题,解决了二叉链表找左、右孩子困难的问题。
跋扈洋
2022/12/03
5110
4.3.2 线索二叉树
二叉树结点的各种遍历序列,其实质是对一个非线性结构进行线性化操作,使在这个访问序列中每一个结点(除第一个和最后一个)都有一个直接前驱和直接后继。
week
2018/08/24
5770
边看边思考十五分钟拿下线索存储二叉树
将数列 {1, 3, 6, 8, 10, 14 } 构建成一颗二叉树. n+1=7
冷环渊
2022/03/01
3050
边看边思考十五分钟拿下线索存储二叉树
数据结构:图文详解二叉树(遍历、类型、操作)
从根节点出发,按照某种次序访问二叉树中的所有结点,使得每个结点被访问1次 且 只被访问1次
Carson.Ho
2020/01/13
7020
数据结构:图文详解二叉树(遍历、类型、操作)
重学数据结构(六、树和二叉树)
树(Tree)是n(n>=0)个结点的有限集,它或为空树(n= 0);,或为非空树,对千非空树T:
三分恶
2020/10/30
8190
重学数据结构(六、树和二叉树)
数据结构——树和二叉树
满二叉树是叶子一个也不少的树,而完全二叉树虽然前n-1层是满的,但最底层却允许在右边缺少连续若干个结点。满二叉树是完全二叉树的一个特例。
ruochen
2021/06/29
7230
推荐阅读
相关推荐
线索二叉树怎么画-先序线索二叉树和中序线索二叉树有什么区别 最好图解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验