图的三种存储方法

 对于节点 i , j 

邻接矩阵:

开二维数组 G[][] ,

int G[N][N];

用G[i][j]存储边的权值,如果是

无向图,G[i][j]=G[j][i]= i->j 或 j->i 的权值

有向图则令G[i][j]和G[j][i]分别为 i->j 和 j->i 各自的权值。

如果 i , j 之间并没有直连边,可以将G[i][j]和G[j][i]设置为某个特殊值,如 -1,0x3f3f3f3f 等,但是不建议设置为0x7fffffff或者0x7f7f7f7f,因为这有可能在计算中导致数据溢出。

优点:

编码简单,访问快捷高效

适合稠密图

缺点:

如果 i,j 之间有多条边则无法存储

对于稀疏图则严重浪费空间

无法应对节点数较多的图

邻接表:

为了应对邻接矩阵无法存储i,j间多条边的情况,可以建立邻接表

对于每一个节点i,用一个结构体存储他的所有直连边以及对应的权值

struct edge
{
	int to,length;
	edge (int TO,int LENGTH)
	{
		to=TO;
		length=LENGTH;
	}
};

再利用C++的vector存储每一个点的结构体,这样可以省下很多空间

vector <edge> e[N];

初始化:

将以 i 为起点的边的数据写入 e[i] :

e[i].push_back(edge(j,len));

访问 i 的所有直连边时,用一个 for 循环遍历 e[i] 即可

邻接表的缺点是想要找到他的某个邻居就需要遍历他的所有邻居,效率不如邻接矩阵,但是一般邻居不会太多,影响不大

链式前向星:

思路:

有时候边数太多,邻接表也无法应对,这时候我们就需要想办法节省空间,这一方法就是按顺序将所有的边存储起来

这么存储会遇到一个问题,如何找到某个节点的所有直连边?

不难想到,如果对节于点 i 的某个已知的邻居 j ,他都能告诉我们在他前面出现的节点i的另外一个邻居的存储编号,即nex,那么以此类推,我们就可以找到 j 以及在 j 前面出现的 i 的所有邻居。因此,只要保证 j 是 i 的最晚出现的邻居,就可以遍历 i 的所有邻居了。由此我们不难联想到单链表的思想,只需用一个数组记录某个节点最后一个邻居的存储编号(即表头)head[i],便可以按照他们出现的顺序的逆序遍历

如图,记录节点1最晚出现的邻居6被存储在6号,就从6号开始遍历

6号记录上一个邻居存储在3号,于是进入到三号,再到1号,最后遍历到0号,结束遍历

于是重点便是:

每当加入一条新边,就要及时更新该边起点 i 的“最晚出现的邻居的位置”,也即单链表中加入一个新元素的情况:

新加入的边的nex就是未更新的head[i],新的head[i]就是当前的存储编号

如图,head[1] 变成了8,8号的nex就是更新之前的head[1]==6,这样便能保证从8号开始沿着nex遍历到0号可以不剩余地遍历以节点1为起点的所有边

于是便有以下代码

int cnt,head[N];
struct edge
{
	int to,length,nex;
}e[N];
void add(int from,int to,int len)
{
	e[++cnt].to=to;
	e[cnt].length=len;
	e[cnt].nex=head[from];
	head[from]=cnt;
}

其中结构体e用于按顺序存储所有的边

head[i] 记录以i 为起点的边的最后一条存储在几号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值