2021数据结构CH05【树与二叉树】

5.3.3

3.


#include<stdio.h>  
#include<stdlib.h>  
#define maxsize 5
#define ElemType int//假设元素类型为int

typedef struct BiTree{
	ElemType data;
	struct BiTNode *lchild,*rchile;
}BiTNode,*BiTree;

typedef struct{    
	ElemType stack[maxsize];    
	int top;
}stk; 
void InitSTK(stk &S){
}

//入栈操作
bool push(stk &S,ElemType &x){
}
//出栈操作
bool pop(stk &S,ElemType &x){
}
//栈的判空
bool StackEmpty(stk S){
}
//读取栈顶元素,返回栈顶元素的指针
GetTop(S,p);
void LRN_Stack(BiTree T){
	InitStack(S);
	p=T;
	r=NULL;
	while(p||!StackEmpty(S)){
		if(p){
			push(S,p);
			p=p->lchild;
		}
		else{
			GetTop(S,p);
			if(p->rchilid!=NULL&&p->rchild!=r){
				p=p->rchild;
				push(S,p);
				p=p->lchild;
			}
			else{
				pop(S,p);
				visit(p->data);
				r=p;
				p=NULL;
			}
		}
	}
}

4.

从上到下,从左到右,依次入队,并且依次出队,但不访问,而是入栈,全部入栈之后再全部出栈即可。

void LevelOrderFan(BiTree T){
	Stack S,Queue Q;
	InitStack(S);
	InitQueue(Q);
	EnQueue(Q,T);
	while(IsEmpty(Q)==false){//如果队列非空
		DeQueue(Q,p);//队头出队,返回队头指针
		Push(S,p);//入栈
		if(p->lchild){
			EnQueue(Q,p->lchild);
		}
		if(p->rchild){
			EnQueue(Q,p->rchild);
		}
	}
	while(IsEmpty(S)!=false){//出栈并访问,直到栈空
		Pop(S,p);
		visit(S,p);
	}
}

5.

//递归法 
int BtDepth1(BiTree T){
	if(T=NULL){
		return 0;
	}
	int ldep=BtDepth1(T->lchild);
	int rdep=BtDepth1(T->lchild);
	if(ldep>rdep)
		return ldep++;
	else 
		return rdep++;
}

//非递归法、
void BtDepth2(BiTree T){
	if(!T) return 0;//数为空 高度为0
	BiTree Q[Maxsize];//创建一个队列Q,用于存放二叉树的结点
	BiTree p;
	int last=0,level=0;//last记录二叉树每一层最右边的结点,初始默认为0,level为深度
	int front=-1,rear=-1;//队首队尾均从-1开始
	Q[++rear]=T;//根节点入队
	while(front<rear){//队列非空
		p=Q[++front];//队首出队,并且用p记录出队的结点
		if(p->lchild){//当前出队结点有左孩子
			Q[++rear]=p->lchild;//当前结点左孩子入队
		}
		if(p->rchild){//当前出队结点有右孩子
			Q[++rear]=p->rchild;//当前结点左孩子入队
		}
		if(front==last){//如果队首指针此时为二叉树当前层最右边的结点,则进行处理
			level++;
			last=rear;//last更新为下一层最右边的结点
		}
	}
	return level;
}

7.

bool IsComplele(BiTree T){
	InitQueue(Q);
	if(!T) return 1;
	EnQueue(Q,T);
	while(!IsEmpty(Q)){
		DeQueue(Q,p);
		if(p){//当前出队结点非空(只有左孩子,只有右孩子,有左右孩子,三种情况均入队,若左孩子为空,则入队的结点为NULL)
			EnQueue(Q,p->lchild);
			EnQueue(Q,p->rchild);
		}
		else//当前出队结点为空
			while(!IsEmpty(Q)){//若队列中还有元素,继续出队
				DeQueue(Q,p);
				if(p)//出队结点还有左或者右孩子
					return 0;
			}
	}
	return 1;
}

8.

int i=0;
int PreOrder(BiTree T){
	if(T!=NULL){
		visit(T);
		PreOrder(T->lchild);
		PreOrder(T->rchild,k);//必定在右子树中
	}
}

int visit(BiTNode *p){
	if(T->lchild!=NULL&&T->rchild!=NULL)
			i++;	
}

9.

int InOrder(BiTree T){
	if(T!=NULL){
		InOrder(T->lchild);
		InOrder(T->rclild);
		temp=b->lchild;
		b->lchild=b->rchild;
		b->rchild=temp;
	}
}

10.

int i=1;
ElemType PreOrder(BiTree T,int k){
	if(T==NULL)//遍历过程中发现非空二叉树的叶结点,则返回特殊字符
		return '#';
	if(i==k)//如过遍历到第k个结点了,返回结点的值
		return T->data;
	i++;//否则继续遍历下一个结点
	ch=PreOrder(T->lchild,k);//在左子树中找
	if(ch!='#')//遍历过程没有返回#,说明还没到左子树的叶子结点就发现了第K个元素,直接该结点的ch的值即可
		return ch;//ch为第k个结点的data值
	ch=PreOrder(T->rchild,k);//必定在右子树中
	return ch;
}

11.

BiTree Del_X_Tree(BiTree b){
	if(b){
		Delx_X_Tree(b->lchild);
		Delx_X_Tree(b->rchild);
		free(b);
	}
}

void SearchXFather(BiTree b,int x){
	if(b==NULL)
		return;
	if(b->data==x){
		Del_X_Tree(b);
		return ;
	}
	else{
		InitQueue(Q);
		BiTree Q[MaxSize];
		EnQueue(Q,b);
		while(!IsEmpty(Q)){
			DeQueue(Q,p);
			if(p->lchild){
				if(p->lchild->data==x){
					Delx_X_Tree(p->lchild);
					p->lchild=NULL;
				}
				else
					EnQueue(Q,p->lchild);
			}
			if(p->rchild){
				if(p->rchild->data==x){
					Delx_X_Tree(p->rchild);
					p->rchild=NULL;
				}
				else
					EnQueue(Q,p->rchild);
			}
				
		}
}

12.

bool PrintXZuxian(BiTree b,int x){
	if(!b) return false;//结点为空,直接返回false
	if(b->data==x) return true;//递归出口,找到了x,若只有一个结点本程序也符合输出要求
	if(printXZuxian(b->lchild,x)||printXZuxian(b->rchild,x)){//结点非空,查看左子树或者右子树中有没有符合要求的x
		printf("%d ",b->data);//有则输出 返回true
		return true;
	}
	else //与上一个if并列,没有则返回false
		return false;
}

13.

后序遍历非递归算法的逻辑:在遍历中,栈内元素都是当前元素的直系祖先

14.

typedef struct{
	BiTree data[MaxSize];
	int level[Maxsize];
	int front,rear;
}Qu;

int BiWidth(BiTree b){
	InitQueue(Q);
	front=-1;
	rear=-1;
	BiTree p;
	int k;//k用于记录出队结点的层次
	Qu.data[++rear]=b;//根节点入队
	Qu.level[Qu.rear]=1;//根节点就是第一层
	while(front<rear){//队列非空
		p=Qu.data[++Qu.front];//队首出队
		k=Qu.level[Qu.front];//出队结点的层次标记为k
		if(p->lchild){
			Qu.data[++Qu.rear]=p->lchild;
			Qu.level[Qu.front]=k+1;//孩子结点处在下一层
		}
		if(p->rchild){
			Qu.data[++Qu.rear]=p->rchild;
			Qu.level[Qu.front]=k+1;//孩子结点处在下一层
		}
	}
	//通过上面的代码,所有结点所处在的层数均记录在level数组中了
	k=1;//回到第一层,即从level的第一个元素开始遍历,找到出现最多的数是谁,即找到树宽
	int max=0,i=0,n;
	while(i<=Qu.rear){//rear从-1开始,层次遍历的最后一个结点位置为Qu.rear
		n=0;//每一层计完数之后归0
		while(i<=Qu.rear&&Qu.level[i]==k){
			n++;
			i++;
		}
		k=Qu.level[i];//i不断往前走,k的值也不断更新
		if(n>max)
			max=n;
	}
	return max;
}

15.

对于满二叉树,把pre转换成post

如上图,实际上就是把pre中的根1放到post中的根2,把pre中的左子树1放到post中的左子树2,把pre中的右子树1放到post中的右子树2。在每次递归转换的过程中把pre中的根1放到post中的根2,递归到最后的结果是只剩一个根节点需要转换。

所以最重要的就是找到pre中的起始和结尾位置分别对应于post中的起始和结尾位置。

递归转换到倒数第二轮的时候,pre一定只剩下三个结点,即根左右,此时仍旧只把根的位置转换好。

继续递归则只剩下一个结点,仍旧把他视为根节点,把pre中的根1放到post中的根2完成所有结点的转换,如下图。

本题代码如下:


void PreToPost(ElemType pre[],int f1,int r1,ElemType post[],int f2,int r2){
	//f1 为先序序列的起始地址
	//r1 为先序序列的结束地址
	//f2 为后序序列的起始地址
	//r2 为后序序列的结束地址
	int half;//用于记录先序序列中的中间结点
	if(r1>=f1){
		half=(r1-f1)/2;
		post[r2]=pre[f1];//转换根节点,如图,pre中的根1是post中的根2
		PreToPost(pre,f1+1,f1+half,post,f2,f2+half-1);//转换左子树,如图,pre中的左子树1对应post中的左子树2
		PreToPost(pre,f1+half+1,r1,post,f2+half,r2-1);//转换右子树,如图,pre中的右子树1对应post中的右子树2	
	}
}

16.

LinkList head;
pre=NULL;
LinkList InOrder(BiTree b){
	if(b){
		InOrder(b->lchild);//最左下的叶子结点
		if(b->lchild==NULL&&b->rchild=NULL){//只处理叶子结点
			if(pre=NULL){
				head=b;
				pre=b;
			}
			else{
				pre->rchild=b;
				pre=b;
			}
		}
		InOrder(b->rchild);
		b->rchild=NULL;
	}
	return head;
}

17.

三种情况 两空,一空,零空

bool Similar(BiTree a,BiTree b){
	int l,r;
	if(!a&&!b)//两树皆为空
		return true;
	else if(!a||!b)//由于else的存在,此处的if是建立在上一个if的基础下的,即上一个if和这一个if只能满足一个
		//即此处的if指的是只有一棵树为空
		return false;
	else{ //既不是两颗空树,也不是只有一棵树为空,即两棵树均非空
		l=Similar(a->lchild,b->lchild);
		r=Similar(a->rchild,b->rchild);
		return l&&r;//左右均为真才是真
	}
}

18.

线索二叉树的相关知识没有考过

19.

int WPL(BiTree b){
	return PreOrderWPL(b,0);
}

int PreOrderWPL(Bitree b,int deep){
	if(!b)
		return 0;
	else{
		static int wpl=0;
		if(!b->lchild&&!b->rchild)
			wpl=wpl+deep*b->weight;
		PreOrderWPL(b->lchild,deep+1);
		PreOrderWPL(b->rchild,deep+1);
	}
	return wpl;
}

20.

void BiTreeToE(BiTree b){
	if(!b)
		return;
	printf("(");
	BiTreeToE(b->lchild);
	printf("%s",b->data);
	BiTreeToE(b->rchild);
	printf(")");
}//次优解答,不考虑何时加括号,每时每刻加括号即可

5.4.5

5.

孩子兄弟表示法:左孩子右兄弟,故用孩子兄弟表示法存储森林时叶子结点无左孩子,右孩子可有可无。求叶子结点数,即求无左孩子的结点有多少个。

遍历森林:

1.树为空,叶子结点不存在,return 0;

2.当前遍历结点有两种情况

   若左子树为空,则叶子结点数为:1+右子树的叶子节点数

   若左子树不为空,则叶子结点数为:左子树的叶子节点数+右子树的叶子节点数

typedef struct node{
	ElemType data;
	struct node *child,*brother;
}*Tree;

int Yezi(Tree b){
	if(!b)
		return 0;
	if(!b->child)
		return 1+Yezi(b->brother);
	else
		return Yezi(b->child)+Yezi(b->brother);
}

6.

对于一棵单独的树(即非森林),化作孩子兄弟表示法存储结构时,因为一棵树不存在“兄弟”,所以这棵树的根节点一定是没有右孩子的。求这棵树的高度,就要分清这棵树的左子树的情况。

在孩子兄弟表示法存储结构中,树根的左子树可能有左子树或者右子树。树根的左子树的深度是树根的左子树的左子树的深度+1与树根的左子树的右子树的深度之中较大的一个。

所以对于整棵树而言,树的深度其实就是树根的左孩子深度+1,所以树在遍历树根的左子树的过程中会递归进入树根的左子树的左子树或者树根的左子树的右子树,每一部分的左子树和右子树的高度都会被不断求出来,可能返回L+1,也可能返回R,而在递归完成后,树的高度一定是返回L+1的,因为树没有“兄弟”,不可能返回R。

用递归求一棵树的高度时,不是简单的以为一棵树只有左孩子,没有右孩子,那么最后树高等于左子树的高度+1就行了。而要明白递归总是把一棵树看成更小的一棵树,而这个更小的树可能是有兄弟的,兄弟可能也有其它的兄弟,他们之中最高的高度才是最大树的左孩子的高度,这段话可能对纠结树高的返回值明明是L+1,为什么还要返回L+1或者R中的某一个有所帮助。

具体代码如下:

typedef struct node{
	ElemType data;
	struct node *child,*brother;//孩子结点与兄弟结点
}*Tree;

int Level(Tree t){
	int L,R;
	if(!t)//递归出口
		return 0;
	else{//树不为空
		L=Level(t->child);//第一个孩子的树高
		R=Level(t->brother);//兄弟树高
		if(L+1>R)
			return L+1;
		else
			return R;
	}
}

7.

需要设置一个辅助数组存放层次序列,然后依次将这个数组的元素链接起来。

#define MaxSize 15
typedef struct CSNode{
	ElemType data;
	struct CSNode *lchild,*rchild;//孩子结点与兄弟结点
}*CSTree;

void creatCSTree(CSTree t,ElemType e[],int degree[],int n){
//根据数的层次序列e和结点的度degree来构建孩子兄弟链表
//n为结点个数
	CSNode *p=new CSNode[MaxSize];
	int i,j,d,k=0;
	for(i=0;i<n;i++){
		p[i]->data=e[i];
		p[i]->lchild=p[i]->rchild=NULL;
	}
	for(i=0;i<n;i++){
		d=degree[i];
		if(d){
			k++;
			p[i]->lchild=p[k];//构建第i个结点的孩子结点
			for(j=2;j<=d;j++){//第一个结点第二个结点一定分别是根节点与根节点的第一个孩子结点,所以从第三个结点开始遍历
				p[k]->rchild=p[k++];//构建这个孩子结点的兄弟结点
			}
		}
	}
	T=p[0];
	delete [] p;
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值