【PAT】第九章 数据结构专题 - 树(例题)

9.2 二叉树的遍历

【例】A1020 Tree Traversals (25 分)

后序 + 中序 -> 层序输出

#include <cstdio>
#include <queue>
#include <vector>
using namespace std;

int n,post[40],in[40];
struct node{
	int data;
	node* lChild,*rChild;
	node(){lChild=rChild=NULL;}
};
vector<node*> ans;

node* create(int poL,int poR,int inL,int inR)
{
	if(poL>poR) return NULL;
	node* root=new node; 	// #include <iostream> + using namespace std;
	root->data=post[poR];
	int k;
	for(k=inL;k<=inR;k++)
		if(in[k]==post[poR])
			break;
	int numLeft=k-inL;
	root->lChild=create(poL,poL+numLeft-1,inL,k-1);
	root->rChild=create(poL+numLeft,poR-1,k+1,inR); 
	
	return root;
}
void layerOrder(node* root)
{
	queue<node*> q;
	q.push(root);
	while(!q.empty())
	{
		node* front=q.front();
		q.pop();
		ans.push_back(front);
		if(front->lChild!=NULL)
			q.push(front->lChild);
		if(front->rChild!=NULL)
			q.push(front->rChild);
	}
}
int main()
{
	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]);
	node* root=create(0,n-1,0,n-1);
	layerOrder(root);
	for(int i=0;i<n;i++)
	{
		if(i==0) printf("%d",ans[i]->data);
		else printf(" %d",ans[i]->data);
	}
	return 0;
}

【例】A1086 Tree Traversals Again (25 分)

ATTENTION

  • pop的顺序是中序遍历
  • push的顺序是先序遍历
  • 所以就是已知先序遍历个中序遍历,求后序遍历!
#include <iostream>
#include <string>
#include <stack>
using namespace std;

int n,pre[40],in[40];
struct node{
	int data;
	node* lChild,*rChild;
}; 

node* create(int preL,int preR,int inL,int inR)
{
	if(preL>preR) return NULL;
	node* root=new node;
	root->data=pre[preL];
	int k;
	for(k=1;k<=n;k++)
		if(pre[preL]==in[k])
			break;
	int numLeft=k-inL;
	root->lChild=create(preL+1,preL+numLeft,inL,k-1);
	root->rChild=create(preL+numLeft+1,preR,k+1,inR);
	
	return root;
}
void postOrder(node* root)
{
	if(root==NULL) return;
	postOrder(root->lChild);
	postOrder(root->rChild);
	if(root->data==pre[1]) cout<<root->data;
	else cout<<root->data<<" ";
}
int main()
{
	cin>>n;
	int d,preIdx=1,inIdx=1;
	stack<int> st;
	for(int i=1;i<=2*n;i++)
	{
		string op;
		cin>>op;
		if(op=="Push")
		{
			cin>>d;
			st.push(d);
			pre[preIdx++]=d;
		}
		else
		{
			d=st.top();
			st.pop();
			in[inIdx++]=d;
		}
	}
	//1-n pre in -> post
	node* root=NULL;
	root=create(1,n,1,n);
	postOrder(root);
}


直接模拟
ATTENTION

  • push的时候,记录push的前一个结点,那一定是它的父亲。
  • pop的时候,先记录top(),再pop,因为pop的一定是父亲结点。直到下一个push,此时push的一定是top()的孩子。
#include <iostream>
#include <string>
#include <vector>
using namespace std;

struct Node{
	int lChild,rChild;
	Node(){lChild=rChild=-1;}
}node[40];

int n,root;
vector<int> seq;

void postOrder(int r)
{
	if(r==-1) return;
	postOrder(node[r].lChild);
	postOrder(node[r].rChild);
	if(root==r) cout<<r;
	else cout<<r<<" ";
}
int main()
{
	cin>>n;
	string op;
	int d,father=-1;
	for(int i=1;i<=2*n;i++)
	{
		cin>>op;
		if(op=="Push")
		{
			cin>>d;
			if(i==1) root=d;
			seq.push_back(d);
			if(father!=-1)
			{
				if(node[father].lChild==-1)
					node[father].lChild=d;
				else
					node[father].rChild=d;
			}
			father=d;
		}
		else
		{
			father=seq[seq.size()-1];
			seq.pop_back();
		}
	}
	postOrder(root);
	return 0;
}

【例】A1102 Invert a Binary Tree (25 分)

invert vt. 使…转化;使…颠倒;使…反转;使…前后倒置

ATTENTION

  • 反转全靠样例猜orz
  • 需要自己找根节点,这里在数组pre[]中记录了每个结点的父亲,这样随便选一个有父亲的结点向上递归到没有父亲的结点,就是根了。笔记中的方法是,不是任何结点的孩子的结点就是根
  •   \:   \: string只能被cin读入   \:   \:
  •   \:   \: 反转的正确做法:在后序遍历中交换左右孩子   \:
void postOrder(int root)
{
	if(root==-1)
		return;
	postOrder(node[root].lChild);
	postOrder(node[root].rChild);
	swap(node[root].lChild,node[root].rChild) 	//头文件 #include<iostream> 
} 
#include <cstdio>
#include <iostream>
#include <queue>
#include <string>
#include <algorithm>
using namespace std;


int n,root,idx=1;
int pre[20];
int node[20][2];

void levelOrder(int t)
{
	queue<int> q;
	q.push(t);
	int cnt=1;
	while(!q.empty())
	{
		int f=q.front();
		if(cnt==1)
			printf("%d",f);
		else
			printf(" %d",f);
		cnt++;
		q.pop();
		if(node[f][1]!=-1) q.push(node[f][1]);
		if(node[f][0]!=-1) q.push(node[f][0]);
	}
	printf("\n");
}
int findRoot(int t)
{
	if(pre[t]==-1)
		return t;
	findRoot(pre[t]);
}
void inOrder(int t)
{
	if(node[t][1]!=-1)
		inOrder(node[t][1]);
	if(idx==1)
		printf("%d",t);
	else
		printf(" %d",t);
	idx++;
	if(node[t][0]!=-1)
		inOrder(node[t][0]);
}
int main()
{
	scanf("%d",&n);
	fill(pre,pre+n,-1);
	for(int i=0;i<n;i++)
	{
		string s1,s2;
	//	scanf("%s %s",s1,s2);
	cin>>s1>>s2;
		//printf("*%s %s*",s1.c_str(),s2.c_str());
		if(s1[0]!='-')
		{
			node[i][0]=(int)(s1[0]-'0');
			pre[s1[0]-'0']=i;
		}
		else
			node[i][0]=-1;
		if(s2[0]!='-')
		{
			node[i][1]=(int)(s2[0]-'0');
			pre[s2[0]-'0']=i;
		}
		else
			node[i][1]=-1;
	}
	for(int i=0;i<n;i++)
	{
		if(pre[i]!=-1)
			root=findRoot(i);
	}
	levelOrder(root);
	inOrder(root);
	return 0;	
}

【例】*A1151 LCA in a Binary Tree (30 分)

ATTENTION

  • 不建树,建树疯狂超时。利用先序和中序遍历,判断结点u和v相对于根的位置。如果两者分别在左右子树,则这时候的根为最近公共祖先。如果u和v在同一子树中,则只递归该部分子树,继续寻找最近公共祖先。
  • 这道题有几个点特别卡时间。法1是取巧过的,由于结点的keyint范围的,多试了几次试出了范围…
  • 理论上讲,pos应该用map实现。但只用map也会超时,需要在多个地方注意减少操作
#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;

int m,n,u,v,pre[10010],in[10010];
map<int,int> mp,pos;

void create(int preL,int inL,int inR)
{
	if(inL>inR) return ;
	int a=pos[u],b=pos[v];
	int k=pos[pre[preL]];
	if(a==k)
	{
		printf("%d is an ancestor of %d.\n",u,v);
		return ;
	}
	if(b==k)
	{
		printf("%d is an ancestor of %d.\n",v,u);
		return ;
	}	
	if((a<k&&b>k)||(a>k&&b<k))
	{
		printf("LCA of %d and %d is %d.\n",u,v,pre[preL]);		
		return;
	}
	if(a<k&&b<k)
		create(preL+1,inL,k-1);
	if(a>k&&b>k)
		create(preL+k-inL+1,k+1,inR);
}
int main()
{
	scanf("%d %d",&m,&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&in[i]);
		pos[in[i]]=i;
	}
	for(int i=1;i<=n;i++)
		scanf("%d",&pre[i]);
	for(int i=0;i<m;i++)
	{
		scanf("%d %d",&u,&v);
		if(pos[u]==0&&pos[v]==0)
			printf("ERROR: %d and %d are not found.\n",u,v);
		else if(pos[u]==0)
			printf("ERROR: %d is not found.\n",u);
		else if(pos[v]==0)
			printf("ERROR: %d is not found.\n",v);
		else
			create(1,1,n);	
	}
	return 0;
}

取巧版本

//取巧 
#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;

int m,n,u,v,pre[10010],in[10010],pos[1000010];
map<int,int> mp;

void create(int preL,int preR,int inL,int inR)
{
	if(preL>preR) return ;
	int k=pos[pre[preL]];
	int numLeft=k-inL;
	if(pos[u]==k)
	{
		printf("%d is an ancestor of %d.\n",u,v);
		return ;
	}
	if(pos[v]==k)
	{
		printf("%d is an ancestor of %d.\n",v,u);
		return ;
	}	
	if((pos[u]<k&&pos[v]>k)||(pos[u]>k&&pos[v]<k))
	{
		printf("LCA of %d and %d is %d.\n",u,v,pre[preL]);		
		return;
	}
	if(pos[u]<k&&pos[v]<k)
		create(preL+1,preL+numLeft,inL,k-1);
	if(pos[u]>k&&pos[v]>k)
		create(preL+numLeft+1,preR,k+1,inR);
}
int main()
{
	scanf("%d %d",&m,&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&in[i]);
		pos[in[i]]=i;
		mp[in[i]]=1;
	}
	for(int i=1;i<=n;i++)
		scanf("%d",&pre[i]);
	for(int i=0;i<m;i++)
	{
		scanf("%d %d",&u,&v);
		if(mp[u]==0&&mp[v]==0)
			printf("ERROR: %d and %d are not found.\n",u,v);
		else if(mp[u]==0)
			printf("ERROR: %d is not found.\n",u);
		else if(mp[v]==0)
			printf("ERROR: %d is not found.\n",v);
		else
			create(1,n,1,n);	
	}
	return 0;
}

【例】A1127 ZigZagging on a Tree (30 分)

ATTENTION

  • 每次这种遍历判断,当前与前一个比较再输出的时候,都要记得在循环结束后,需要输出最后一组!!!
#include <cstdio>

int n,m;
int de[510]={0};
int g[510][510]={0};
bool vis[510]={false};

void dfs(int u)
{
	vis[u]=true;
	for(int v=1;v<=n;v++)
	{
		if(vis[v]==false&&g[u][v]==1)
			dfs(v);
	}
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=0;i<m;i++)
	{
		int c1,c2;
		scanf("%d %d",&c1,&c2);
		de[c1]++;
		de[c2]++;
		g[c1][c2]=g[c2][c1]=1;
	}
	int even=0,odd=0;
	for(int i=1;i<=n;i++)
	{
		if(de[i]%2==0)	even++;
		else odd++;
		if(i==1) printf("%d",de[i]);
		else printf(" %d",de[i]);
	}
	bool flag=true;
	dfs(1);
	for(int i=1;i<=n;i++)
	{
		if(vis[i]==false)
		{
			flag=false;
			break;
		}
	}
	if(flag&&even==n) printf("\nEulerian");
	else if(flag&&even==n-2&&odd==2) printf("\nSemi-Eulerian");
	else printf("\nNon-Eulerian");
	return 0;
}

【例】A1110 Complete Binary Tree (25 分)

ATTENTION

  • 笑死。这道题debug了好久。第二天才发现不能用char存储结点序号,结点可能是两位数。白瞎9分。
#include <cstdio>
#include <string>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;

struct Node{
	int lChild,rChild,idx;
	Node(){lChild=rChild=-1;}
}node[100];
int n,layer[100],cnt=1;
bool vis[100]={false};
bool judge[100]={false};

void layerOrder(int root)
{
	queue<int> q;
	q.push(root);
	fill(layer,layer+100,-1);

	node[root].idx=1;
	while(!q.empty())
	{
		int u=q.front();
		layer[cnt++]=u;		
		q.pop();
		judge[node[u].idx]=true;
		if(node[u].lChild!=-1)
		{
			q.push(node[u].lChild);
			node[node[u].lChild].idx=node[u].idx*2;
		}
		if(node[u].rChild!=-1)
		{
			q.push(node[u].rChild);
			node[node[u].rChild].idx=node[u].idx*2+1;
		}
	}
}
int main()
{
	scanf("%d",&n);
	if(n==0)
	{
		printf("NO 0");
		return 0;
	}
	for(int i=0;i<n;i++)
	{
		//getchar(); 	//。。。。。。 
		//char l,r;
		string l,r;
		cin>>l>>r;
	//	scanf("%c %c",&l,&r);
		if(l!="-")
		{
			int tmp=atoi(l.c_str());
			node[i].lChild=tmp;
			vis[tmp]=true;
		}
		if(r!="-")
		{
			int tmp=atoi(r.c_str());
			node[i].rChild=tmp;
			vis[tmp]=true;
		}
	}
	int root=0;
	while(vis[root]==true) root++; 
	layerOrder(root);
	for(int i=1;i<=n;i++)
	{
		if(judge[i]==false)
		{
			printf("NO %d\n",root);
			return 0;
		}	
	}
	printf("YES %d\n",layer[n]);
	return 0;
}

9.3 树的遍历

【例】A1004 Counting Leaves (30 分)

hierarchy n. 层级;等级制度
pedigree n.家谱
fix v.固定
For the sake of simplicity 简单起见

#include <cstdio>
#include <vector>
#include <queue>
using namespace std; 	//使用STL要加这句话 
 
const int maxn=200;
int n,m;
int layer[maxn]={};
int maxLayer=-1;

struct node{
	int layer;
	vector<int> child;
	node()
	{
		layer=-1;
	}
}tNode[maxn];

void layerOrder(int root)
{
	queue<int> Q;
	Q.push(root);
	tNode[root].layer=0;
	maxLayer=0;
	while(!Q.empty())
	{
		int front=Q.front();
		Q.pop();
		if(tNode[front].child.size()==0) 
			layer[tNode[front].layer]+=1;
		for(int i=0;i<tNode[front].child.size();i++)
		{
			int child=tNode[front].child[i];
			Q.push(child);
			tNode[child].layer=tNode[front].layer+1;
			if(tNode[child].layer>maxLayer)
				maxLayer=tNode[child].layer;
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++)
	{
		int id,k;
		scanf("%d%d",&id,&k);
		for(int j=0;j<k;j++)
		{
			int cidx;
			scanf("%d",&cidx);
			tNode[id].child.push_back(cidx);
		}
	}
	layerOrder(1);

	for(int i=0;i<=maxLayer;i++)
	{
		printf("%d",layer[i]);
		if(i!=maxLayer)
			printf(" ");
	}
	return 0;
}

【例】A1053 Path of Equal Weight (30 分)

ATTENTION

  • vector默认的大小比较方式与题目中的类似,所以直接比较vector即可。但在不知道这一点前,vector的比较很是麻烦。
  • 一开始放在ans中的是结点的下标,所以比较大小一直有问题orz。
  • 这道题还可以用DFS+剪枝的思想做。这样可以免去最后的比较大小。
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

int n,m,s,k;
struct Node{
	int w;
	vector<int> child;
}node[110];
vector<int> path;
vector<vector<int> > ans;

bool cmp(vector<int> a,vector<int> b)
{
	return a>b;
}
void dfs(int root)
{
	path.push_back(node[root].w);
	if(node[root].child.size()==0) 	//叶子结点
	{
		int sum=0;
		for(int i=0;i<path.size();i++)
			sum+=path[i];
		if(sum==s)
			ans.push_back(path);
		//path.pop_back();
		return;
	} 
	for(int i=0;i<node[root].child.size();i++)
	{
		dfs(node[root].child[i]);
		path.pop_back();
	}
	
}
int main()
{
	scanf("%d %d %d",&n,&m,&s);
	for(int i=0;i<n;i++)
		scanf("%d",&node[i].w);
	for(int i=0;i<m;i++)
	{
		int v,tmp;
		scanf("%d %d",&v,&k);
		for(int j=0;j<k;j++)
		{
			scanf("%d",&tmp);
			node[v].child.push_back(tmp);
		}
	}
	dfs(0);
	sort(ans.begin(),ans.end(),cmp);
	for(int i=0;i<ans.size();i++)
	{
		for(int j=0;j<ans[i].size();j++)
		{
			if(j==0) printf("%d",ans[i][j]);
			else printf(" %d",ans[i][j]);
		}
		printf("\n");
	}
	return 0;
}

【例】A1094 The Largest Generation (25 分)

ATTENTION

  • 因为根节点的层数为1而非0,所以输出的层号的初始值应该为1,而不是0。
  • DFS求解层号
void dfs(int idx,int level)
{
	cnt[level]++;
	for(int i=0;i<node[idx].size();i++)
	{
		dfs(node[idx][i],level+1);
	}
}
#include <cstdio>
#include <queue>
#include <cmath> 
using namespace std;

int n,m,k,maxLevel=0;
vector<int> node[110];
int level[110]={0},cnt[110]={0};


void levelOrder()
{
	queue<int> q;
	q.push(1);
	while(!q.empty())
	{
		int cur=q.front();
		q.pop();
		for(int i=0;i<node[cur].size();i++)
		{
			level[node[cur][i]]=level[cur]+1;
			cnt[level[cur]+1]++;
			q.push(node[cur][i]);
			maxLevel=max(maxLevel,level[node[cur][i]]); 	//头文件? 
		}
	}
}

int main()
{
	level[1]=1;cnt[1]=1;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int idx;
		scanf("%d %d",&idx,&k);
		for(int j=1;j<=k;j++)
		{
			int tmp;
			scanf("%d",&tmp);
			node[idx].push_back(tmp);
		}
	}
	levelOrder();
	int idx=1,max=1; 	//如果只有root 答案是 0 1 
	for(int i=1;i<=maxLevel;i++)
	{
		if(max<cnt[i])
		{
			idx=i;
			max=cnt[i];
		}
	}
	printf("%d %d",max,idx);
	return 0; 
}

【例】A1090 Highest Price in Supply Chain (25 分)

ATTENTION

  • 又是死在英语上的一天呢,棒棒的 😃
  • 一棵树,求树高和最高层叶子数量。
#include <cstdio>
#include <vector>
using namespace std;

int n,lev[100010]={0},maxLev=0;;
double p,r;
vector<int> node[100010];

void dfs(int t,int l)
{
	lev[t]=l;
	if(maxLev<l) maxLev=l;
	for(int i=0;i<node[t].size();i++)
	{
		dfs(node[t][i],l+1);
	}
}
int main()
{
	scanf("%d %lf %lf",&n,&p,&r);
	for(int i=0;i<n;i++) 	//所有结点序号+1 
	{
		int tmp;
		scanf("%d",&tmp);
		node[tmp+1].push_back(i+1);
	}
	dfs(0,0);
	for(int i=1;i<maxLev;i++)
	{
		p=(1+0.01*r)*p;
	}
	printf("%.2f ",p);
	int cnt=0;
	for(int i=0;i<=n;i++)
	{
		if(lev[i]==maxLev)
			cnt++;
	}
	printf("%d",cnt);
	return 0;
}

9.4 二叉查找树BST

【例】A1043 Is It a Binary Search Tree (25 分)

ATTENTION

  • 这道题的关键在于:按先序遍历 (或后序遍历) 的顺序插入可以得到一棵唯一地二叉搜索树。因为二叉查找树左<根<右,所以在插入时,根据二叉查找树的有序性,可以自动地区分左子树和右子树,从而得到一棵唯一地二叉树。
  • 怎么回事儿,这个YES和NO竟然是全大写的,爆零太可怕了orz
  • 递归函数!复制粘贴修改时!一定要好好修改!别漏掉任何一个地方的递归!!!
#include <cstdio>
#include <vector>
using namespace std;

int n;
vector<int> origin,mir;
struct node{
	int data;
	node* lChild,*rChild;
	node(int x)
	{
		lChild=rChild=NULL;
		data=x;
	}
	node()
	{
		lChild=rChild=NULL;
	}
};

void insert(node* &root,int x)
{
	if(root==NULL)
	{
		root=new node(x);
		return ;
	}
	if(x<root->data)
		insert(root->lChild,x);
	else
		insert(root->rChild,x);
}
void mirInsert(node* &root,int x)
{
	if(root==NULL)
	{
		root=new node(x);
		return ;
	}
	if(x>=root->data)
		mirInsert(root->lChild,x);
	else
		mirInsert(root->rChild,x);
}
void preOrder(node* root,vector<int> &path)
{
	if(root==NULL) return ;
	path.push_back(root->data);
	preOrder(root->lChild,path);
	preOrder(root->rChild,path);
}
void postOrder(node* root,vector<int> &path)
{
	if(root==NULL) return;
	postOrder(root->lChild,path);
	postOrder(root->rChild,path);
	path.push_back(root->data);
}
int main()
{
	node* root=NULL,*mirRoot=NULL;
	scanf("%d",&n);
	vector<int> num(n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&num[i]);
		insert(root,num[i]);
		mirInsert(mirRoot,num[i]);
	}
	preOrder(root,origin);
	preOrder(mirRoot,mir);
	if(num==origin)
	{
		printf("YES\n");
		origin.clear();
		postOrder(root,origin);
		printf("%d",origin[0]);
		for(int i=1;i<n;i++)
			printf(" %d",origin[i]);
		return 0;
	}
	if(num==mir)
	{
		printf("YES\n");
		mir.clear();
		postOrder(mirRoot,mir);
		printf("%d",mir[0]);
		for(int i=1;i<n;i++)
			printf(" %d",mir[i]);
	}
	else
		printf("NO\n");
	return 0;	
}
#include <cstdio>
#include <vector>
using namespace std;

int n;
vector<int> origin,mir;
struct node{
	int data;
	node* lChild,*rChild;
	node(int x)
	{
		lChild=rChild=NULL;
		data=x;
	}
	node()
	{
		lChild=rChild=NULL;
	}
};

void insert(node* &root,int x)
{
	if(root==NULL)
	{
		root=new node(x);
		return ;
	}
	if(x<root->data)
		insert(root->lChild,x);
	else
		insert(root->rChild,x);
}
void mirInsert(node* &root,int x)
{
	if(root==NULL)
	{
		root=new node(x);
		return ;
	}
	if(x>=root->data)
		mirInsert(root->lChild,x);
	else
		mirInsert(root->rChild,x);
}
void preOrder(node* root,vector<int> &path)
{
	if(root==NULL) return ;
	path.push_back(root->data);
	preOrder(root->lChild,path);
	preOrder(root->rChild,path);
}
void mirpreOrder(node* root,vector<int> &path)
{
	if(root==NULL) return ;
	path.push_back(root->data);
	mirpreOrder(root->rChild,path);
	mirpreOrder(root->lChild,path);
}
void postOrder(node* root,vector<int> &path)
{
	if(root==NULL) return;
	postOrder(root->lChild,path);
	postOrder(root->rChild,path);
	path.push_back(root->data);
}
void mirpostOrder(node* root,vector<int> &path)
{
	if(root==NULL) return;
	mirpostOrder(root->rChild,path);
	mirpostOrder(root->lChild,path);
	path.push_back(root->data);
}
int main()
{
	node* root=NULL,*mirRoot=NULL;
	scanf("%d",&n);
	vector<int> num(n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&num[i]);
		insert(root,num[i]);
		//mirInsert(mirRoot,num[i]);
	}
	preOrder(root,origin);
	mirpreOrder(root,mir);
	if(num==origin)
	{
		printf("YES\n");
		origin.clear();
		postOrder(root,origin);
		printf("%d",origin[0]);
		for(int i=1;i<n;i++)
			printf(" %d",origin[i]);
		return 0;
	}
	if(num==mir)
	{
		printf("YES\n");
		mir.clear();
		mirpostOrder(root,mir);
		printf("%d",mir[0]);
		for(int i=1;i<n;i++)
			printf(" %d",mir[i]);
	}
	else
		printf("NO\n");
	return 0;	
}

【例】A1064 Complete Binary Search Tree (30 分)

ATTENTION

  • 二叉搜索树的中序遍历是有序的。
  • 以数组存储完全二叉树,其数组本身就是该完全二叉树的层次遍历。
#include <cstdio>
#include <algorithm>
using namespace std;

int n,idx=1;
int num[1010],cbt[1010];

void inOrder(int root) 	//中序遍历的是cbt数组的下标 
{
	if(root>n) return ;
	inOrder(root*2);
	cbt[root]=num[idx++];
	inOrder(root*2+1);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&num[i]);
	sort(num+1,num+1+n);
	inOrder(1);
	printf("%d",cbt[1]);
	for(int i=2;i<=n;i++)
		printf(" %d",cbt[i]);
	
	return 0;
}


【例】A1099 Build A Binary Search Tree (30 分)

property n.属性
traversal n.遍历

#include <cstdio>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;

struct Node{
	int left,right;
	Node()
	{
		left=-1;right=-1;
	}
}node[110];

vector<int> num,turn;	 //输入的数,前序遍历 
int ans[110];
int n;

void preOrder(int t)
{
	if(node[t].left!=-1)
		preOrder(node[t].left);
	turn.push_back(t);
	if(node[t].right!=-1)
		preOrder(node[t].right);
}
void levelOrder(int t)
{
	queue<int> q;
	q.push(0);
	int idx=0;
	while(!q.empty())
	{
		int cur=q.front();
		q.pop();
		printf("%d",ans[cur]);
		if(idx!=n-1)
			printf(" ");
		idx++;
		if(node[cur].left!=-1)
			q.push(node[cur].left);
		if(node[cur].right!=-1)
			q.push(node[cur].right);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		int l,r;
		scanf("%d %d",&l,&r);
		node[i].left=l;
		node[i].right=r;		
	}
	for(int i=0;i<n;i++)
	{
		int tmp;
		scanf("%d",&tmp);
		num.push_back(tmp);
	}
	sort(num.begin(),num.end());
	preOrder(0);
	for(int i=0;i<n;i++)
		ans[turn[i]]=num[i];

	levelOrder(0);
	return 0;
}

9.5 平衡二叉树AVL

【例】A1066 Root of AVL Tree (25 分)

ATTENTION

  • AVL的插入有相当强的对称性,根据对称性能比较快地记住模板。
#include <cstdio>
#include <algorithm>
using namespace std;

int n,data[30];
struct node{
	int data,height;
	node* lChild,*rChild;
	node(int x)
	{
		lChild=rChild=NULL;
		data=x;
		height=1;
	}
	node()
	{
		lChild=rChild=NULL;
		height=1;
	}
};
int getHeight(node* root)
{
	if(root==NULL) return 0;
	return root->height;
}
void updateHeight(node* root)
{
	root->height=max(getHeight(root->lChild),getHeight(root->rChild))+1;
}
int getBalanceFactor(node* root)
{
	return getHeight(root->lChild)-getHeight(root->rChild);
}
void L(node* &root)
{
	node* tmp=root->rChild;
	root->rChild=tmp->lChild;
	tmp->lChild=root;
	updateHeight(root);
	updateHeight(tmp);
	root=tmp;
}
void R(node* &root)
{
	node* tmp=root->lChild;
	root->lChild=tmp->rChild;
	tmp->rChild=root;
	updateHeight(root);
	updateHeight(tmp);
	root=tmp;
}
void insert(node* &root,int x)
{
	if(root==NULL)
	{
		root=new node(x);
		return ;
	}
	if(x<root->data)
	{
		insert(root->lChild,x);
		updateHeight(root);
		if(getBalanceFactor(root)==2)
		{
			if(getBalanceFactor(root->lChild)==1)
			{
				R(root);
			}
			else if(getBalanceFactor(root->lChild)==-1)
			{
				L(root->lChild);
				R(root);
			}
		}
	}
	else
	{
		insert(root->rChild,x);
		updateHeight(root);
		if(getBalanceFactor(root)==-2)
		{
			if(getBalanceFactor(root->rChild)==-1)
			{
				L(root);
			}
			else if(getBalanceFactor(root->rChild)==1)
			{
				R(root->rChild);
				L(root);
			}
		}
	}
}
int main()
{
	scanf("%d",&n);
	node* root=NULL;
	for(int i=0;i<n;i++)
	{
		scanf("%d",&data[i]);
		insert(root,data[i]);
	}
		
	printf("%d",root->data);
	
}

9.6 并查集

【例】A1107 Social Clusters (30 分)

cluster n.群;簇;集群

ATTENTION

  • 这道题的逻辑是:人 – hobby – 群。所以群实际上是hobby的群,所以,把目标放在合并hobby上。
  • 假设第一个提出hobby[tmp]的人是这个hobby的根结点。
  • 对于每个人的hobby list,将这个人ilist中的每个hobby的集合进行合并,即合并同一个群的hobby集合
  • 对于每个集群的人数,遍历每个人,将其根结点++即可。
  • 不愧是30分的题,还是有思考难度的,记得二刷。
#include <cstdio>
#include <algorithm> 
using namespace std;

int n,k;
int father[1010],hobby[1010]={0},isRoot[1010]={0};

int findFather(int x)
{
	if(father[x]==x)
		return x;
	return findFather(father[x]);
}
void Union(int a,int b)
{
	int faA=findFather(a);
	int faB=findFather(b);
	if(faA!=faB)
		father[faA]=faB;
}
bool cmp(int a,int b)
{
	return a>b;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		father[i]=i; 	//初始化 
		scanf("%d: ",&k);
		for(int j=0;j<k;j++)
		{
			int tmp;
			scanf("%d",&tmp);
			if(hobby[tmp]==0) 	//第一个喜欢hobby[tmp]的作为这个hobby的根 
				hobby[tmp]=i;
			//第i个人与喜欢hobby[tmp]的所有人union 
			Union(i,findFather(hobby[tmp]));
		}
	}
	for(int i=1;i<=n;i++)
	{
		isRoot[findFather(i)]++; 	//这时候的根结点是每个cluster的根结点而非每个hobby的根结点。 
	}
	int cnt=0;
	for(int i=1;i<=n;i++)
		if(isRoot[i]!=0) cnt++;
	printf("%d\n",cnt);
	sort(isRoot+1,isRoot+n+1,cmp);
	printf("%d",isRoot[1]);
	for(int i=2;i<=cnt;i++)
		printf(" %d",isRoot[i]);
}

9.7 堆

【例】A1098 Insertion or Heap Sort (25 分)

insertion sort 插入排序
iterate v.迭代;重复
iteration n. [数] 迭代;反复;重复
iteratively adv. 迭代地;反复地
remove v.删除
shrink v.缩小
region n.地区;范围;部位
extract v.选取
ascending a.上升的,增长的,升序的
indicate v.表明;指出

ATTENTION

  • 误区: 堆排序!=冒泡排序。即每次删除大顶堆的根时,是拿堆的最后一个元素代替的根,并且重新排序。而冒泡排序每次寻找最大的值的下标,将最大的值和未排序范围内的最后一个元素进行swap。这两者完全不同!
  • 再刷,堆排序还不够熟练。
#include <cstdio>
#include <algorithm>
using namespace std;

int n;
int origin[110],after[110],ans[110];
int heap[110];

bool isEqual(int after[],int ans[],int n)
{
	for(int i=1;i<=n;i++)
		if(after[i]!=ans[i])
			return false;
	return true;
}
void downAdjust(int low,int high)
{
	int i=low,j=i*2;
	while(j<=high)
	{
		if(j+1<=high&&heap[j+1]>heap[j])
			j++;
		if(heap[j]>heap[i])
		{
			swap(heap[i],heap[j]);
			i=j;
			j=i*2;
		}
		else
			break;
	}
}
void createHeap()
{
	for(int i=n/2;i>=1;i--)
		downAdjust(i,n);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&origin[i]);
		heap[i]=ans[i]=origin[i];
	}
	createHeap();
	for(int i=1;i<=n;i++)
		scanf("%d",&after[i]);
	for(int i=2;i<=n;i++)
	{
		sort(ans+1,ans+1+i);
		if(isEqual(after,ans,n))
		{
			printf("Insertion Sort\n");
			sort(ans+1,ans+1+i+1);
			printf("%d",ans[1]);
			for(int j=2;j<=n;j++)
			{
				printf(" %d",ans[j]);
			}
			return 0;
		}
	}
	for(int i=n;i>1;i--)
	{
		swap(heap[1],heap[i]);
		downAdjust(1,i-1); 	//无序范围!!! 
		if(isEqual(after,heap,n))
		{
			if(i>2)
			{
				swap(heap[1],heap[i-1]);
				downAdjust(1,i-2);
			}
			printf("Heap Sort\n");
			printf("%d",heap[1]);
			for(int j=2;j<=n;j++)
			{
				printf(" %d",heap[j]);
			}
			return 0;
		}
	}
}

【例】A1155 Heap Paths (30 分)

specialized a.专门的
property n. 性质,性能;财产;所有权
implementation n. [计] 实现;履行;安装启用
traversal n. [计] 遍历;横越;横断物

ATTENTION

  • 理智告诉我这道题不用建树,但因为搞不清中间逻辑,最后还是建了树orz
  • 层序遍历层序遍历!就是按照结点0~n-1的顺序遍历的!所以给你的数组key[]暗含了树结构。
  • 投机取巧.jpg。因为输入的序列没有重复的元素,所以直接比较前两个元素,确定是大顶堆还是小顶堆。ps.如果不是堆并且刚好前两个元素不符合堆结构,后面会判断是不是整个序列都满足大顶堆/小顶堆,所以不会影响结果。
  • 如果不建树的话,在读入key序列前,现将key[]中所有元素初始化成-1,表示这个结点为空。对于每个结点i,它的左孩子就是2*i,右孩子就是2*i+1
  • 路径输出:递归遍历,每次把当前递归结点push进路径ans中,在退出前再pop出当前递归结点,实现回溯。对于当前递归节点,判断其是不是叶子(有没有孩子),这就是递归边界。不是叶子就继续递归其右孩子和左孩子。
  • 判断堆:对于所有的非叶节点[1,2/n],判断它的孩子和它的大小关系。注意柳神是判断孩子与父亲的大小关系的,所以不需要开大空间。但如果是判断父亲与孩子们的关系,可能最后一层只有一个结点,因此会有多个父亲的孩子可能不在key[],就会出现段错误,所以要把数组开大点,确保不会下标越界。

建树:

#include <cstdio>
#include <vector>
using namespace std; 

int n,level=0; 	//树结点数量
int type; 	//0 大顶堆  ; 1 小顶堆 
int key[1010];
struct Node{
	int data;
	int left,right;
	Node()
	{
		left=right=-1;
	}
}node[1010];
vector<int> ans;
bool flag=true;

int height(int n)
{
	int tmp=1,idx=1;
	while(1)
	{
		if(n<tmp)
			return idx;
		tmp*=2;
		idx++;
	}
}
void create() 	//建树 
{
	int idx=1,cnt=1,lastCnt=0,idxKey=0;
	for(int i=1;i<=level;i++)
	{
		idx=1;
		while(idx<=cnt&&(idx+lastCnt)<=n)
		{
			node[idx+lastCnt].data=key[idxKey++]; 	//结点 1-n 
			if((2*(idx+lastCnt))<=n)
			{
				node[idx+lastCnt].left=2*(idx+lastCnt);
			}	
			if((2*(idx+lastCnt))<n)
			{
				node[idx+lastCnt].right=2*(idx+lastCnt)+1;
			}
			idx++;
		}
		lastCnt=lastCnt+idx-1;
		cnt*=2;
	}
}
void printPath(int t)
{
	ans.push_back(node[t].data);
	if(node[t].left==-1&&node[t].right==-1) //到达叶子结点 
	{
		for(int i=0;i<ans.size();i++)
		{
			if(i==0) printf("%d",ans[i]);
			else printf(" %d",ans[i]);
		}
		printf("\n");
		ans.pop_back();
		return ;
	}
	if(node[t].right!=-1)
		printPath(node[t].right);
	if(node[t].left!=-1)
		printPath(node[t].left);
	ans.pop_back();
}
int main()
{
	
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&key[i]);
	}
	if(key[0]>key[1]) type=0; 	//大顶堆 
	else type=1; 	//小顶堆
	
	level=height(n); 
	
	create();

	printPath(1);
	
	for(int i=1;i<=n/2;i++)
	{
		int max=0,min=0;
		if(node[i].left!=-1)
		{
			max=node[node[i].left].data;
			min=node[node[i].left].data;
		}
		if(node[i].right!=-1)
		{
			if(max<node[node[i].right].data) max=node[node[i].right].data;
			if(min>node[node[i].right].data) min=node[node[i].right].data;
		}
		if(type==0)
		{
			if(max!=0&&node[i].data<max)
			{
				flag=false;
				printf("Not Heap\n");
				break;
		 } 
		
		}
		if(type==1)
		{
			if(min!=0&&node[i].data>min) 
			{
				flag=false;
				printf("Not Heap\n");
				break;
			}
		}
			
	}
	if(type==0&&flag)
	{
		printf("Max Heap\n");
	}
	if(type==1&&flag)
	{
		printf("Min Heap\n");
	}
	return 0;
}

不建树:

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std; 

int n,level=0; 	//树结点数量
int type; 	//0 大顶堆  ; 1 小顶堆 
int key[5000];

vector<int> ans;
bool flag=true;

void printPath(int t)
{
	if(t<=n)
		ans.push_back(key[t]);
	if(key[2*t]==-1&&key[2*t+1]==-1) //到达叶子结点 
	{
		for(int i=0;i<ans.size();i++)
		{
			if(i==0) printf("%d",ans[i]);
			else printf(" %d",ans[i]);
		}
		printf("\n");
		ans.pop_back();
		return ;
	}
	if(key[2*t+1]!=-1)
		printPath(2*t+1);
	if(key[2*t]!=-1)
		printPath(2*t);
	ans.pop_back();
}
int main()
{
	fill(key,key+5000,-1); 	//初始化key,即-1表示NULL。 
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&key[i]);
	}
	if(key[1]>key[2]) type=0; 	//大顶堆 
	else type=1; 	//小顶堆
	

	printPath(1);
	
	for(int i=1;i<=n/2;i++)
	{
		int max=0,min=0;
		if(2*i<=n&&key[2*i]!=-1)
		{
			max=key[2*i];
			min=key[2*i];
		}
		if(2*i+1<=n&&key[2*i+1]!=-1)
		{
			if(max<key[2*i+1]) max=key[2*i+1];
			if(min>key[2*i+1]) min=key[2*i+1];
		}
		if(type==0)
		{
			if(max!=0&&key[i]<max)
			{
				flag=false;
				printf("Not Heap\n");
				break;
		 } 
		
		}
		if(type==1)
		{
			if(min!=0&&key[i]>min) 
			{
				flag=false;
				printf("Not Heap\n");
				break;
			}
		}
			
	}
	if(type==0&&flag)
	{
		printf("Max Heap\n");
	}
	if(type==1&&flag)
	{
		printf("Min Heap\n");
	}
	return 0;
}

【例】A1147 Heaps (30 分)

水题

#include <cstdio>
#include <vector> 	//初始化大小开辟空间吗?
using namespace std; 

int n,m;
vector<int> post;

bool isMax(vector<int> heap,int m)
{
	for(int i=1;i<=m/2;i++)
	{
		int j=2*i;
		if(j<m&&heap[j+1]>heap[j]) j++;
		if(heap[i]<heap[j]) return false;
	}
	return true;
}
bool isMin(vector<int> heap,int m)
{
	for(int i=1;i<=m/2;i++)
	{
		int j=2*i;
		if(j<m&&heap[j+1]<heap[j]) j++;
		if(heap[i]>heap[j]) return false;
	}
	return true;
}
void postOrder(int r)
{
	if(r>m) return;
	postOrder(2*r);
	postOrder(2*r+1);
	post.push_back(r);
}
int main()
{
	scanf("%d %d",&n,&m);
	for(int i=0;i<n;i++)
	{
		vector<int> heap(m+1);
		for(int j=1;j<=m;j++)
			scanf("%d",&heap[j]);
		if(isMax(heap,m)==true)
			printf("Max Heap\n");
		else if(isMin(heap,m)==true)
			printf("Min Heap\n");
		else
			printf("Not Heap\n");
		post.clear();
		postOrder(1);
		for(int j=0;j<m;j++)
		{
			if(j==0) printf("%d",heap[post[j]]);
			else printf(" %d",heap[post[j]]);
		}
		printf("\n");
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值