数据结构(树与二叉树)

树与二叉树:

树的基本概念:
树的定义:
树是由n个节点组成的有穷集合(不妨用D来表示这个集合)与在D上关系的集合R构成的结构,记为T。当n=0时,称T为空树。对于任何一个非空树,有一个特殊节点t,称之为t的根节点,其余节点被分解成m个不相交的子集,其中每个子集Di本身又构成一棵树,称之为根节点的子树。
树的定义是一个递归定义,因为他的任何一棵子树也是树。
在一棵树中,一个节点被定义成子树根节点的直接前驱节点,而其子树的根节点则是他的直接后继节点,从逻辑上看,树形结构具有以下特点:
1.任何非空树中有且仅有一个节点没有前驱节点,这个节点就是树的根节点
2.除根节点除外,其余所有节点有且仅有一个前驱节点
3.包括根节点在内,每个节点可以有多个直接后继节点
4.树形结构是一种具有递归特征的数据结构
树形结构中的元素之间存在的关系通常是一对多,或者多对1 的关系。
在计算机领域,磁盘上信息组织的目录结构也是一种树形结构。
在计算机网络系统中,域名管理也是按照树形结构进行的
树的逻辑表示方法:
1.树形结构表示法:
2.文氏图表示法
3.凹入表示法
4.嵌套括号表示法
基本术语:
1.节点的度:节点拥有子树的数目称为该节点的度
2.树的度:树种各节点的最大值被定义为该树的度
3.叶子节点:度为0 的节点称为叶子节点或者终端节点,简称叶节点
4.分支节点:度不为0的节点称为分支节点或者非终端节点
5.节点的层次:
6.树的深度:树中节点的最大层次数被定义为该树的深度或者高度
7.有序树
8.森林:m棵不相交的树的集合被称为森林或树林。
树的性质:
1.非空树的节点总数等于树中所有节点的度之和加1
2.度为k的非空书的第i层最多有ki-1个节点
3.深度为h的k叉树最多有(kh-1)/(k-1)个节点
4.具有n个节点的k叉树的最小深度为┌logk(n(k-1)+1)┐
树的基本操作,
1.建立一棵空树T
2.求节点n所在树的根节点,或者求树T的根节点
3.求树T中节点X的双亲节点
4.求树T中节点x的第i个孩子节点
5.求树T中节点x右边的兄弟节点
6.把以S为根的树插入到树T中作为节点X的第i棵子树
7.删除树T中节点x的第i棵子树
8.对一棵树进行遍历,即按照某个次序依次访问树中所有节点,每个节点仅被访问一次,得到一个由所有节点组成的序列
树的存储结构:
多重链表表示法:
1.定长链接点的多重链表表示
这种存储方法取树的度作为每个链接点的指针数目。
描述如下:
#define MaxTreeD 100
typedef struct node {
int data;
struct node *child[MaxTreeD];
}DTREE;
2.不定长链接点的多重链表表示
这种存储方法是每个链接点都取自己的度数作为指针域的数目。
可以定义如下:

#define MaxTreeD 100
typedef struct node {
	int data;
	int degree;
	struct node *child[MaxTreeD];
}DTREE;

三重链表表示法:
这种存储方式是对树中每个节点除了数据域外都设置了三个指针域,其中第一个域给出节点第一个孩子的节点所在的链接地址,第二个指针域给出改节点的双亲节点所在的链接点的地址,第三个指针域给出该节点右边第一个兄弟节点所在的链接点地址。如果不存在,则为NULL。
对于三重链表的定义如下:

typedef struct node {
	int data;
	struct node *child,*parent,*brother;
}TTREE;

二叉树:
二叉树是有序树。
二叉树的定义:
二叉树是n个节点的有穷集合D与D上关集合R构成的结构。当n=0时,称该二叉树为空二叉树。否则,他便包含了一个根节点以及两棵不相交的,分别左子树,右子树的二叉树。
二叉树的定义是递归的。
二叉树的操作:
1.置BT为空二叉树
2.求二叉树BT的根节点或者求节点x所在二叉树的根节点。若BT是空二叉树或者x不在二叉树上,则该操作返回空。
3.求二叉树BT中节点x双亲节点。若x节点是二叉树BT的根节点或者二叉树不存在X节点,则该操作返回为空。
4.分别求二叉树BT中节点x的左孩子和右孩子节点。若x节点是二叉树BT的叶节点或者二叉树不存在X节点,则该操作返回为空。
5.分别求二叉树BT中节点x的左兄弟和右兄弟节点,若节点x为二叉树的根节点,或者二叉树不存在x节点或者节点x是其双亲节点的左右孩子,则返回操作为空。
6.生成一棵以节点x为根节点,分别以二叉树LBT和RBT为左子树和右子树的二叉树
7.将以节点y为根节点且右子树为空的二叉树分别置为二叉树BT中节点x的左子树和右子树,若节点x有左子树(右子树),则插入后作为节点y的右子树
8.分别删除二叉树BT中以节点x为根节点的左子树和右子树。若节点x无左子树或右子树,则该操作为空操作。
9.按某种次序依次访问二叉树BT中各个节点,并且每个节点只能被访问一次,,得到一个由该二叉树所有节点组成的序列
10.销毁一个二叉树。该操作删除二叉树BT中所有节点,并释放他们的存储空间,是BT成为一个空二叉树
11.求二叉树中节点x所处的曾次
12.求二叉树BT的深度。若二叉树为空,则其深度为0;否则,其深度等于左子树与右子树最大深度加1.

特殊形态的二叉树:
1.满二叉树:
如果二叉树中的任意一个节点,或者是叶节点,或者有两棵非空子树,而且叶节点都集中在二叉树的最下面一层,这样的二叉树称为满二叉树。
2.完全二叉树:
若二叉树中最多只有下面两层节点的度可以小于2,并且下面一层的节点(叶节点)都依次排列在最左边的位置上,这样的二叉树称为完全二叉树。
完全二叉树的特点:
1.叶节点只能在二叉树最下面两层出现。
2.对于任意节点,若其右分支下的子孙节点的最大层次数位s,则其左分支下的子孙节点最大层次一定为s+1
在一棵二叉树中,若除了最下面一层外,其余其余各层都是满的,这样的二叉树称为平衡二叉树。理想平衡二叉树包括满二叉树和完全二叉树。

二叉树的性质:
性质一:具有n个节点的非空二叉树有且仅有n-1个分支
性质二:非空二叉树的第i层最多有2i-1个节点
性质三:深度为h的非空二叉树最多有2h-1个节点
性质四:在任意非空二叉树中,若叶节点的数目为n0,度为2的节点数目为n2,则有关系n0=n2+1成立。
性质五:具有n(n>0)个节点的完全二叉树的深度h=˪log2n˩+1
二叉树与树,森林之间的转换:
1.树与二叉树的转换
将一般树转化为二叉树按一下步骤进行
1.在所有相邻兄弟节点之间分别加一条连线。
2.对每个分支节点,除了最左孩子外,删去该节点与其他孩子节点的连线
3.以根节点为轴心,顺时针旋转45。。
树林与二叉树的转换:
将一棵树转换为二叉树的过程如下:
1.分别将树林中的每棵树转化为二叉树
2.从最后那棵二叉树开始,依次把后面的那颗二叉树的根节点作为前一棵二叉树的根节点的右孩子,直到所有二叉树全部链接,这样得到的二叉树的根节点就是树林中第一棵树的根节点。
可以得到如下定义:
设F={T1,T2,T3…Tn}为树林,则此树林所对应的二叉树B(F)可以递归定义如下:
1.若n=0,则B(F)为空二叉树
2.若n>0,则B(F)的根节点为T1的根节点,其左子树是B(T11,T12…T1m)其中T11,T12…T1m是T1的子树,即左子树是从T1中根节点的子森林F1={T11,T12…T1m}转换而成的二叉树;B(F)的右子树为B(T2,T3…Tn)即从森林F={T2,T3…Tn}转换而成的二叉树。
二叉树还原为一般树:
将一棵由一般树转换成二叉树还原为树可以按下列步骤进行:
1.若某节点是其双亲节点的左孩子,则将该节点的右孩子以及当且仅当连续的沿着此右孩子方向不断的搜索到的所有右孩子,都分别与该节点的双亲节点用虚线连接起来。
2.删去原二叉树中所有双亲节点与其右孩子连线
3.将图形规整化,使各节点按层次排列,并且将虚线改成实线。
二叉树的存储结构:

二叉树的顺序存储结构:
对于完全二叉树来说采用顺序存储比较方便,且不浪费空间。
当树的形态与大小经常发生动态变化时,适合链式存储结构。
二叉树的链式存储结构:
两种方式:
1.二叉链表结构
链表中每个链接点由三个域组成,数据域和两个指针域,分别用来给出左右两个孩子的地址
2.三叉链表结构
就是在二叉链表的基础上加了一个指针双亲节点的指针域。
二叉树与树的遍历:
通常有三种遍历方式:前序遍历(DLR)中序遍历(LDR)后序遍历(LRD)
1.前序遍历:
前序遍历的原则是:
1.访问根节点
2.以前序遍历方式遍历根节点的左子树
3.以前序遍历方式遍历根节点的右子树

void preoreder(BETREE T)
{
	if (T != NULL)
		Vist(T);//访问T所指的节点
	preoreder(T->lchild);//访问左子树
	preoreder(T->rchild);//访问右子树
}
2.中序遍历:

中序遍历的原则是:
1.以中序遍历方式遍历根节点的左子树
2.访问根节点
3.以中序遍历方式遍历根节点的右子树

void INRDER(BETREE T)
{
	if (T != NULL)
			INRDER(T->lchild);//访问左子树
Vist(T);//访问T所指的节点
			INRDER(T->rchild);//访问右子树
}

3.后序遍历
后序遍历的原则是:
1.以后序遍历方式遍历根节点的左子树
2.以后序遍历方式遍历根节点的右子树
3.访问根节点

void POSTORDER(BETREE T)
{
	if (T != NULL)
			POSTORDER(T->lchild);//访问左子树
POSTORDER(T->rchild);//访问右子树
Vist(T);//访问T所指的节点
}

层次遍历:

void LAYERORDER(BTREE T)
{
	BTREE QUEUE[M], p;
	int front, rear;
	if (T != NULL) {
		QUEUE[0] = T;
		front = -1;
		rear = 0;
		while (front < rear)
		{
			p = QUEUE[++front];
			visit(p);
			if (p->lchild != NULL)
				QUEUE[++rear] = p->lchild;
			if (p->rchild != NULL)
				QUEUE[++rear] = p->rchild;
		}
	}
}

由遍历序列恢复二叉树(自己找方法)
二叉树的等价性:
如果说二叉树T1与二叉树T2是相似性,则是指他们具有相同的拓扑结构。也就是说要么他们都是空二叉树,要么他们都不空,并且他们的左右子树都分别相似
如果说二叉树T1与二叉树T2是等价的,则是指他们不仅具有相同的拓扑结构,而且在他们对应节点中包含相同的数据信息。
判断相似的算法:
利用二叉树的前序遍历操作对两棵二叉树进行测试:

int SIMILAR(BTREE T1, BTREE T2)
{
	if (!T1&&!T2)
		return 1;//两棵树为空
	if (T1&&T2&&SIMILAR(T->lchild, T->rchild) && SIMILAR(T->rchild, T->lchild))//左右子树相似
		return 1;
	return 0;
}

树和树林的遍历:
1.树的遍历
(1)前序遍历方式:若被遍历的树非空,则:
1.访问根节点
2.依次按照前序遍历方式递归的遍历根节点的每一棵子树
(2)后序遍历方式:若被遍历的树非空,则:
1.依次按照后序遍历方式递归的遍历根节点的每一棵子树
2.访问根节点
树林的遍历:
前序遍历:若被遍历的树林非空,则:
1.访问第一棵树的根节点
2.按照前序遍历方式遍历第一棵树上的根节点的子树林
3.按照前序遍历方式遍历除去第一棵树以后的子树林
中序遍历:若被遍历的树林非空,则
1.按照中序遍历方式遍历第1棵树上根节点的子树林
2.访问第一棵树的根节点
3.按照中序遍历方式遍历除去第1棵树以后的子树林

基于二叉树遍历操作的算法举例:
1.二叉树的建立
采用前序遍历算法,建立二叉树的过程如下:
1.输入一个根节点
2.若输入的是“ ”(空格字符),则表明二叉树为空,置为NULL
3.若输入的不是“ ”,则将该字符赋值给T->data,然后依次递归的建立他们的左子树T->lchild和右子树T->rchild。

typedef struct node {
	char data;
	struct node *lchild, *rchild;
}*BTREE,BTNode;

void BUILDBT(BTREE &T)
{
	char ch;
	scanf("%c",&ch);//输入一个元素
	if (ch == ' ')
	{
		T = NULL;//建立空二叉树
	}
	else {
		T = (BTREE)malloc(sizeof(BTNode));//生成一个新的链接点
		T->data = ch;
		BUILDBT(T->lchild);//递归建立左子树
		BUILDBT(T->rchild);//递归建立右子树
	}
}

2.二叉树的销毁
//二叉树的销毁
//删除并释放二叉树中所有节点空间的算法如下

void DESTROYBT(BTREE T)
{
	if (T != NULL)
	{
		DESTROYBT(T->lchild);
		DESTROYBT(T->rchild);
		free(T);
	}
}

//销毁二叉树的算法如下:
//销毁二叉树的算法

void CLEARBT(BTREE &T)
{
	DESTROYBT(T);
	T = NULL;
}

3.二叉树的复制
二叉树的复制操作过程:
1.若二叉树为空,则返回空指针
2.若二叉树非空,则复制根节点,并将根节点指针赋予T2;然后复制根节点的左子树,并将左子树的根节点指针赋予T2->lchild;接着再复制根节点的右子树,并将右子树的根节点指针赋予T2->rchild;最后返回经复制得到的二叉树的根节点的指针。
//二叉树的复制

BTREE COPYBT(BTREE T1)
{
	BTREE T2;
	if (T1 == NULL)
		return NULL;
	else {
		T2 = (BTREE)malloc(sizeof(BTNode));
		T2->data = T1->data;
		T2->lchild = COPYBT(T1->lchild);
		T2->rchild = COPYBT(T1->rchild);
	}
	return T2;
}

4.测试二叉树是否等价:

int EQUALBT(BTREE T1, BTREE T2)
{
	if (T1 == NULL && T2 == NULL)
		return 1;
	if (T1 != NULL && T2 != NULL && T1->data == T2->data&&EQUALBT(T1->lchild, T2->lchild&&EQUALBT(T1->rchild, T2->rchild)))
		return 1;
}

求二叉树的深度
5.求二叉树的深度

int DepthBT(BTREE T)
{
	BTREE STACK1[M], p = T;
	int STACK2[M];
	int curdepth, maxdepth = 0, top = -1;
	if (T != NULL) {
			curdepth = 1;
		do {
			while (p != NULL)
			{
				STACK1[++top] = p;
				STACK2[top] = curdepth;
				p = p->lchild;//将p移到其左孩子节点
				curdepth++;//层次数加1
			}
			p = STACK1[top];
			curdepth = STACK2[top--];//退栈
			if (p->lchild == NULL && p->rchild == NULL)
				if (curdepth > maxdepth)
					maxdepth = curdepth;
			p = p->rchild;//将p移到右孩子
			curdepth++;//层次加一
		} while (!(p==NULL)&&top==-1);
	}
	return (maxdepth);//求得二叉树的深度
}

6.求节点所在的层次

int LAYERBT(BTREE T, char item)
{
	BTREE STACK1[M], p = T;
	int STACK2[M], flag,top = -1;
	do
	{
		while (p != NULL)
		{
			STACK1[++top] = p;
			STACK2[top] = 0;//标志0进栈
			p = p->lchild;//将p移到其左孩子节点
		}
		p = STACK1[top];
		flag = STACK2[top--];//退栈
		if (flag == 0)
		{
			STACK1[++top] = p;//当前p所指节点的地址再次进栈
			STACK2[top] = 1;//标志1进栈
			p = p->rchild;//将p移向其右孩子节点
		}
		else {
			if (p->data == item)//若当前节点满足条件
				return (top + 2);//求得的曾次数
			p = NULL;
		}
	} while (!(p==NULL&&top==-1));
}

7.二叉树的删除

BTREE DeleteBT(BTREE &T, char item)
{
	BTREE STACK[M], p, q = T;
	int top = -1;
	if (T->data == item)//如果根节点满足
	{
		DESTROYBT(T);//删除整棵树
		return NULL;
	}
	else {
		do
		{
			while (p != NULL)
			{
				if (p->data == item)//若p指针满足条件
				{
						if (q->lchild == p)
							q->lchild = NULL;//p的双亲节点左指针置为空
						else//p的双亲节点右指针置为空
							q->rchild = NULL;
					DESTROYBT(p);//删除并释放
					return T;
				}
			STACK[++top] = p;//当前p所指的节点的地址进栈
			q = p;//保存p所指节点的地址
			p = p->lchild;	//将p移到左孩子节点
			}
			p = STACK[top--];//退栈
			q = p;
			p = p->rchild;//p移到右孩子节点
		} while (!(p==NULL&&top==-1));
	}
}

8.交换所有节点左,右子树的位置

void ExchangeBT(BTREE T)
{
	BTREE QUEUE[M], temp, p = T;
	int front, rear;
	if (T != NULL)
	{
		QUEUE[0] = T;
		front = -1;//对头指针赋初始值
		rear = 0;//队尾指针赋初始值
		while (front < rear)//若队列不空
		{
			p = QUEUE[++front];//退出对头元素
			temp = p->lchild;
			p->lchild = p->rchild;
			p->rchild = temp;//叫唤左右节点的位置
			if (p->lchild != NULL)
				QUEUE[++rear] = p->lchild;//p所指的左孩子进队
			if(p->rchild!=NULL)
				QUEUE[++rear] = p->rchild;//p所指的右孩子进队
		}
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值