数据结构 —— 第六章 树形结构

目录

first树的基本概念

树的说明

树的几个基本用语

结点的度

数的度

叶子(终端结点)

儿子

父亲

子孙

祖先

兄弟

堂兄弟

结点的层次

深度

有序树

森林

路径

树的其他表示方法

树的存储结构

双亲表示法

孩子表示法

孩子兄弟表示法

树的遍历

定义

树的前序遍历的递归实现

树的后序遍历的递归实现

按前序遍历顺序建立一棵度数为3的树的递归算法

树的层次遍历算法

代码实现

树的线性表示

树的括号表示

树的括号表示具有以下特点:

树的括号表示和树的孩子表示转换算法

树的层号表示法和树的扩充孩子表示转换算法


first树的基本概念

树是由n (n>=0) 个结点构成的有限集合,n=0的树被称为空树;当n!=0时树中的结点应满足以下两个条件

  1. 有且仅有一个特定的结点称之为根
  2. 其余结点分成m(m>=0)个互不相交的有限集合T1、T2......Tm,其中每一集合又都是一个树,T1、T2......Tm为根节点的子树

树的说明

说明:

  1. 数据结构中允许空树的存在
  2. 当只有一个结点时,树的结点为根
  3. 当n>1时,其余结点可分为互不相交的子树

树的几个基本用语

结点的度

树中每个结点具有的子树数或者后继结点数称为结点的度

数的度

树中所有结点的度的最大值称为该数的度

叶子(终端结点)

度不为零的结点

儿子

一个结点的后继结点称为该结点的儿子

父亲

一个结点称为其后继结点的父亲

子孙

一个结点的所有子树中的结点称为该结点的子孙

祖先

是从根结点到达一个结点的路径上通过的结点称为该结点的子孙

兄弟

同一个父结点上的结点

堂兄弟

其父亲在同一结点上的结点

结点的层次

从根结点开始定义

深度

树中结点的最大层数称为树的层数或高度

有序树

若把树中结点的各子树看成是从左到右(不可置换)有序的,则称为有序树;否则,称为无序树

森林

0个或多个不相交的树的集合称为 森林

路径

在树中,若从结点Ki开始沿着数枝自上而下能到达结点Kj,则称从Ki到Kj存在一条路径

注:路径的长度等于所经过树枝的长度

森林详解:

由m棵互不相交的树构成的集合称为森林。森林和树的概念十分相近,一棵树中每个结点其子树的集合即为一个森林;而在森林中的每棵树之上加一个共同的根,森林就成为了一棵树

树的其他表示方法

嵌套集合表示法(了解)

凹入表示法(了解)

树的存储结构

根据数据元素之间关系的不同表示方式,常用的数的存储结构主要有三种:双亲表示法、孩子表示法、孩子兄弟表示法

双亲表示法

存储树中的结点时,可以包含两个信息:结点的值data和体现结点之间相互关系的属性——该结点的双亲parent。借助于每个结点的这两个信息便可唯一地表示任意一棵树,这种表示方法称为双亲表示法

代码实现

#define MAXSIZE 100
typedef char datatype;
typedef struct node
{
	datatype data;
	int parent;
}node;
typedef struct tree
{
	node treelist[MAXSIZE];
	int length,root;
}tree;

孩子表示法

整棵树中所有结点的相互关系是通过指明结点子女的位置来体现的,称这种表示法为孩子表示法

分类:

指针式的孩子表示法

数组式的孩子表示法

链表式的孩子表示法

//指针式的孩子表示法
#define m 3               //度数
typedef char datatype;    //结点值类型
typedef struct node{
	datatype data;
	struct node *child[m];//指向子女的指针数组
}node ,*tree;
tree root;
//数组式的孩子表示法
#define m 3               //树的度数
#define MAXSIZE 20        //存放树结点的数组大小
typedef char datatype;    //树中结点值的类型
typedef struct node{      //树中结点的类型
	datatype data;
	int child[m];
}treenode;
treenode tree[MAXSIZE];   //根节点下标
int root;                 //树中实际所含结点的个数
int length;
//链表式的孩子表示法
#define MAXSIZE 50
typedef char datatype;
typedef struct chnode{           //孩子结点的类型
	int child;
	struct chnode *next;
}chnode,*chpoint;
typedef struct{                  //树中每个结点的类型
	datatype data;
	chpoint firstchild;          //指向第一个子女的指针
}node ;
typedef struct{                  //树的类型
	node treelsit[MAXSIZE];
	int length,root; 
}tree; 

孩子兄弟表示法

在存储树中的每个结点时,除了包括结点值域外,还设置两个指针域firstchild和rightsibling,分别指向该结点的第一个子女和其右兄弟,即以二叉链表方式加以存储,因此该方法也常被称为二叉树表示法

typedef char datatype;                 //树中结点的类型
typedef struct node{                   //树中每个结点的类型
	datatype data;
	struct node *firstchild ,*rightsibling;
}node ,*pnode;
pnode root;                            //指向树根结点的指针

树的遍历

定义

指按某种规定的顺序访问树中的每一个结点一次,且每个结点仅被访问一次。数的遍历方式分为以下三种

树的前序遍历:首先访问根节点,再依次按前序遍历的方式访问根节点的每一棵子树。

树的后序遍历:首先按后序遍历的方式访问根节点的每一棵子树,然后再访问根结点。

树的层次遍历:首先访问第一层上的根结点,然后从左到右依次访问第二层上所有的结点,再以同样的方式访问第三层的结点,......,最后访问树中最低一层的所有结点。

树的前序遍历的递归实现

void preorder(tree p)          //*p为指向树根节点的指针
{
	int i;
	if(p!=NULL)                //树不为空
	{
		printf("%c",p->data);  //输出根节点的值
		for(i=0;i<m;++i)       //依次递归实现树的前序遍历
			preorder(p->child[i]);
	}
 } 

树的后序遍历的递归实现

void postorder(tree p)       //*p为指向树根节点的指针
{
	int i;
	if(p!=NULL)              //树不为空
	{
		for(i=0;i<m;++i)     //依次递归实现各子树的后序遍历
			postorder(p->child[i]);
		printf("%c",p->data);//输出根节点的值
	}
 } 

按前序遍历顺序建立一棵度数为3的树的递归算法

void creattree(tree *p)
{
	int i;
	char ch;
	if((ch=getchar())==' ') *p=NULL;
	else 
	{
		*p=(tree)malloc(sizeof(node));
		(*p)->data=ch;
		for(i=0;i<m;++i)
			creattree(&(*p)->child[i]);
	}
 } 

树的层次遍历算法

在树的层次遍历过程中,对于某一层上的每个结点被访问后,应立即将其所有子女结点按从左到右的顺序依次保存起来,该层上所有结点的这些子女结点正好构成下一层的所有结点,接下来应该被访问的就是它们。显然,这里用于保存子女结点的数据结构选择队列最合适,队伍中的每个元素均为在排队等待访问的结点。

由于树的层次遍历首先访问的是根结点,因此初始值时队列中仅包含根结点。只要队列不为空,就意味着还有结点未被访问,遍历就必须继续进行;每次需访问一个结点时均取队头元素,访问完成后若其子女非空,则将其所有子女按顺序依次进队;不断重复以上过程,直到队列为空。

代码实现

void levelorder(tree t)         //t为指向树根结点的指针
{
	tree queue[20];             //存放等待访问的结点队列
	int f,r,i;                  //f、r分别为队头、队尾指针
	tree p;
	f=0;
	r=0;
	queue[0]=t;
	while(f<=r)                 //队列不为空
	{
		p=queue[f];
		f++;
		printf("%c",p->data);   //访问队头元素
		for(i=0;i<m;++i)        //将刚被访问的元素的所有子女结点依次进队
		{
			if(p->child[i])
			{
				++r;
				queue[r]=p->child[i]; 
			}
		}
	}
}

树的线性表示

树的线性表示便于树的输入、输出,同时在存储时也比较节省空间。本节主要介绍树的两种线性表示方法:括号表示法和层号表示法。

树的括号表示

  1. 若T为空树,其括号表示为空;
  2. 若树T只包含一个结点,则其括号表示即为该结点本身;
  3. 如果树T由根结点A和它的m棵子树T1,T2,......Tn构成,则其括号表示为:
  • A(T1的括号表示,T2的括号表示,......,Tn的括号表示)其中子树的括号表示同样应该遵循以上规则

树的括号表示具有以下特点:

  1. “( ”  前面的元素一定为某棵树或子树的根结点,而其所有子树中的结点一定位于该“( ”  和与之对应的“( ” 之间;
  2. 任何“( ” 和与之配对的“( ” 之间的括号表示序列同样满足(1)中的性质。

树的括号表示和树的孩子表示转换算法

从左至右扫描树的括号表示

每当遇到左括号时,其前一个结点进栈,并读下一个符号

每当遇到有括号时,栈顶元素出栈。说明以栈顶元素为根的子树结构完毕,若此时栈为空,算法结束,否则读下一个符号

没当遇到“,”,则滑过该符号,并读下一个符号。

#define m 3                  //树的度数
#define MAXSIZE 20           //树的孩子表示法对应的数组的大小
#define BMAXSIZE 50          //树的括号表示对应的数组大小
typedef char datatype;       //树的结点类型
typedef struct node{         //树中孩子表示法中结点的类型
	datatype data;
	int child[m]; 
}treenode;
treenode tree[MAXSIZE];      //树孩子表示法的存储数组
int root;                    //根节点下标
int length;                  //树中实际所含结点个数
char p[BMAXSIZE];            //存放树括号表示的数组

//将树的括号表示法转换成树的孩子表示法
void bracktotree(char p[],int *root,int *length,treenode tree[])
{
	int stack[MAXSIZE];      //存储树或子树根结点的栈
	int top;                 //栈顶指针
	int i,j,k,l,done;        //done为程序结束的标志
	k=0;
	j=0;
	*root=0;
	top=0;                   //栈和标志的初始化
	done=1;
	tree[j].data=p[k];       //产生孩子表示法中的根结点
	++k;
	for(i=0;i<m;++i)
		tree[j].child[i]=-1;
	while(done)
	{
		if(p[k]=='(')        //遇到左括号,则前面的元素对应结点进栈
		{
			stack[top]=j;
			++top;
			++k;
		}
		else if(p[k]==')')   //遇到有括号,栈顶元素出栈
		{
			--top;
			if(top=0) done=0;//栈顶为空,算法结束
			else ++k;
		}
		else if(p[k]==',')
			++k;
		else                 //将当前被扫表的元素作为栈顶元素的子女
		{
			++j;
			tree[j].data=p[k];
			for(i=0;i<m;++i)
				tree[j].child[i]=-1;
				l=stack[top-1];
				i=0;        //寻找栈顶元素当前第一个空子女
				while(tree[l].child[i]!=-1)
					++i;
					tree[l].child[i]=j;
					++k;
		}
	}
}

树的层号表示法和树的扩充孩子表示转换算法

#define m 3                        //树的度数
#define MAXSIZE 20                 //数组元素个数的最大值
typedef char datatype;             //树中结点值的类型
typedef struct node{               //树的扩充孩子表示法中结点的类型
	datatype data;
	int child[m];
	int parent;
}treenode;
typedef struct{                    //层号表示法中结点的类型
	datatype data;
	int lev;                       //存储结点的层号
}levelnode;
treenode tree[MAXSIZE];            //树的扩充孩子表示法存储数组
int root;                          //根结点下标
int lenth;                         //树中实际所含结点的个数
levelnode ltree[MAXSIZE];          //树层号表示法的数组
//将树的层号表示法转换成树的扩充孩子表示法
leveltree(int length,levelnode ltree[],int *root,treenode tree[])
{
	int i,j,k;
	for(i=0;i<length;++i)
		for(j=0;j<m;++j) tree[i].child[j]=-1;
	*root=0;                       //第一个元素为根结点
	tree[0].data=ltree[0].data;
	tree[0].parent=-1;             //根结点的双亲为空
	for(i=1;i<length;++i)
	{
		tree[i].data=ltree[i].data;
		j=i-1;
		if(ltree[i].lev>ltree[j].lev)//结点i为前一个元素j的第1个子女
		{
			tree[i].parent=j;
			tree[j].child[0]=i;	
		}
		else
		{
			while(ltree[i].lev<ltree[j].lev)//寻找i的兄弟
				j=tree[j].parent;
			tree[i].parent=tree[j].parent;  //结点i和结点j的双亲相同
			j=tree[j].parent;
			k=0;
			while(tree[j].child[k]!=-1) ++k;
			tree[j].child[k]=i;
		}
		
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值