带你了解二叉树

Posted 熬夜磕代码丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带你了解二叉树相关的知识,希望对你有一定的参考价值。

文章目录

一、树是什么?

我们已经掌握了数组和链表,为什么还要有树?先来看看数组和链表的优缺点

数组:因为有索引,所以可以快速地访问到某个元素。但是如果要进行插入或者删除的话,被插入/删除位置之后的元素都得移动,如果插入后超过了数组容量,还得进行数组扩容。可见,数组查询快,增删慢。
链表:没有索引,要查询某个元素,得从第一个元素开始,一个一个往后遍历。但是要进行插入或者删除,无需移动元素,只要找到插入/删除位置的前一个元素即可。所以链表查询慢,增删快。

说到这里,那肯定知道树存在的意义了,没错,它吸收了链表和数组的优点,查询快,增删也快。

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
有一个特殊的结点,称为根结点,根结点没有前驱结点
除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、…、Tm,其中每一个集合 Ti (1 <= i
<= m) 又是一棵与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
树是递归定义的。

二、树的概念

重要概念

结点的度:一个结点含有子树的个数称为该结点的度;
树的度:一棵树中,所有结点度的最大值称为树的度;
叶子结点或终端结点:度为0的结点称为叶结点;
双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点;
孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点;
根结点:一棵树中,没有双亲结点的结点;
结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推
树的高度或深度:树中结点的最大层次;

了解概念

树的以下概念只需了解:
非终端结点或分支结点:度不为0的结点;
兄弟结点:具有相同父结点的结点互称为兄弟结点;
堂兄弟结点:双亲在同一层的结点互为堂兄弟;
结点的祖先:从根到该结点所经分支上的所有结点;
子孙:以某结点为根的子树中任一结点都称为该结点的子孙
森林:由m(m>=0)棵互不相交的树组成的集合称为森林

三、二叉树

一棵二叉树是结点的一个有限集合,该集合:

  1. 或者为空
  2. 或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
    T1为左子树,T2为右子树
  3. 二叉树不存在度大于2的结点
  4. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树(这里的有序指的是左右子树的有序的,不是大小有序)

特殊的二叉树

  1. 满二叉树: 一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。也就是说,如果一棵二叉树的层数为K,且结点总数是2^k - 1 ,则它就是满二叉树。
  2. 完全二叉树: 完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从0至n-1的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

    二叉树的性质:
    1.若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有 (i>0)个结点
    2 若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 (k>=0)
    3 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1
    4 具有n个结点的完全二叉树的深度k为log(n+1)上取整
    5.对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:
    若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
    若2i+1<n,左孩子序号:2i+1,否则无左孩子
    若2i+2<n,右孩子序号:2i+2,否则无右孩子

四、二叉树的存储

二叉树的表示方法

二叉树的表示方法有很多,我们这里以孩子表示法为例

//孩子表示法
    static class TreeNode 
        public int val;
        public TreeNode left;//左孩子
        public TreeNode right;//右孩子

        public TreeNode(int val) 
            this.val = val;
        
    

手动创建二叉树


我们用最简单的方法创建这样一棵树。

public void InitTree() 
        TreeNode node1 = new TreeNode(1);
        TreeNode node2 = new TreeNode(2);
        TreeNode node3 = new TreeNode(3);
        TreeNode node4 = new TreeNode(4);
        TreeNode node5 = new TreeNode(5);
        TreeNode node6 = new TreeNode(6);
        TreeNode node7 = new TreeNode(7);
        TreeNode node8 = new TreeNode(8);
        node1.left = node2;
        node1.right = node3;
        node2.left = node4;
        node2.right = node5;
        node3.left = node6;
        node3.right = node7;
        node5.right = node8;
    

五、二叉树的遍历

所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。

前序遍历

NLR:前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点—>根的左子树—>根的右子树。

public void perOrder(TreeNode root) 
        //先序遍历
        if(root == null) 
            return;
        
        System.out.print(root.val+" ");
        perOrder(root.left);
        perOrder(root.right);
    

中序遍历

LNR:中序遍历(Inorder Traversal)——根的左子树—>根节点—>根的右子树。

public void inOrder(TreeNode root) 
        //中序遍历
        if(root == null) 
            return;
        
        inOrder(root.left);
        System.out.print(root.val+" ");
        inOrder(root.right);
    

后序遍历

LRN:后序遍历(Postorder Traversal)——根的左子树—>根的右子树—>根节点。

public void postOrder(TreeNode root) 
        //后序遍历
        if(root == null) 
            return;
        
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val+" ");
    

层序遍历

层序遍历就是一行一行遍历

带你全面的了解二叉树

​摘要:日常生活中,很多事物都可以用树来描述,例如书的目录、工作单位的组织架构等等。树是计算机中非常重要的一种数据结构,树存储方式可以提高数据的存储、读取效率。

本文分享自华为云社区​​《【云驻共创】想了解二叉树,看这篇文章就够了》​​,作者: liuzhen007 。

前言

日常生活中,很多事物都可以用树来描述,例如书的目录、工作单位的组织架构等等。树是计算机中非常重要的一种数据结构,树存储方式可以提高数据的存储、读取效率。

一、树的基本定义

日常生活中,很多事物都可以用树来描述,树是计算机中非常重要的一种数据结构,树存储方式可以提高数据的存储、读取效率,比如利用二叉排序树,既可以保证数据的检索速度,同时,也可以保证数据的插入、删除、修改的速度。


其实,树的种类有很多种,比如普通的二叉树、完全二叉树、满二叉树、线索二叉树、哈夫曼树、二叉排序树、平衡二叉树、AVL平衡二叉树、红黑树、B树、B+树、堆等。今天介绍的主要内容是二叉树的基本知识和几种基础类型的二叉树。

二、二叉树的相关术语

在正式开讲前,首先介绍一些关于二叉树的专业名词和术语,包括结点、结点的度、叶子结点、分支结点、结点的层次、树的度、树的深度等,了解这些基础的专业名词和术语对于我们理解二叉树的特性有非常重要的帮助作用。


1)结点:包含一个数据元素及若干指向子树分支的信息。

2)结点的度:一个结点拥有子树的数据成为结点的度。

3)叶子结点:也称为终端结点,没有子树的结点或者度为零的结点。

4)分支结点:也称为非终端结点,度不为零的结点成为非终端结点。

5)结点的层次:从根结点开始,根结点的层次为1,根的直接后继层次为2,以此类推。

6)树的度:树中所有结点的度的最大值。

7)树的深度:树中结点的最大层次。

1. 树的特点

树作为一种特殊的数据结构,有非常多的特性,比如:

1)每个结点有多个或者零个子结点

2)没有父结点的结点成为根结点,没有子结点的结点成为叶结点

3)每一个非根结点只有一个父结点

4)每个结点及其后代结点整体上可以看做是一棵树,称为当前结点为根的子树


2. 二叉树的基本定义

1)二叉树就是度不超过2的树,其每个结点最多有两个子结点

2)二叉树的结点分为左结点和右结点


3. 满二叉树

1)二叉树的每一层的结点度都达到最大值,则这个二叉树就是满二叉树

2)一棵深度为n的满二叉树,有2^n-1个结点


4. 完全二叉树

叶子结点只能出现在最下层和次下层,最后一层的叶子结点在左边连续,倒数第二层的叶子结点在右边连续,我们称为完全二叉树。


三、二叉树的创建

接下来,我们通过代码来描述二叉树,语言以Java为例,其中结点类定义如下:



二叉查找树类定义如下:



相关类定义好后,我们来看具体的方法实现,下面分别介绍。

1. size()方法

size()方法相对简单,每次添加元素加一,删除元素减一,维护一个公共的变量 num 即可,代码实现如下:


2. put(Key key,Value value)方法

put(Key key,Value value)方法可以利用重载方法 put(Node x,Key key,Value value),因此实现也相对简单,其中第一个参数只需要传根结点即可,代码实现如下:


3. put(Node x,Key key,Value value)方法

put(Node x,Key key,Value value)方法应该是整个类中实现相对较为复杂的,下面进行简单的分析。


第一种情况,当前树中没有任何一个结点,直接将新插入的结点作为根结点。


第二种情况,当前树不为空,则从根结点开始。这种情况有可以细分为三种情况:


1)如果新结点的key小于当前结点的key,则继续查找当前结点的左子结点。



2)如果新结点的key大于当前结点的key,则继续查找当前结点的右子结点。



3)如果新结点的key等于当前结点的key,则树中已经存在这样的结点,替换该结点的value值即可。



具体的代码实现如下:


4. get(Key key)方法

get(Key key)方法和 put(Key key,Value value)方法类似,也可以利用重载方法 get(Node x,Key key)来实现,代码实现如下:


5. get(Node x,Key key)方法

get(Node x,Key key)方法实现查询方法从根结点开始,如果要查询的key小于当前结点的key,则继续找当前结点的左子结点;如果要查找的key大于当前结点的key,则继续找当前结点的右子结点;如果要查找的key等于当前结点的key,则返回当前结点的value。


具体的代码实现如下:



通过上面的代码,我们可以看出 get()方法和 put()方法类似,都是通过递归调用实现的。

6. delete(Key key)方法

delete(Key key)方法也是一样的思路,调用后面的重载方法就行了,代码实现如下:


7. delete(Node x,Key key)方法

删除方法的实现思路,以最复杂的情况为例,首先找到被删除的结点,找到被删除结点右子树中的最小结点 minNode,删除右子树中的最小结点,让被删除结点的左子树成为最小结点 minNode 的左子树,让被删除结点右子树成为最小结点minNode的右子树,让被删除结点的父结点指向最小结点 minNode。


具体的代码实现如下:



既然代码已经写完了,接下来通过代码来验证一下,看看我们写得是否正确:



答案输出:

3

李四

2

Nice,太好了,通过上述输出结果已经证明了程序是正确的。

四、查找二叉树中最小和最大的键

比如二叉树中用来记录某个公司员工薪资和员工姓名数据,或者某班级学生们的排名和姓名数据。如何快速找出排名最高和最低的同学数据?


这样的话,该怎么做呢?其实,一般还可以整理出如下四个方法,定义如下:


1. min()方法

min()方法和上面提到的 get()和 put()方法类似,也可以使用下面的重载方法实现,具体代码如下:


// 找出树中最小的键

public key min() {

return min(root).key;

}

2. min(Node x)方法

min(Node x)方法需要根据传入的结点位置,查找左子树中的最小的结点,如果为空,则直接返回空,具体代码实现如下:


// 找出树中最小键所在的结点

public Node min(Node x) {

if (x == null) {

return x;

}

Node minNode = x;

while (minNode.left != null) {

minNode = minNode.left;

}

return minNode;

}

3. max()方法

max()方法和上面提到的 min()方法类似,也可以使用下面的重载方法实现,具体代码如下:


// 找出树中最小的键

public key max() {

return max(root).key;

}

4. max(Node x)方法

max(Node x)方法需要根据传入的结点位置,查找右子树中的最大的结点,如果为空,则直接返回空,具体代码实现如下:


// 找出树中最大键所在的结点

public Node min(Node x) {

if (x == null) {

return x;

}

Node maxNode = x;

while (maxNode.right != null) {

maxNode = maxNode.right;

}

return maxNode;

}

五、二叉树的遍历

二叉树的遍历有三种方式,分别是前序遍历、中序遍历、后序遍历。

1. 前序遍历

先访问根结点,再访问左子树,最后访问右子树,比如下图中的二叉树,前序遍历结果如下:

EBADCGFH。

2. 中序遍历

先访问左子树,再访问根结点,最后访问右子树,比如下图中的二叉树,中序遍历结果如下:

ABCDEFGH。

3. 后序遍历

先访问左子树,再访问右子树,最后访问根结点,比如下图中的二叉树,后序遍历结果如下:

ACDBFHGE。


结论

二叉树的不仅在基础的数据结构方面有非常重要的研究意义,在实际应用中也有非常重要的应用场景,兼顾了常规数据结构数组和链表的优点,同时又避免了二者天生的不足。许多实际的问题抽象出来的数据结构往往是二叉树的形式,从而利用二叉树的存储结构和算法特性,因此学习二叉树就非常的必要。希望通过今天本文的介绍能够帮助大家深入理解和掌握二叉树。


​点击关注,第一时间了解华为云新鲜技术~​

以上是关于带你了解二叉树的主要内容,如果未能解决你的问题,请参考以下文章

风哥带你手撕算法之吃透二叉树(初版)

树(二叉树)的建立和遍历算法(前序,中序,后序)

数据结构 二叉树 用二叉链链表存储结构 写出删除二叉树所有的叶子节点的算法

以二叉链表为存储结构,写出求二叉树高度和宽度的算法

1、创建一棵二叉树,以二叉链表作存储结构,实现先根遍历算法 2、创建一棵二叉树,实现先根遍历算法、中根

算法Java解答有序链表转换二叉搜索树,从中序与后序遍历序列构造二叉树