树问题

一、哈弗曼树

树中所有叶子节点的带权路径之和为该树的带权路径长度和;给定n个节点和他们的权值,以它们为叶子节点构造一棵带权路径长度最小的二叉树,该树即为哈弗曼树。

若k中只剩一个节点,该节点即为构造出的哈弗曼树的根节点,所有构造得到的中间节点的权值和即为该哈弗曼树的带权路径和。

方法:利用优先队列实现堆数据结构

#include<queue>
using namespace
priority_queue<int> Q  //建立大顶堆
priority_queue<int,vector<int>,greater<int> > Q  //建立小顶堆

与堆有关的操作

Q.push(i);
int a=Q.top();
Q.pop();

例:九度1107 搬水果

#include<queue>
#include<stdio.h>
using namespace std;
struct cmp {
    bool operator()(int a, int b) {
        return a > b;
    }
};
priority_queue<int, vector<int>, cmp > Q; //构建一个小顶堆

int main(int argc, char* argv[])
{
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		while(Q.empty()==false)	Q.pop(); //要清空栈中的元素
		int i;
		for(i=0;i<n;i++)
		{
			int p;
			scanf("%d",&p);
			Q.push(p);
		}
		int sum=0;
		while(Q.size()>1) //关键啊:当只有一个根节点时,构造哈弗曼树过程中所有构造得到的中间节点(哈弗曼树的非叶子节点)即为最小带权路径和
		{
			int a,b;
			a=Q.top();
			Q.pop();
			b=Q.top();
			Q.pop();
			sum+=a+b;
			Q.push(a+b);
		}
		printf("%d\n",sum);
	}
	return 0;
}

二、二叉树

二叉树结构

struct Node{
   Node *lchild;
   Node *rchild;
   char c;
  /*其他节点信息*/
}Tree[50];

二叉树遍历方式:前序遍历、中序遍历、后序遍历

 
/*中序遍历*/ void inOrder(Node *Tree) { if(Tree->lchild!=NULL) //必须是大写的NULL inOrder(Tree->lchild); /* 对当前节点的操作,比如
   printf("%d",Tree->c);
*/
   if(Tree->lchild!=NULL)
        inOrder(Tree->lchild);
}



  
  
  


二叉树申请新结点

int loc;
Node *creat(){
	Tree[loc].lchild=Tree[loc].rchild=NULL;
	return &Tree[loc++];
}

二叉树插入新结点

Node *Insert(Node *T,int x){
	if(T==NULL)
	{
		T=creat();
		T->c=x;
		return T;
	}
	else if(x<T->c)
	{
		T->lchild=Insert(T->lchild,x);
	}
	else if(x>T->c)
	{
		T->rchild=Insert(T->rchild,x);
	}
	return T;
}

例1 九度1078  由前序和中序求后序

题目描述:

二叉树的前序、中序、后序遍历的定义:
前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历其右子树;
中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树;
后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。
给定一棵二叉树的前序遍历和中序遍历,求其后序遍历(提示:给定前序遍历与中序遍历能够唯一确定后序遍历)。

输入:

两个字符串,其长度n均小于等于26。
第一行为前序遍历,第二行为中序遍历。
二叉树中的结点名称以大写字母表示:A,B,C....最多26个结点。

输出:

输入样例可能有多组,对于每组测试样例,
输出一行,为后序遍历的字符串。

样例输入:
ABC
BAC
FDXEAG
XDEFAG
样例输出:
BCA
XEDGAF
#include<stdio.h>
#include<string.h>
//定义树结点结构体
struct Node{
	Node *lchild; //左儿子
	Node *rchild; //右儿子
	char c; //结点值
}Tree[50];
//初始化树
int loc;
Node *creat(){
	Tree[loc].lchild=Tree[loc].rchild=NULL;
	return &Tree[loc++];
}
char str1[30],str2[30];
//后续遍历
void postOrder(Node *T)
{
	if(T->lchild!=NULL)
	{
		postOrder(T->lchild);
	}
	if(T->rchild!=NULL)
	{
		postOrder(T->rchild);
	}
	printf("%c",T->c);
}
//由前序和中序还原树
Node *build(int s1,int e1,int s2,int e2)
{
	Node *ret=creat();
	ret->c=str1[s1];
	int rootIndex;
	int i;
	for(i=s2;i<=e2;i++)
	{
		if(str1[s1]==str2[i])
		{
			rootIndex=i;
			break;
		}
	}
	if(rootIndex!=s2)
	{
		ret->lchild=build(s1+1,s1+(rootIndex-s2),s2,rootIndex-1);
	}
	if(rootIndex!=e2)
	{
		ret->rchild=build(s1+(rootIndex-s2)+1,e1,rootIndex+1,e2);
	}
	return ret; //返回根节点指针
}

int main(int argc, char* argv[])
{
	while(scanf("%s",str1)!=EOF)
	{
		scanf("%s",str2);
		loc=0;
		Node *T=build(0,strlen(str1)-1,0,strlen(str2)-1);
		postOrder(T);
		printf("\n");
	}
	return 0;
}

例2 九度1176 完全二叉树

题目描述:

有一棵树,输出某一深度的所有节点,有则输出这些节点,无则输出EMPTY。该树是完全二叉树。

输入:

输入有多组数据。
每组输入一个n(1<=n<=1000),然后将树中的这n个节点依次输入,再输入一个d代表深度。

输出:

输出该树中第d层得所有节点,节点间用空格隔开,最后一个节点后没有空格。

样例输入:
4
1 2 3 4
2
样例输出:
2 3

#include<stdio.h>
int main(int argc, char* argv[])
{
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		int a[1000];
		int i;
		int sum=1,d;
		for(i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
		}
		scanf("%d",&d);
		for(i=0;i<d;i++)
		{
			sum=sum*2;
		}
		int st,en;
		st=sum/2-1;
		if(n<st)
		{
			printf("EMPTY");
		}
		en=(sum-1)>n?n:(sum-1);
		for(i=st;i<en;i++)
		{
			if(i==st)
				printf("%d",a[i]);
			else
				printf(" %d",a[i]);
		}
		printf("\n");
	}
	
	return 0;
}


例3 九度1201 二叉排序树

题目描述:

    输入一系列整数,建立二叉排序数,并进行前序,中序,后序遍历。

输入:

    输入第一行包括一个整数n(1<=n<=100)。
    接下来的一行包括n个整数。

输出:

    可能有多组测试数据,对于每组数据,将题目所给数据建立一个二叉排序树,并对二叉排序树进行前序、中序和后序遍历。
    每种遍历结果输出一行。每行最后一个数据之后有一个空格。

样例输入:
5
1 6 5 9 8
样例输出:
1 6 5 9 8 
1 5 6 8 9 
5 8 9 6 1 
提示:

输入中可能有重复元素,但是输出的二叉树遍历序列中重复元素不用输出。


#include<stdio.h>
//树结点结构
struct Node{
	Node *lchild;
	Node *rchild;
	int c;
}Tree[100];
//初始化一个新节点
int loc;
Node *creat(){
	Tree[loc].lchild=Tree[loc].rchild=NULL;
	return &Tree[loc++];
}
//前序遍历
void preOrder(Node *T)
{
	printf("%d ",T->c);
	if(T->lchild!=NULL)	preOrder(T->lchild);
	if(T->rchild!=NULL)	preOrder(T->rchild);
}
//中序遍历
void inOrder(Node *T)
{
	if(T->lchild!=NULL)	inOrder(T->lchild);
	printf("%d ",T->c);
	if(T->rchild!=NULL)	inOrder(T->rchild);
}
//后序遍历
void postOrder(Node *T)
{
	if(T->lchild!=NULL)	postOrder(T->lchild);
	if(T->rchild!=NULL)	postOrder(T->rchild);
	printf("%d ",T->c);
}
//插入树结点
Node *Insert(Node *T,int x){
	if(T==NULL)
	{
		T=creat();
		T->c=x;
		return T;
	}
	else if(x<T->c)
	{
		T->lchild=Insert(T->lchild,x);
	}
	else if(x>T->c)
	{
		T->rchild=Insert(T->rchild,x);
	}
	return T;
}
int main(int argc, char* argv[])
{
	int n;
	while(scanf("%d",&n)!=EOF)
	{
		int i;
		loc=0;
		Node *T=NULL;
		for(i=0;i<n;i++)
		{
			int x;
			scanf("%d",&x);
			T=Insert(T,x);
		}
		preOrder(T);
		printf("\n");
		inOrder(T);
		printf("\n");
		postOrder(T);
		printf("\n");
	}
	return 0;
}


例4 九度1009 二叉搜索树

题目描述:
判断两序列是否为同一二叉搜索树序列
输入:
开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束。
接下去一行是一个序列,序列长度小于10,包含(0~9)的数字,没有重复数字,根据这个序列可以构造出一颗二叉搜索树。
接下去的n行有n个序列,每个序列格式跟第一个序列一样,请判断这两个序列是否能组成同一颗二叉搜索树。
输出:

如果序列相同则输出YES,否则输出NO

样例输入:
2
567432
543267
576342
0
样例输出:
YES
NO

#include<stdio.h>
#include<string.h>
//树结点结构
struct Node{
	Node *lchild;
	Node *rchild;
	int c;
}Tree[100];
//初始化一个新节点
int loc;
Node *creat(){
	Tree[loc].lchild=Tree[loc].rchild=NULL;
	return &Tree[loc++];
}

char str1[25],str2[25];//定义原始字符串和当前需比较的字符串
int size1,size2;//定义两个字符串的长度
char *str; //定义一个字符串指针指向当前字符串
int *size;
//中序遍历
void inOrder(Node *T)
{
	if(T->lchild!=NULL)	inOrder(T->lchild);
	str[(*size)++]=T->c+'0';
	if(T->rchild!=NULL)	inOrder(T->rchild);
}
//后序遍历
void postOrder(Node *T)
{
	if(T->lchild!=NULL)	postOrder(T->lchild);
	if(T->rchild!=NULL)	postOrder(T->rchild);
	str[(*size)++]=T->c+'0';
}

//插入树结点
Node *Insert(Node *T,int x){
	if(T==NULL)
	{
		T=creat();
		T->c=x;
		return T;
	}
	else if(x<T->c)
	{
		T->lchild=Insert(T->lchild,x);
	}
	else if(x>T->c)
	{
		T->rchild=Insert(T->rchild,x);
	}
	return T;
}
int main(int argc, char* argv[])
{
	int n;
	char temp[20];
	while(scanf("%d",&n)!=EOF&&n!=0)
	{
		int i;
		//读入原始字符串,先用temp保存,再依次插入到树T中
		scanf("%s",temp);
		loc=0;
		Node *T=NULL;
		for(i=0;temp[i]!=0;i++)
		{
			T=Insert(T,temp[i]-'0');
		}
		size1=0;
		str=str1; //将str指向str1
		size=&size1; //将size指向size1的地址
		postOrder(T);
		inOrder(T);
		str1[size1]=0;//最后加'\0'结束字符串

		while(n--)
		{
			//读入要比较的字符串,先用temp保存,再依次插入到树T2中
			scanf("%s",temp);
			loc=0;
			Node *T2=NULL;
			for(i=0;temp[i]!=0;i++)
			{
				T2=Insert(T2,temp[i]-'0');
			}
			size2=0;
			str=str2;
			size=&size2;
			postOrder(T2);
			inOrder(T2);
			str2[size2]=0;
			//只有两个树的前序遍历中序遍历连接成的字符串相等才说明两个树相同
			puts(strcmp(str1,str2)==0?"YES":"NO");
		}
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值