算法基础课 第三章 搜索与图论(一)树与图的深度优先遍历

图的邻接表的存储与初始化

#include <iostream>
using namespace std;
const int N = 100010;

int h[N],e[N],en[N],idx;

int main()
{
    memset(h,-1,sizeof h);

    return  0;
}

宽度优先遍历

#include<iostream>
#include <cstring>
using namespace std;
const int N = 100010,M = N*2;

int h[N],e[M],en[M],idx;
bool st[N];

void add(int a,int b)
{
    e[idx] = b,en[idx] = h[a],h[a] = idx,idx++;
}
//为节点a加一条有向边,或者说加一个可达节点 
//当前节点idx的值先赋上,其指向头节点(因为是头插法)。


void dfs(int u)
{
    st[u] = true;
	for(int i = h[u];i!=-1;i = en[i])
	{
		int j = e[i];
		if(!st[j]) dfs(j);
	 } 
}

//存储按照邻接表的形式,每个节点u连了一大串,都是其可到达的节点
/*
st[u] 为true 代表u点已经用过了,遍历过了。接着看u节点的可达节点,看其是否用过
没用过就往下找,用过回溯,看看u节点的其他可达节点。
*/
 
int main()
{
    
    memset(h,-1,sizeof h);
    dfs(1);
	
    return 0;
}

深度优先遍历

树的重心

样例模拟:

 

删除每个节点 统计其他连通块大小

删除每个节点 其左右儿子为连通块 剩余节点是另一个连通块

/*重心就是删除其之后 整个树的剩余连通块的点数最大值最小
找到重心 然后输出其剩余连通块点数最大的。 不理解就自己手动模拟一遍*/ 

#include<iostream>
#include <cstring>
using namespace std;
const int N = 100010,M = N*2;

int n;
int h[N],e[M],en[M],idx;
bool st[N];


void add(int a,int b)
{
    e[idx] = b,en[idx] = h[a],h[a] = idx,idx++;
}


int ans  = N; //全局答案,就是遍历所有删除节点的情况中 一个树中的剩余连通块里最大值的里最小的那一个 就是重心了。最大值也不会超过n个节点.
 
//dfs返回以u为根的子树中点的数量大小
int dfs(int u)
{
	//1 当前节点已被使用 
	st[u] = true;
	//2
	int sum = 0;//当前子树大小(<=>当前节点下一共有多少节点)
	int size = 0;//记录删除当前节点后 每一个连通块点的最大值(<=>当前节点下子树最小的)
    //3
	for(int i = h[u];i!=-1;i = en[i]) //遍历以u为根节点的所有的节点 
	{
		int j = e[i];
		if (st[j]) continue;
		
		int s = dfs(j);//当前节点子树大小
		
		size = max(size,s);
		// 首先是0与当前节点左子树比较,接着是当前节点左子树与当前节点右子树(比较),接着等等 总之是所有的子树比较一遍。当前节点子树s也是一个连通块
		sum += s;
		//当前节点子树s也是以u为根节点的子树的一部分,因为sum一直在递归中 所以越上层值越大,因为一直在加,
		//记录当前节点下一共有多少节点,(便于后续求u节点之上的连通块点数) 

	 }
	 //4 此时的max中的size是左右子树中的最大值,接着再和 u节点上面的一堆n-sum-1比较 
	 size = max(size,n-sum-1);  
	 ans = min(ans,size); 
	 //5
	 return sum+1;//每次返回一次就是其下加了一个节点
}	

int main()
{
    cin>>n;
    
    memset(h,-1,sizeof h);
    
	for(int i = 0;i<n-1;i++)
	{
		int a,b;
		cin>>a>>b;
		add(a,b);add(b,a);
	 } 
	dfs(1);//dfs(n)//从树中任意一个节点开始搜都可以,因为是树->是无向边
	printf("%d\n", ans);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值