栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

一看就懂的树和二叉树

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

一看就懂的树和二叉树

终于学到树了,也是不容易,打算 把数据结构和算法学一遍然后就开始刷leetcode了,加油!

树的定义

什么是树,在先介绍树之前,我们先看几张图

我从图中可以发现,树这种数据结构,如果把他倒过来,很像生活中的树,最顶上的元素好像就是树根,下面的是一个一个的分枝。所以我们把树上每个元素称为节点,最上面的元素称为根节点。节点与节点之间有一定的父子关系,上下节点为父子节点,左右节点称为兄弟节点。

下面再来看一张图

 

在图中,节点A是节点B的父节点,节点B是节点A的子结点。因为B,C,D这三个节点的父节点是同一个节点,所以他们称为兄弟节点。我们把没有父节点的节点称为根节点,如图中的E节点,我们把没有子结点的节点称为叶子节点,如图中的G,H,I,J。

除此之外,我们还需要了解几个概念:高度,深度,层

#节点的高度 = 节点到叶子节点最长路径长度(边数)

#节点的深度 = 根节点到这个节点的路径长度(边数)

#节点的层 = 节点的深度 + 1

#树的高度 = 根节点的高度

下面再来看几张图

 

 二叉树的定义

树的结构多种多样,不过,最常用的还是二叉树。

对于二叉树,每个节点最多有两个叶子节点,分别是左子节点和右子节点,但是二叉树也不要求每个节点必须有两个节点,可以有左子节点,也可以有右子节点。下面来看一个图。

在上图中,编号为2和3的树比较特殊。在编号为2的树中,他的叶子节点全部在最底层,然后除了叶子节点外,每个节点都有两个叶子节点 ,这种二叉树称为满二叉树。编号为3的树中,他的叶子节点分布在最底层和倒数第二层,而且最下面一次的叶子节点都靠左排列,我们把这种二叉树称为完全二叉树。这个树可能不太好分清,而且可能还会有疑问为什么最下面一层的叶子节点必须都靠左排列,下面我会进行讲解,现在先来看图。

 二叉树的存储

要完全理解二叉树,我们还需要知道二叉树是如何存储的,存储一颗二叉树一般有两种方法,一个是基于指针(引用)的链式存储方式,另一种是基于数组的顺序存储方式。‘

先来看一张图

从图中可以看出,每个节点包含三个字段,数据本身和指向左右节点的指针,我们可以根据指针,将整个树给串联起来.

下面看看二叉树链式存储的代码实现

public class Node{
   public int data;
   public Node left;
   public Node right;
}

 下面看看基于数组的顺序存储方式。

我们用数组来存储所有的节点。对于节点之间的父子关系,通过计算数组下标获得。如果节点x存储在数组中下标为i的位置,那么下标为2i存储的就是他的左子节点,下标2i+1存储的就是右子节点,下标i/2存储的是父节点。

通过这种方式,我们只要知道根节点的下标在什么位置,就可以知道其他子结点的下标了,还有我们为了方便计算,通常把根节点存放在下标为1的地方。下面通过一张图进行举例说明

对于节点B,他存放的位置i = 2,所以他的左子节点(D)2i = 4.右子节点(E)2i+1 = 5。他的父节点(A)i/2 = 1。

大家仔细看这张图应该就会发现,他是一棵完全二叉树存储的。在数组中是连续的。如果换成非完全二叉树又是什么结果呢?看一个图。

 从图中可以发现,采用数组来存储非完全二叉树,我们会浪费较多的空间。所以,对于非完全二叉树,我们一般会采用链式存储的方式来进行存储。对于完全二叉树,采用线性存储,基于数组的形式存储完全二叉树会更大的节省内存,也不需要记录左右子节点的指针。这就是完全二叉树要求最下面一层的叶子节点都靠左排列的原因。

二叉树的遍历

上面讲了关于二叉树的定义和存储,相信大家对二叉树都有了一个概念和基本的了解。这次我们来聊一聊二叉树的遍历。

将二叉树中节点进行输出,经典的方法有三种,前序遍历,中序遍历,和后序遍历。

前序遍历(根左右):对于树中的任意节点,先输出自己,然后输出左子树,然后右子树

中序遍历(左根右):对于树中任意节点,先输出左子树,然后输出自己,然后右子树

后序遍历(左右根):对于树中任意节点,先输出左子树,再输出右子树,最后输出自己

实际上对树的很多操作是非常适合用递归来进行实现的。二叉树的前序,中序,后序就是典型的递归过程。例如前序遍历就是先输出根节点,然后左子树,然后右子树。

递归的写法主要是写出递归公式,关于写出递归公式关键是将子问题进行分解。比如说,要解决A,就要假设B和C都已经解决了。下面看看递推公式

前序遍历递推公式:

preOrder(root) = print root -> preOrder(root.left) -> preOrder(root.right)

中序遍历

inOrder(root) = inOrder(root.left) -> print root -> inOrder(root.right)

后续遍历

postOrder(root) = postOrder(root.left) -> postOrder(root.right) -> print root

下面看看代码

public void preOrder(Node root){
   if(root == null) return;
   System.out.println(root.data);
   preOrder(root.left);
   preOrder(root.right);
}

public void inOrder(Node root){
   if(root == null) return;
   inOrder(root.left); 
   System.out.println(root.data);
   inOrder(root.right);
} 

public void postOrder(Node root){
   if(root == null) return;
   postOrder(root.left);
   postOrder(root.right);
   System.out.println(root.data);
}

三种遍历操作的时间复杂度与节点的个数n成正比,时间复杂度为O(n)。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/287267.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号