樹問題

一、哈弗曼樹

樹中所有葉子節點的帶權路徑之和爲該樹的帶權路徑長度和;給定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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章