数据结构---------树和森林(最通俗易懂的文章)

树和森林的存储结构简单理解

树和森林的结构就不说了,和二叉树的区别就是每个节点可以有多个子树(子节点)。如下就为树的抽象图。
在这里插入图片描述

树的存储结构

从前面的文章,一般存储的方式都有两种,一种顺序,一种链式,顺序就是创建一个结构体的数组,将每个节点放入,但是缺点就是你创建数组都是指定性的创建10个单位或者几个单位,创建的多了,有空余,创建的少了又不够,链式是有一个你就连接一个,没有就算了,资源较节省,但是稍微难理解。

树的表示方法有三个:双亲,孩子,兄弟孩子。第一种是纯数组顺序类型,第二种是数组+链式,第三种是纯链式来表示。

一.双亲表示法

双亲表示法主要是用数组来(一组连续的空间)存储节点。首先知道双亲的含义,就是节点的爹,就是连接的上一个节点。那么它是如何通过数组中的节点来实现树的结构。只需要在节点的结构体里加上一个索引就可以了,指向它的父节点在数组中的位置。就实现了连接。

在这里插入图片描述
在数组中存放节点的位置顺序没有要求,只需要在它的结构体中的索引设置父节点的位置即可,如上图所示。

那么首先定义树节点的结构体:

typedef struct PTNode        //定义单个树节点
{ 
  ElemType data;       //节点中存储的数据
  int parent;          //父节点在数组中的位置
}PTNode
typedef struct
{
  PTNode[MAX_SIZE];     //定义一个结构体数组存放节点
  int r,n;              //根的位置和节点数
}PTree;

下面实现一下该结构的构造

typedef struct PTNode        //定义单个树节点
{ 
  char name[10];       //这里存储学生姓名
  int parent;          //父节点在数组中的位置
}PTNode
typedef struct
{
  PTNode[10];     //定义一个结构体数组存放节点,这里假设存放10个
  int r,n;        //根的位置和节点数
}PTree;

int main()
{
 PTree tree;      //创建 树
 tree.r=0;        //设置树的根节点在数组中的位置
 int n=tree.n=3;  //设置树的节点数,你要创建几个就节点
 
 for(int i=0;i<n;i++)     //在数组中递归,创建节点,n表示你要创建3个节点,一般数组第一个都为根节点
 {
  scanf("%s",&tree.nodes[i].name);    //先输入每个节点的数据
  scanf("%d",&tree.nodes[i].parent);   //再输入每个几点的父节点在数组中的位置,-1表示为根节点。
 }
 } 
}

在这里插入图片描述
例如上面输入了三个节点,第一个为根节点,所以设置它的parent索引为 -1,然后下面两个节点的parent索引为0,指向根节点,所以就构成了简单的树
在这里插入图片描述
如上图所示,自然想到了找根节点的函数方法,假如给了一个节点数组,你不知道根节点在数组中的位置,定义一个找根节点的函数方法

void findbase(PTree &tree,int x)  //传入树,和随便一个节点(在数组中的位置)
{
 
 while(tree.nodes[x].parent!=-1)  //因为设置的根节点的parent索引为-1,所以当其为-1的时候,说明它就是根节点
 {
  x=tree.nodes[x].parent;   //根据树的结构可以理解,一直向上查找父节点就找到了根节点在数组中位置
 }
   printf("已经找到了根节点,其内容为%s",tree.nodes[x].name);  //将根节点的值输出
}

然后调用该函数,随便传入参数为1,还是以上面的例子

findbase(tree,1);   //查找节点为小红的父节点,因为该节点在数组中的位置为1,所以第二个参数为1

在这里插入图片描述
这里只是一个简单的例子,也可以创建多个节点做实验。查找根节点的方法也可以使用递归的方法来查找,可以自己做一下。

孩子表示法

以孩子为命名,所以孩子应该是该方法的主体,上面说了该方法是以数组和链表相解合的,所以和上面结构体定义类似,只是在每个单个节点里创建一个链表代替了在结构体数组中parent的作用。

假如某个节点有多个孩子,那么在该节点里面创建链表将它孩子的位置放进去即可,顶替了parent作用。(链表里只存它孩子在数组中的位置,并不存孩子的数据)。所有节点还是放在结构体数组中。如下图所示 (后面的尖表示空,其为叶子)

在这里插入图片描述

typedef struct TList      //创建每个节点的链式表,哪个节点有孩子就创建链式表
{
   int child;      //孩子在数组中的位置,可以看上图
   struct TList *next;     //下一个节点的地址
} *TList;
typedef struct 
{ 
    ElemType elem;      //每个节点中需要放的类型数据
    TList firstchild;   //如果有孩子,就创建,如果没有,设置其为空
}TNode;
typedef struct
{
    TNode nodes[MAX_SIZE];    //创建树和上面差不多
    int r,n;
}Tree;

孩子表示法就是在双亲表示法上的每个节点创建一个链式表,若有孩子就创建链式表,没有就置空,且链式表中每个节点存放的是孩子在数组中的位置,不存放孩子的数据。例如你想看某个节点有没有孩子:

node.firstchild->child;  
若没有孩子则该child值为空,若有,可以再调用它的next看是否有下一个孩子
孩子兄弟表示法

顾名思义,孩子兄弟表示法,是以孩子和兄弟为中心,也被称为二叉树表示法,就是把给的一个树转化为二叉树的方式理解。首先转化如图所示:
在这里插入图片描述
结构体存储图如下:

在这里插入图片描述

如上图所示,将树转化为二叉树,那么二叉树就只能连接两个节点,两个指针域(左面child,右面sibling),一个child,一个sibling(兄弟),将其节点的第一个孩子放到child里,第二个孩子放到第一个孩子的sibling里,第三个孩子放到第二个孩子的sibling里,那么 C节点的孩子自然放到C节点的child里。那么二叉树中BCD就为兄弟,自然就是A的孩子。

所以二叉树表示法就是将树的孩子放到左面节点,兄弟就放到右节点,那么找A的第二个孩子只需要找它第一个孩子,再调用其的兄弟指针就可以找到第二个孩子的节点,依次可得找第三个,那么继续调用第二个孩子的兄弟节点即可。

结构体定义自然就为:

typedef struct Tree
{
   ElemType elem;
   struct Tree *child,*sibling;
}Node,*Tree;

创建二叉树函数:(这里假设每个节点的数据为char数据)

void CreateTree(Tree &T)
 {
  char t;
  scanf("%c",&t);
  getchar();           //消除回车键的影响,否则会直接进行两次scanf,导致第二次scanf失效
  if(t==' ') T=NULL;   //当输入为空格的时候,表示子树为空
  else
  {
   if(!(T=(Node*)malloc(sizeof(Node)))) exit; 
   T->elem=t;        //将数据域赋值
   CreateTree(T->child);       // 创建孩子节点
   CreateTree(T->siblig);      // 创建兄弟节点
  } 
 }

那么该先序遍历算法为:

void PreOrder(Tree &T){	
if(T){		
    printf("%s",T->elem);		
    PreOrder(T->child);		
    PreOrder(T->sibling);
  }
}

二叉树先序遍历和树的线序遍历结果相同

树的中序和后续遍历可见下面文章:
树的遍历

最近发现了一个国外挺牛的网站,将所有数据结构都实现了可视化。你可以调节演示速度,有各种算法演示,比如什么查找之类,红黑树,什么都有。可能就是全英文,看不懂的可以用浏览器的翻译软件。地址如下:

数据结构可视化

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值