数据结构之树

7-1 还原二叉树 (25分)

给定一棵二叉树的先序遍历序列和中序遍历序列,要求计算该二叉树的高度。

输入格式:

输入首先给出正整数N(≤50),为树中结点总数。下面两行先后给出先序和中序遍历序列,均是长度为N的不包含重复英文字母(区别大小写)的字符串。

输出格式:

输出为一个整数,即该二叉树的高度。

输入样例:

9
ABDFGHIEC
FDHGIBEAC         

输出样例:

5

AC代码:

#include 
#include 
#include 
int n;
char pre[60],in[60];
struct TNode{
	int Data;
	struct TNode* Left;
	struct TNode* Right;
};//二叉树的结构体 
typedef struct TNode* Tree;
Tree restoreTree(char pre[],char in[],int n){//还原二叉树的函数 
	int i;
	char lpre[60],rpre[60];
	char lin[60],rin[60];
	int n1 = 0,n2 = 0;//n1记录前序遍历序列的左子树长度n2则记录前序遍历序列的右子树长度 
	int m1 = 0,m2 = 0;//m1记录中序遍历序列的左子树长度m2记录中序遍历序列右子树长度
	if(n==0){//如果长度为零说明这可子树建完了返回NULL 
		return NULL;
	}
	Tree T = (Tree)malloc(sizeof(struct TNode));
	if(T==NULL){
		return NULL;//若内存满了,返回NULL; 
	}
	T->Data = pre[0];//每一次,一定是前序遍历的第一个作为根节点,然后再去建左右子树
	//下面是关键,通过根节点把中序遍历分出左右子树,然后再根据这个分好的长度,再把前序遍历分成相同长度,依次确定根节点,递归实现
	//分中序遍历序列 
	for(i = 0; i < n; i++){
		if(i<=n1&&in[i]!=pre[0]){//中序遍历被根节点分开的左子树的点 
			lin[n1] = in[i];
			n1++;
		}
		else if(in[i]!=pre[0]){//右子树的点,注意是else if,因为这个时候i是大于n1的 
			rin[n2] = in[i];
			n2++;
		}
	}
	//分前序遍历序列,注意!这里从1开始循环,因为0号元素作为根 
	for(i = 1; i < n; i++){
		if(i<(n1+1)){
			lpre[m1] = pre[i];
			m1++;
		}
		else{
			rpre[m2] = pre[i];
			m2++;
		}
	}
	T->Left = restoreTree(lpre,lin,n1);
	T->Right = restoreTree(rpre,rin,n2);
	return T;//最后一定要return这颗树,要不然怎么算高。。。 
}
int getHight(Tree BST){//得到树的高度,已知左右树高,树高为max(左树高,右树高)+1; 
	int lh,rh;
	if(BST==NULL){
		return 0;
	}
	else {
		lh = getHight(BST->Left);
		rh = getHight(BST->Right);
	    return (lh>rh?lh:rh)+1;
	} 
	
}
int main(){
	scanf("%d",&n);
	scanf("%s",pre);
	scanf("%s",in);//输入 
	Tree BST = NULL;
	BST = restoreTree(pre,in,n);//建树 
	int hight;
	hight = getHight(BST);//求高 
	printf("%d\n",hight);//输出 
	return 0;
}

7-2 根据后序和中序遍历输出先序遍历 (25分)

本题要求根据给定的一棵二叉树的后序遍历和中序遍历结果,输出该树的先序遍历结果。

输入格式:

第一行给出正整数N(≤30),是树中结点的个数。随后两行,每行给出N个整数,分别对应后序遍历和中序遍历结果,数字间以空格分隔。题目保证输入正确对应一棵二叉树。

输出格式:

在一行中输出Preorder:以及该树的先序遍历结果。数字间有1个空格,行末不得有多余空格。

输入样例:

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

输出样例:

Preorder: 4 1 3 2 6 5 7

AC代码:

#include
using namespace std;

int in[31],post[31];

typedef struct BiTNode{
	int data;
	struct BiTNode *lchild;
	struct BiTNode *rchild;
}BiTNode,*BiTree;

BiTree Build(int *in,int *post,int n)
{//第一个参数是中序序列的起始位置,第二个参数是后序序列的起始位置,n是长度 
	if(n <= 0)//如果长度小于等于0,直接返回即可 
		return NULL;
	int *p = in;//定义一个指向in的指针 
	while(p){//找到根节点在中序中的位置 
		if(*p == *(post + n - 1))
			break;
		p++;
	}
	BiTree T = new BiTNode;
	T->data = *p;
	int len = p - in;
	T->lchild = Build(in,post,len);
	T->rchild = Build(p + 1,post + len,n - len - 1);
	return T; 
}

void PreOrder(BiTree T){
	if(T){
		printf(" %d",T->data);
		PreOrder(T->lchild);
		PreOrder(T->rchild);
	}
	return;
}

int main(){
	int n;
	BiTree T;//只需要定义,不需要赋值->[BiTree T = new BiTNode;] 
	scanf("%d",&n);
	for(int i = 0;i < n;i++)
		scanf("%d",&post[i]);
	for(int i = 0;i < n;i++)
		scanf("%d",&in[i]);
	T = Build(in,post,n);
	printf("Preorder:");
	PreOrder(T);
	return 0;
}

7-3 愿天下有情人都是失散多年的兄妹 (25分)

呵呵。大家都知道五服以内不得通婚,即两个人最近的共同祖先如果在五代以内(即本人、父母、祖父母、曾祖父母、高祖父母)则不可通婚。本题就请你帮助一对有情人判断一下,他们究竟是否可以成婚?

输入格式:

输入第一行给出一个正整数N(2 ≤ N ≤104),随后N行,每行按以下格式给出一个人的信息:

本人ID 性别 父亲ID 母亲ID    

其中ID是5位数字,每人不同;性别M代表男性、F代表女性。如果某人的父亲或母亲已经不可考,则相应的ID位置上标记为-1

接下来给出一个正整数K,随后K行,每行给出一对有情人的ID,其间以空格分隔。

注意:题目保证两个人是同辈,每人只有一个性别,并且血缘关系网中没有乱伦或隔辈成婚的情况。

输出格式:

对每一对有情人,判断他们的关系是否可以通婚:如果两人是同性,输出Never Mind;如果是异性并且关系出了五服,输出Yes;如果异性关系未出五服,输出No

输入样例:

24
00001 M 01111 -1
00002 F 02222 03333
00003 M 02222 03333
00004 F 04444 03333
00005 M 04444 05555
00006 F 04444 05555
00007 F 06666 07777
00008 M 06666 07777
00009 M 00001 00002
00010 M 00003 00006
00011 F 00005 00007
00012 F 00008 08888
00013 F 00009 00011
00014 M 00010 09999
00015 M 00010 09999
00016 M 10000 00012
00017 F -1 00012
00018 F 11000 00013
00019 F 11100 00018
00020 F 00015 11110
00021 M 11100 00020
00022 M 00016 -1
00023 M 10012 00017
00024 M 00022 10013
9
00021 00024
00019 00024
00011 00012
00022 00018
00001 00004
00013 00016
00017 00015
00019 00021
00010 00011

输出样例:

Never Mind
Yes
Never Mind
No
Yes
No
Yes
No
No

AC代码:

#include
#include
using namespace std;
typedef struct Node{
	char sex;
	int fId;
	int mId;
}Node; 
Node p[100001];
int flag,visited[100001];
void f(int a,int sum){
	if(sum > 5 || a == -1 || a == 0)//如果代数大于5代,或者a==-1,说明都不在人世,
									//或者a==0说明没有被赋值,直接返回 
		return;
	visited[a]++;//记录被访问的次数 
	if(visited[a] >= 2)//如果五代以内的同一个人被访问超过2次,说明在五代之内有共同的祖先 
		flag = 0;
	f(p[a].fId,sum + 1);//递归调用,a的父亲,代数+1 
	f(p[a].mId,sum + 1);//递归调用,a的母亲,代数+1 
	return;
}
int main(){
	int n,selfId,fId,mId,k,a,b;
	char c;
	scanf("%d",&n);
	for(int i = 0;i < n;i++){
			scanf("%d %c%d%d",&selfId,&c,&fId,&mId);
			p[selfId].sex = c;//记录自己的性别 
			p[selfId].fId = fId;//记录父亲的Id 
			p[selfId].mId = mId;
			p[fId].sex = 'M';//记录自己父亲的性别,否则,可能会漏掉 
			p[mId].sex = 'F';//记录自己母亲的性别,否则,可能会漏掉
	}
	scanf("%d",&k);
	while(k--){
		flag = 1;//标记变量,默认为1 
		memset(visited,0,sizeof(visited));//每循环一次,必须进行一次初始化 
		scanf("%d%d",&a,&b);
		if(p[a].sex == p[b].sex){			
			printf("Never Mind\n");	
			continue;
		}	
		f(a,1);//以a为起点,代数为一代,进行寻找
		f(b,1);//以a为起点,代数为一代,进行寻找
		if(flag)
			printf("Yes\n");
		else
			printf("No\n");
	}
	return 0;
}

7-4 列出叶结点 (25分)

对于给定的二叉树,本题要求你按从上到下、从左到右的顺序输出其所有叶节点。

输入格式:

首先第一行给出一个正整数 N(≤10),为树中结点总数。树中的结点从 0 到 N−1 编号。随后 N 行,每行给出一个对应结点左右孩子的编号。如果某个孩子不存在,则在对应位置给出 “-”。编号间以 1 个空格分隔。

输出格式:

在一行中按规定顺序输出叶节点的编号。编号间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

8
1 -
- -
0 -
2 7
- -
- -
5 -
4 6 

输出样例:

4 1 5

AC代码:

#include
#include
using namespace std;

struct fun{
	char l,r;
};
fun s[100];
vector<int>ve[100];

void funs(int t,int cur)
{
	if(s[t].l=='-'&&s[t].r=='-')
	{
		ve[cur].push_back(t);//cur++了 
		return ;
	}
	if(s[t].l!='-')
	funs(s[t].l-'0',cur+1);
	if(s[t].r!='-')
	funs(s[t].r-'0',cur+1);
}

int main()
{
	int root[100]={0};
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		cin>>s[i].l>>s[i].r;
		root[s[i].l-'0']=1;
		root[s[i].r-'0']=1;
	}
  int r;
	for(int i=0;i<n;i++)
	{
		if(root[i]==0)//
		{
			r=i;
			break;
		}
	}
  funs(r,1);
	int flag=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<ve[i].size();j++)
		{
			if(flag==0)
				flag=1;
			else
				cout<<" ";
			cout<<ve[i][j];
		}
	} 
}

7-5 二叉树的遍历 (10分)

根据输入构造二叉树,输出该二叉树的先序序列。二叉树共有N个节点,节点编号是1到N。约定1号节点是根节点。

输入格式:

第一行输入整数N。 接下来有N行,依次给出1到N节点的左孩子和右孩子。对于这N行中的每一行,有两个整数。第i(i=1, 2, …, N)行中,第一个整数指出左孩子的编号,第二个整数指出右孩子的编号。如果整数值为0,表示没有左孩子或右孩子。

输出格式:

输出一行,内容是二叉树的先序序列。节点编号之间用空格隔开,行末有1个空格。

输入样例:

6
2 5
3 4
0 0
0 0
0 6
0 0

输出样例:

1 2 3 4 5 6 

AC代码:

#include 
using namespace std;

struct Node 
{
	int lChild;  
	int rChild;  
};

void Pre(Node nodes[], int root)
{
	if (root == 0)
		return;	//结束
	cout << root << " ";
	Pre(nodes, nodes[root].lChild);
	Pre(nodes, nodes[root].rChild);
}

int main()
{
	int N;       
	cin >> N;	
	Node nodes[N + 1]; 	//注意一下数据的类型
	for (int i = 1; i <= N; i++)
	cin >> nodes[i].lChild >> nodes[i].rChild;
	//调用递归函数
	Pre(nodes, 1); 
}

7-6 哈夫曼编码 (30分)

给定一段文字,如果我们统计出字母出现的频率,是可以根据哈夫曼算法给出一套编码,使得用此编码压缩原文可以得到最短的编码总长。然而哈夫曼编码并不是唯一的。例如对字符串"aaaxuaxz",容易得到字母 ‘a’、‘x’、‘u’、‘z’ 的出现频率对应为 4、2、1、1。我们可以设计编码 {‘a’=0, ‘x’=10, ‘u’=110, ‘z’=111},也可以用另一套 {‘a’=1, ‘x’=01, ‘u’=001, ‘z’=000},还可以用 {‘a’=0, ‘x’=11, ‘u’=100, ‘z’=101},三套编码都可以把原文压缩到 14 个字节。但是 {‘a’=0, ‘x’=01, ‘u’=011, ‘z’=001} 就不是哈夫曼编码,因为用这套编码压缩得到 00001011001001 后,解码的结果不唯一,“aaaxuaxz” 和 “aazuaxax” 都可以对应解码的结果。本题就请你判断任一套编码是否哈夫曼编码。

输入格式:

首先第一行给出一个正整数 N(2≤N≤63),随后第二行给出 N 个不重复的字符及其出现频率,格式如下:

c[1] f[1] c[2] f[2] ... c[N] f[N]

其中c[i]是集合{‘0’ - ‘9’, ‘a’ - ‘z’, ‘A’ - ‘Z’, ‘_’}中的字符;f[i]c[i]的出现频率,为不超过 1000 的整数。再下一行给出一个正整数 M(≤1000),随后是 M 套待检的编码。每套编码占 N 行,格式为:

c[i] code[i]

其中c[i]是第i个字符;code[i]是不超过63个’0’和’1’的非空字符串。

输出格式:

对每套待检编码,如果是正确的哈夫曼编码,就在一行中输出"Yes",否则输出"No"。

注意:最优编码并不一定通过哈夫曼算法得到。任何能压缩到最优长度的前缀编码都应被判为正确。

输入样例:

7
A 1 B 1 C 1 D 3 E 3 F 6 G 6
4
A 00000
B 00001
C 0001
D 001
E 01
F 10
G 11
A 01010
B 01011
C 0100
D 011
E 10
F 11
G 00
A 000
B 001
C 010
D 011
E 100
F 101
G 110
A 00000
B 00001
C 0001
D 001
E 00
F 10
G 11 

输出样例:

Yes
Yes
No
No

AC代码:

#include
#include
#include
#include
using namespace std;
//代表小顶堆的优先队列
priority_queue<int ,vector<int>,greater<int> > q;
int main(void){
	int n;
	char s[66];
	int p[66];
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		getchar();
		scanf("%c%d",&s[i],&p[i]);
		q.push(p[i]);
	}
	int ans=0;
	//ans即为哈弗曼树带权路径长度和,也就是所对应的哈夫曼编码的长度
	while(q.size()>1){
		int x=q.top();
		q.pop();
		int y=q.top();
		q.pop();
		q.push(x+y);
		ans+=x+y;
	}
	int m;
	scanf("%d",&m);
	for(int i=0;i<m;i++){
		char ss[66][66];
		int sum=0;
		for(int j=0;j<n;j++){
			getchar();
			char c;
			scanf("%c %s",&c,ss[j]);
			sum+=strlen(ss[j])*p[j];
		}
		if(sum!=ans)printf("No\n");
		else{
			int flag=0;
			for(int j=0;j<n;j++){
				int x=strlen(ss[j]);
				for(int l=0;l<n;l++){
					if(j!=l){
						if(strstr(ss[l],ss[j])==&ss[l][0]){
						//查找字符串,如果找到了并且是前缀,就标记为No了
							flag=1;
							break;
						}
					}
				}
				if(flag==1)break;
			}
			if(flag==1)printf("No\n");
			else printf("Yes\n");
		}
	}
	return 0;
}

你可能感兴趣的:(题目解答)