图的存储结构

本文介绍了图的三种存储方式:邻接矩阵、邻接表(包括动态和静态实现)和向前星。详细解释了每种方式的特点、适用场景及其代码实现,并通过示例对比了它们在实际应用中的优劣。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.邻接矩阵
用一个n×n的二维数组来存储,例如node[i][j],代表的是从点i到点j的距离为node[i][j],缺点是无法存储重边的情况,比如从i点到j点有两条路可以走,一条路的长度为3,一条路的长度为5

代码实现

    int n,m,node;  
    scanf("%d%d",&n,&m);//n点的个数,m边的条数   
      
    //建立城市之前的关系图,自身到自身距离为0,两个城市无法到达则为INF   
    for(int i=1;i<=n;i++)  
        for(int j=1;j<=n;j++)  
        {  
            if(i==j)  
                city[i][j]=0;  
            else city[i][j]=INF;  
        }  
    int x,y,r;  
    //输入题目给出的两个城市之间的距离   
    for(int i=1;i<=m;i++)  
    {  
        scanf("%d%d%d",&x,&y,&r);  
        city[x][y]=r;  
    } 


2.向前星
读入每条边的信息,将边存放在数组中,把数组中的边按照起点顺序排序
优点是应对点非常多的情况,可以存储重边,但是不能直接判断任意来两个顶点之间是否有边,而且排序要浪费一些时间

代码实现

#include<iostream>
#include<cstring>
#include<string>    
#include<queue>    
#include<algorithm>    
#include<map>    
#include<iomanip>    
#define INF 99999999    
#define MAX 20   
using namespace std;    
//向前星 
int head[MAX];//用来记录存储起点为i的第一条边的位置 ,也就是说在排好序以后,该起点的第一条边在这个边的数组中的位置 
              //利用head数组也是为了以后查询方便,可以查询从某一顶点出发的所有的边 
struct Node{
	int from;//边的起点 
	int to;//边的终点 
	int w;//边的权值 
};

Node edge[MAX];//建立一个边的集合的数组 

bool cmp(Node a, Node b)//比较函数 
{
	if(a.from==b.from && a.to==b.to) return a.w<b.w;//如果两条边的起点和终点相同,则按照权值从小到大排序 
	if(a.from==b.from) return a.to<b.to;//如果两条边的起点相同,但是终点不相同,按照重点的大小从小到大排序 
	return a.from<b.from;//如果两条边的起点不相同,则按照起点的大小,从小到大排序 
}

int main()
{
	int n,m;//n代表点的个数,m代表边的条数 
	cin>>n>>m; 
	for(int i=0;i<m;i++)
		cin>>edge[i].from>>edge[i].to>>edge[i].w;
		
	sort(edge,edge+m,cmp); //按上面的排列规则,把边按顺序排好 
	memset(head,-1,sizeof(head));
	head[edge[0].from]=0;//第一条边的位置为0
	
	for(int i=1;i<m;i++)
		if(edge[i].from!=edge[i-1].from) head[edge[i].from]=i;//如果该起点与上一条边的起点不相同,则是一个新的点 
	                                                         //所以该新的起点的第一条变的的位置就要记录下来 
	 //遍历代码
	for(int i=1;i<=n;i++)
	{
	 	for(k=head[i];edge[k].from==i && k<m;k++)//k为顶点为i的第一条边的位置,如果该条边的起点不是i,或者该条边的 
	 	{                                        //位置超出了最大位置,这些边的位置最大为m-1,因为是从0开始算起的 
	 		cout<<edge[k].from<<" "<<edge[k].to<<" "<<edge[k].w<<endl;
		}
	} 
	return 0;
}
/*测试数据  
6 9 
1 2 1 
1 3 12 
2 3 9 
2 4 3 
3 5 5 
4 3 4 
4 5 13 
4 6 15 
5 6 4 

 
程序运行效果 
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4  
*/  


3.邻接表
邻接表就是以每一个节点为起点,分别建立一个单链表,用来链接与它直接相邻的点,通过邻接表可以访问与每个节点直接相连的边与顶点
(1)利用vector动态实现


#include<iostream>
#include<cstring>
#include<string>    
#include<queue>    
#include<algorithm>    
#include<map>    
#include<iomanip>    
#define INF 99999999    
#define maxn 20   
#include<vector>
using namespace std;   

struct node{
	int to;//终点
	int w;//权值
}; 

vector<node> Map[maxn];

int main()
{
	int n,m;
	node t;
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		int from;//form为邻接表的头 ,也就是邻接表的边的起点
		cin>>from>>t.to>>t.w;
		Map[from].push_back(t);
	}
	cout<<endl;
	//遍历每一个点的邻接表 
	for(int i=1;i<=n;i++)
	{
		for(vector<node>::iterator k=Map[i].begin();k!=Map[i].end();k++)//遍历邻接表的每一条边 
		{
			t=*k;
			cout<<i<<" "<<t.to<<" "<<t.w<<endl;
		}
	}
	return 0;
}
/*测试数据  
6 9 
1 2 1 
1 3 12 
2 3 9 
2 4 3 
3 5 5 
4 3 4 
4 5 13 
4 6 15 
5 6 4 

 
程序运行效果 
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4  
*/  


(2)利用数组静态实现(链式向前星)


链式向前星的建图效率非常高,读入结束,建图就结束,时间复杂度为O(m),空间上使用了两个数组,空间复杂度为
O(m+n)
链式向前星的优点在于除了必要的边信息的存储空间外,只需要非常少的额外空间即可实现,代码和原理都比较简单,可以存储重边,还可以应付点和边的数量很大的情况,与动态建表相比没有内存管理,更安全,因该说除了不能直接用起点和终点确定是否有边以外,链式向前星几乎是完美的(以上语句摘自《图论及应用ACM-ICPC程序设计系列》)

用邻接表来存储图的结构,构图的时间复杂度为O(m),遍历每一条边的时间复杂度也为O(m)

如果一个图是稀疏图的话,m要远小于n,因此稀疏图用邻接表来存储要比邻接矩阵要好得多

#include<iostream>
#include<cstring>
#include<string>    
#include<queue>    
#include<algorithm>    
#include<map>    
#include<iomanip>    
#define INF 0x3f3f3f3f 
#define maxn 20   
#include<vector>
using namespace std;   

struct node{
	int to;
	int w;
	int next;
}; 

int main()
{
	int n,m,from;//form代表邻接表的第一个点,也就是起点 
	node Edge[maxn];//node类型的存放边的数组 
	int head[maxn];//用来存放第i个点所对应的第一条边在Edge数组里面的位置 
	cin>>n>>m;
	memset(head,-1,sizeof(head));
	//相当于链表的前插,就是每次插入一条边都是插入到该链表的第一位置,比如链表一开始为1->2->3,后来插入了4,就变成了
	//4->1->2->3 
	for(int i=0;i<m;i++)
	{
		cin>>from>>Edge[i].to>>Edge[i].w;
		Edge[i].next=head[from];
		head[from]=i;
	} 
	cout<<endl;
	//遍历每一个点的邻接表 
	for(int i=1;i<=n;i++)
	{
		for(int j=head[i];j!=-1;j=Edge[j].next)//-1代表该邻接表已经没有下一条边了,该点的邻接表遍历完成 
			cout<<i<<" "<<Edge[j].to<<" "<<Edge[j].w<<endl;
	}
	return 0;
}
/*测试数据  
6 9 
1 2 1 
1 3 12 
2 3 9 
2 4 3 
3 5 5 
4 3 4 
4 5 13 
4 6 15 
5 6 4 

 
程序运行效果 
1 3 12
1 2 1
2 4 3
2 3 9
3 5 5
4 6 15
4 5 13
4 3 4
5 6 4  
*/  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值