数据结构——树(二叉树)

树的存储

满二叉树 & 完全二叉树(编号和满二叉树编号相同)
二叉树性质:
1)在二叉树的第i层上至多有2^(i-1)个结点
2)深度为k的二叉树至多有2^k-1个结点
3)任意二叉树,若叶子数为n0,度为2的结点数为n2,则n0=n2+1;
4)具有n个结点的完全二叉树的深度为
完全二叉树编号的性质:【floor-向下取整,ceil-向上取整】
1)若编号i=1,则i是根结点;若i>1,则i的双亲结点编号为floor(i/2)。
2)若2i>n,则i为叶子节点;若2i≤n,则2i为i的左孩子
3)若2i+1>n,则i无右孩子;若2i+1≤n,则2i+1是i的右孩子。

顺序存储

一、存储结构描述

typedef struct{
	datatype data[maxsize];
	int last;//指示最后一个结点的位置
}sequenlist;

二、二叉树的建立(顺序存储)
通过加入虚结点,将二叉树映射为完全二叉树,用完全二叉树的方式存储,即把结点放入对应编号的数组单元中。

链式存储

一、存储结构描述

//二叉链表
typedef struct node{
	datatype data;
	struct node *lchild,*rchild;
}bitree;
bitree *root;
//三叉链表:在二叉链表的基础上,加入parent指针域

二、二叉树的建立(链式存储)

//借助队列
bitree* Creatree(){
	char ch;//结点信息变量
	bitree *Q[maxsize];//队列,指针数组充当
	int front,rear;//队头、队尾指示变量
	bitree *root,*s;//根结点和中间变量指针
	root=NULL;//二叉树置空
	fornt=1;rear=0;//设置队列指针变量初值
	
	while((ch=getchar())!='#'){//‘#’是结束字符
		s=NULL;//将中间变量置空
		if(ch!='@'){//@为虚结点
			s=(bitree*)malloc(sizeof(bitree));//当ch不是虚结点时建立新结点
			s->data=ch;
			s->lchild=NULL;
			s->rchild=NULL;
		}
		rear++;//队尾加1,指向新结点地址在队列中的位置
		Q[rear]=s;//无论s是不是虚结点,都入队
		//以下开始判断结点间的指向关系
		if(rear==1)
			root=s;
		else{
			if(s && Q[front])//孩子和双亲都不是虚结点时进行指向
				if(rear%2==0)//左孩子
					Q[front]->lchild=s;
				else//右孩子
					Q[front]->rchile=s;
			if(rear%2==1)//右孩子也指向完毕,则出队
				front++;
		}
	}
	return root;
}

*图示:
在这里插入图片描述

树的遍历

深度优先

先序遍历:DLR,先根,后左右子树
中序遍历:LDR
后序遍历:LRD

一、先序遍历

void preorder(bitree *p){
	if(p!=NULL){//结束条件,二叉树为空
		printf("%c ",p->data);
		preorder(p->lchild);
		preorder(p->rchild);
	}
	return;
}

二、中序遍历
1、递归算法:

void inorder(bitree *p){
	if(p!=NULL){
		inorder(p->lchild);
		printf("%c ",p->data);
		inorder(p->rchild);
	}
	return;
}

2、非递归算法(借助栈)

bitree *stack[N];
void ninorder(bitree *p){
	bitree *s;
	int top;
	if(p!=NULL){
		top=-1;
		s=p;
		while((top!=-1)||(s!=NULL)){
			while(s!=NULL){
				if(top==N-1){
					printf("overflow\n");
					return;
				}
				else{
					top++;
					stack[top]=s;
					s=s->lchild;
				}
			}//寻找最左结点
			s=stack[top];
			top--;
			printf("%c ",s->data);//中序遍历
			s=s->rchild;
		}
	}
	return;
}

三、后序遍历

void postorder(bitree *p){
	if(p!=NULL){
		postorder(p->lchild);
		postorder(p->rchild);
		printf("%c",p->data);
	}
	return;
}

广度优先

适用场景:「层序遍历」、「最短路径」
参考👉讲解

//借助队列
bitree *Q[maxsize];
void Layer(bitree *p){
	bitree *s;
	if(p!=NULL){//树非空时
		rear=1;
		front=0;//这时,队头队尾的初始化和构建二叉树时不同
		Q[rear]=p;//根节点入队
		while(front<rear){//当队列非空时
			front++;
			s=Q[front];//出队
			printf("%c ",s->data);//出队时遍历
			if(s->lchild!=NULL){
				rear++;
				Q[rear]=s->lchild;
			}
			if(s->rchild!=NULL){
				rear++;
				Q[rear]=s->rchild;
			}
		}
	}
	return;
}

从遍历序列恢复二叉树

一、基本思想
先序和中序,或,中序和后序,能唯一确定二叉树
在这里插入图片描述
二、算法

//递归
datatype preod[maxsize],inod[maxsize];//以先序和中序为例
bitree *BPI(datatype preod[],datatype inod[],int i,int j,inj k,int l){
//i、j、k、l分别是先序、中序序列数组的起始和终点下标
	int m;
	bitree *p;
	p=(bitree*)malloc(sizeof(bitree));//构造根节点
	p->data=preod[i];
	m=k;
	while(inod[m]!=preod[i])//寻找根结点在中序序列中的位置
		m++;
	if(m==k)//构造左子树
		p->lchild=NULL;
	else
		p->lchild=BPI(preod,inod,i+1,i+m-k,k,m-1);//注意参数
	if(m==l)//构造右子树
		p->rchild=NULL;
	else
		p->lchild=BPI(preod,inod,i+m-k+1,j,m+1,l);
	return p;
}

遍历算法的应用

统计二叉树中叶子节点数

一、计算公式
在这里插入图片描述
二、算法

int CountLeaf(bitree *p){
	if(!p)
		return 0;
	else if(!p->lchild && !p->rchild)
		return 1;
	else
		return CountLeaf(p->lchild)+CountLeaf(p->rchild);
}

求二叉树深度

一、计算公式
在这里插入图片描述
二、算法

int Height(bitree *p){
	int lc,rc;
	if(p==NULL)
		return 0;
	lc=Height(p->lchild)+1;
	rc=Height(p->rchild)+1;
	return lc>rc?lc:rc;
}

树、森林、二叉树之间的转换

树转换为二叉树

在这里插入图片描述

任何一个树转换为二叉树后,二叉树的右子树为空

二叉树转换为树

在这里插入图片描述

森林转换成二叉树

在这里插入图片描述

树的应用

哈夫曼树

一、定义
在有n个带权叶子结点的所有二叉树中,带权路径长度WPL最小的二叉树为哈夫曼树,也称最优二叉树

树的带权路径长度WPL:树中所有叶子结点的带权路径长度之和;
树的路径长度:树根到树中每一个结点的路径长度之和;
哈夫曼树不唯一,完全二叉树不一定是哈夫曼树
严格二叉树:树中没有度为1的结点

二、哈夫曼树构造算法
1、思想
在这里插入图片描述
2、存储结构

#define n 6 //叶子结点个数
#define m 2*n-1 //整个哈夫曼树的结点个数
typedef char datatype;
typedef struct{
	float weight;
	datatype data;
	int lchild,rchild,parent;
}hufmtree;
hufmtree tree[m];

3、实现

#include <float.h>
#define EPS 1e-5
void HUFMAN(hufmtree tree[]){
	int i,j,p1,p2;//p1、p2用于存储最小的两个结点的下标
	char ch;
	float small1,small2,f;//small1、small2用于存储最小的两个权值(small1<small2)
	for(i=0;i<m;i++){//初始化
		tree[i].parent=-1;
		tree[i].lchild=-1;
		tree[i].rchild=-1;
		tree[i].weight=0.0;
		tree[i].data='0';
	}
	for(i=0;i<n;i++){//读入n个叶子的信息
		scanf("%f",&f);tree[i].weight=f;
		scanf("%c",&ch);tree[i].data=ch;
	}
	for(i=n;i<m;i++){//进行n-1次合并,产生n-1个新结点
		p1=p2=-1;
		small1=small2=FLT_MAX;
		for(j=0;j<=i-1;j++){//寻找最小的结点
			if(tree[j].parent==-1){
				if(tree[j].weight-small1<EPS){//若当前结点权值比最小的还小
					small2=small1;//把当前的最小赋给次小
					small1=tree[j].weight;//把最最小赋给最小
					p2=p1;p1=j;
				}
				else if(tree[j].weight-small2<EPS){//若当前结点权值小于次小
					small2=tree[j].weight;p2=j;
				}
			}
		}
		tree[p1].parent=i;//更新合并
		tree[p2].parent=i;
		tree[i].rchild=p2;
		tree[i].lchild=p1;
		tree[i].weight=tree[p1].weight+tree[p2].weight;
	}
}		

哈夫曼编码&译码

编码

一、思想
在这里插入图片描述

n个叶子的最大编码长度不会超过n-1
每个叶子的编码都是不一样的,且能区分编码的起始和结束

二、存储结构

typedef char datatype;
typedef struct{
	char bits[n];//编码数组,位串
	int start;//编码在位串中的起始位置
	datatype data;//结点值
}codetype;
codetype code[n];

三、实现

void HUFMANCODE(codetype code[],hufmtree tree[]){
	int i,c,p;
	codetype cd;//缓冲变量
	for(i=0;i<n;i++){ //n为叶子结点数
		cd.start=n;//从叶子结点出发向上回溯
		c=i;p=tree[c].parent;
		cd.data=tree[c].data;
		while(p!=-1){
			cd.start--;
			if(tree[p].lchild==c)
				cd.bits[cd.start]='0';
			else 
				cd.bits[cd.start]='1';
			c=p;
			p=tree[c].parent;
		}
		code[i]=cd;
	}
}

*实例:
在这里插入图片描述

译码

在这里插入图片描述
上算法

void HUFMANDECODE(codetype code[],hufmtree tree[]){
	int i;
	char b;
	int endflag=-1;
	i=m-1;//m-1为根结点下标
	scanf("%d",&b);
	while(b!=endflag){
		if(b==0)
			i=tree[i].lchild;
		else
			i=tree[i].rchild;
		if(tree[i].lchild==-1){//判断tree[i]是否为叶子
			putchar(code[i].data);
			i=m-1;//回到根结点
		}
		scanf("%d",&b);
	}
	if(i!=m-1)//电文读完还没到叶子节点,说明输入有错
		printf("\nerror\n");
}

二叉排序树及其构建&结点删除

构建

一、定义
在这里插入图片描述
二、存储结构

typedef int keytype;
typedef struct node{
	keytype key;//关键字项
	datatype other;//其他数据项
	struct node *lchild,*rchild;
}bstnode;

三、实现

//往二叉排序树中插入一个结点
bstnode *INSERTBST(bstnode *t,bstnode *s){//t为二叉排序树的根指针,s为插入的结点指针
	bstnode *f,*p;//f指向p的parent,p用来查找插入点
	p=t;
	while(p!=NULL){
		f=p;
		if(s->key==p->key)//树中已有s结点,无需插入
			return t;
		if(s->key < p->key)
			p=p->lchild;
		else 
			p=p->rchild;
	}
	if(t==NULL)//原数为空,返回s作为根结点
		return s;
	if(s->key < f->key)
		f->lchild=s;
	else
		f->rchild=s;
	return t;
}
//生成二叉排序树
bstnode *CREATBST(){
	bstnode *t,*s;
	keytype key,endflag=0;//endflag为结点结束标志
	datatype data;
	t=NULL;
	scanf("%d",&key);
	while(key!=endflag){
		s=(bstnode*)malloc(sizeof(bstnode));
		s->lchild=s->child=NULL;
		s->key=key;
		scanf("%d",&data);
		s->other=data;
		t=INSERTBST(t,s);
		scanf("%d",&key);
	}
	return t;
}

结点删除

呜呜呜待续

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值