树相关算法(一)

匿名 (未验证) 提交于 2019-12-03 00:30:01

前言:算法竞赛中常见的树问题

  • (二叉)树的遍历
  • 树的重心
  • 树的直径
  • 最近公共祖先(LCA)
  • 哈夫曼树
  • 树链剖分

一、(二叉)树的遍历

这种遍历顺序和DFS入栈的顺序很像,这种二叉树遍历方式称为先序遍历。除了先序遍历外,还有另外两种遍历,中序遍历和后序遍历。

  • 先序遍历:访问根节点,遍历左子树,遍历右子树;
  • 中序遍历:遍历左子树,访问根节点,遍历右子树;
  • 后序遍历:遍历左子树,遍历右子树,访问根节点。

对于上面的那棵树,给出三种遍历方式是:

  • 先序遍历:A->B->D->F->G->H->I->E->C;
  • 中序遍历:F->D->H->G->I->B->E->A->C;
  • 后序遍历:F->H->I->G->D->E->B->C->A。

说了这么多,二叉树的遍历有什么用呢?答案是,没太大用。一般情况下是用来当做题目中的信息。对于OIer来说这些是常识,初赛会考的。

二、树的重心

树的重心,也叫树的质心。对于一棵树来说,删去该树的重心后,所有的子树的大小不会超过原树大小的二分之一。树的重心还有一个性质,是相对于树上的其他点而言的,就是删去重心后形成的所有子树中最大的一棵节点数最少。换句话说,就是删去重心后生成的多棵子树是最平衡的。一棵树的重心至多有两个。





#include<cstdio> #include<vector> #include<cstring> #include<algorithm> using namespace std; const int maxn=20100; int n,father; int siz[maxn];//siz保存每个节点的子树大小。 bool vist[maxn]; int CenterOfGravity=0x3f3f3f3f,minsum=-1;//minsum表示切掉重心后最大连通块的大小。 vector<int>G[maxn]; void DFS(int u,int x){//遍历到节点x,x的父亲是u。 	siz[x]=1; 	bool flag=true; 	for(int i=0;i<G[x].size();i++){ 		int v=G[x][i]; 		if(!vist[v]){ 			vist[v]=true; 			DFS(x,v);//访问子节点。 			siz[x]+=siz[v];//回溯计算本节点的siz 			if(siz[v]>n/2) flag=false;//判断节点x是不是重心。 		} 	} 	if(n-siz[x]>n/2) flag=false;//判断节点x是不是重心。 	if(flag && x<CenterOfGravity) CenterOfGravity=x,father=u;//这里写x<CenterOfGravity是因为本题中要求节点编号最小的重心。 } void init(){ 	memset(vist,false,sizeof(vist)); 	memset(siz,0,sizeof(siz)); 	minsum=-1; 	CenterOfGravity=0x3f3f3f3f; 	for(int i=0;i<maxn;i++) G[i].clear(); } int main(){ 	int T; 	scanf("%d",&T); 	while(T--){ 		scanf("%d",&n); 		init(); 		for(int i=1;i<n;i++){ 			int u,v; 			scanf("%d%d",&u,&v); 			G[u].push_back(v); 			G[v].push_back(u); 		} 		vist[1]=1; 		DFS(-1,1);//任意选取节点作为根,根节点的父亲是-1。 		for(int i=0;i<G[CenterOfGravity].size();i++) 			if(G[CenterOfGravity][i]==father) minsum=max(minsum,n-siz[CenterOfGravity]); 			else minsum=max(minsum,siz[G[CenterOfGravity][i]]); 		printf("%d %d\n",CenterOfGravity,minsum); 	} 	return 0; }

三、树的直径









易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!