”邻接表建图+图的遍历“教程(C++)

教程1.存图建图

我们都知道,

图和树都由“点”和“边”组成

(一条边连接两个点,下文将一条边指向的点叫做”儿子点“,另一个点则称作“父亲点”)

所以我们只要把”边“与”点“的关系存下来

就基本完成了图的储存

那我们要记录的只有:

一、针对每一个点:

        以当前点为父亲点的的第一条边

二、针对每一条边:

        1.边的编号

        2.该边所指向的点(儿子节点)

        3.下一条以当前边连接的父亲节点为父亲节点的边

        4.点权(不少问题中都牵涉到点权,如最短路,最小生成树。。。可根据问题需要选择记录或不记录)

CODE:

//last[i]代表第一条以i为父亲节点的边(记录边的编号)
int last[N*2]; //N*2   怕有双向边 

//cnt:边的编号 
int cnt;

struct edge//存边 
{
	
	//to:这条边指向的儿子节点(记录点的编号)
	int to;
	
	//next下一条以当前边连接的父亲节点为父亲节点的边 
	int next;
	
	//边的权值 
	int value;
	
} e[N*2];//下标为边的编号 

void insert(int u,int v,int val)// 建边(u为父亲点,v为儿子点) 
{   

    //新加一条边  (编号++)
	++cnt; 
	
	//儿子节点为v
	e[cnt].to=v; 
	
	//附上边权 
	e[cnt].value=val;
	
	//更新当前第一条以u为父亲点的边
	e[cnt].next=last[u]; 
	last[u]=cnt; 
	
}
int main()
{
//    如果我想建个图。。。(如下) 
//    (括号里的数是边权)
	
//              1  
//             / 
//        (1) /                            
//           /  
//          2 
//          /\    
//    (3)  /  \  (2)             
//        /    \            
//       3------4                 
//          (4)
    
    
    //如下建的是双向边(双向边建两次边,单向边只要一次) 
    insert(1,2,1);
	insert(2,1,1);
	
	insert(2,3,3);
	insert(3,2,3);
	
	insert(2,4,2);
	insert(4,2,2);
	
	insert(3,4,4);
	insert(4,3,4);
	
	return 0;
}

教程2. 图的遍历:

图的遍历我们一般采用DFS(深度优先)

遍历顺序如下图

很好写,直接上代码

CODE:

//vis[i]代表i被遍历过
bool vis[N];

void dfs(int x) {

	//打上标记,该点被遍历过
	vis[x]=1;

	for(int i=last[x]/*从第一条边开始*/;i;i=e[i].next/*跳到下一条边*/)
	{

		//如果此节点被遍历过,请跳过
		if(vis[e[i].to]) continue;

		//遍历儿子节点
	    dfs(e[i].to);

	}
}

实战

题目:树的结点计数

Description

第一行是一个整数N1\leqslantN\leqslant50000),表示计算机的台数,计算机被编号为1...N。 下面N-1行,每行包括两个整数X, Y,表示XY这两台计算机之间由一条网线连接。 1号点为根

Format

Input

第一行给出数字N

接下来N-1行描述这个树

Output

给出N行,分别表示从1号到N号点,每个点有多少个子结点

Samples

输入数据 1

3
1 2
1 3

输出数据 1

2
0
0

Limitation

1s, 1024KiB for each  test case.

CODE:

#include<bits/stdc++.h>
using namespace std ;
const int N=50000;
int n,m;
int a,b;
int las[N*2];
int cnt;
int son[N];
bool vis[N];
struct edge
{
	int to,last;
} e[N*2];

void insert(int u,int v)
{
	++cnt; 
	e[cnt].to=v;
	e[cnt].last=las[u];
	las[u]=cnt;
}

void dfs(int x) {
	son[x]=1;
	vis[x]=1;
	for(int i=las[x];i;i=e[i].last) {
		if(vis[e[i].to]) continue;
		dfs(e[i].to);
		//加上每个儿子的子节点个数
		son[x]+=son[e[i].to];
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<n;i++) {
		cin>>a>>b;
		insert(a,b);
		insert(b,a);
	}
	dfs(1);
	for(int i=1;i<=n;i++) {
		//输出时减去自己
		cout<<son[i]-1<<endl;
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值