图论学习笔记——链式前向星——永远滴神

图的存储

邻接矩阵(方阵)

比较简单,没啥可说的

邻接表

struct edge
{
	int from;//边的起点 
	int to;//边的终点 
	int w;//边的权值 
	edge(){}
	edge(int a,int b,int c){from=a,to=b,w=c;}//结构体赋值函数 
};
vector<edge>e[NUM];//e[i] 用于储存第i个节点连接的所有边 
//初始化
for(int i=1;i<=n;i++){
	e[i].clear();
} 
//存边
e[a].push_back(edge(a,b,c));
//检索节点U所有的邻居
for(int i=0;i<e[u].size();i++){
	
} 

代码就是这样,肯定是没错的,vector用起来6到飞起,这就是罗勇军老师的实力,2333

#include<iostream>
#include<vector>
using namespace std;
const int NUM=100;
struct edge{
	int from;//边的起点 
	int to;//边的终点 
	int w;//边的权值 
	edge(){}
	edge(int a,int b,int c){from=a,to=b,w=c;}//结构体赋值函数 
};
vector<edge>e[NUM];//e[i] 用于储存第i个节点连接的所有边 
int main()
{
    //初始化
    for(int i=1;i<=NUM;i++){
        e[i].clear();
    }
    int T;
    cin >> T;
    int a,b,c;
    while(T--){
        cin >> a >> b >>c;
        //存边
        e[a].push_back(edge(a,b,c));
    }
    int u;
    cin >> u;     
    //检索节点U所有的邻居
    for(int i=0;i<e[u].size();i++){
        cout << e[u][i].to << endl;
    }     
    system("pause");
    return 0;
}

测试数据
在这里插入图片描述

前向星

假设有那么一张有向图,

一种数据结构,以储存边的方式来存储图。构造方法如下:读入每条边的信息,将边存放在数组中,把数组中的边按照起点顺序排序(可以使用基数排序,如下面例程),前向星就构造完了。通常用在点的数目太多,或两点之间有多条弧的时候。一般在别的数据结构不能使用的时候才考虑用前向星。除了不能直接用起点终点定位以外,前向星几乎是完美的。——来自百度百科

简而言之,前向星是一种特殊的边集(边的集合)数组。

构造前向星

首先读入所有的边(一般都是两个点)比如(1,2),表示的是以1为起点,2为终点的这一条边。把这些点全部读入到一个数组(可以是结构体数组)中,我们把这个数组称为边集,也就是边的集合。
然后我们对这个边集进行排序,我们把每条边按照起点,从小到大排序,(如果某几条边起点相同,就把这几条起点相同的边按照终点数值从小到大排序)
紧接着记录下以某个点为起点的所有边在边集中的起始位置和存储长度(我们把同一个起点视作同一个类型,这里的储存长度指的就是同一个类型在边集中所占用的空间)
那么前向星就构造好了!!!是的,这就是前向星,很简单有木有

在这里插入图片描述
我们来演示一下
首先假设输入为

1 3
3 4
1 6
6 5
6 3
5 4
1 4
1 2
2 4

我们读入后对边集进行排序,得到

1 2 ————head[1]=1————len[1]=4
1 3
1 4
1 6
2 4————head[2]=5————len[2]=1
3 4————head[3]=6————len[3]=1
5 4————head[5]=7————len[5]=1
6 3————head[6]=8————len[6]=9
6 5

我们同时还要将每种类型的边的起始位置保存在head数组中,我们可以定义head[i]=a表示以i为起点的边在边集内的起点为a;然后我们还需要知道每种类型的边占用了多少空间,我们同样可以定义len[i]=b,表示==以i为起点的边在边集内的占用了b个空间。

前向星的PRO——PLUS——ULTRA:链式前向星

相较于前向星,链式前向星可以省略掉排序的过程,你问我为什么?答案就在它的名字:链式,因为相互链接了嘛,我能够顺着边来找到你,那我还排序干啥,其实我们只要深刻理解链式,那么就能够很轻松的理解链式前向星了。

上代码

const int NUM=1000005;
struct Edge
{
	//假设现在这条边的起点是i 
	int to;//用于存放当前边的终点,
	int next;//链式的体现,next指向的就是边集中(可就是这个结构体数组中)上一个以i为起点的数据的位置 
	int w;//w表示当前边的权值
}edge[NUM];//结构体数组,就是我们所说的边集,用来存放所有边的数据 
int cnt;//用来标记最后一个元素的位置 
int head[NUM];//用来存放起点为i的最后加进来的那条边的位置

void init()//初始化 
{
	for(int i=0;i<NUM;i++){//注意,这里初始值为-1大有深意 
		edge.next=-1;//数组的头元素是0,-1表示这个元素不存在,因为是初始化,所以肯定不指向谁 
		head[i]=-1;//和上面同理
		//edge.to和edge.w这两个不重要,不用初始化
	}
	cnt=0; 
}

void addedge(int u,int v,int w)//向边集中添加元素,u表示起点,v表示终点,w表示权值 
{
	//把这个新读入的边放入边集 
	edge[cnt].to=v;
	//更新链式的指向
	edge[cnt].next=head[u];
	//更新权值
	edge[cnt].w=w; 
	//更新head数组 
	head[u]=cnt++;//cnt++,这样下一次存入元素就直接可以拿来用了
}

//遍历所有以u为起点的边
for(int i=head[u];~i;i=edge[i].next){//这里很妙,为什么要对i取反 
	//因为我们默认在不断查找的时候,都是向前查找,直到查找到以u为起点的元素第一次出现的时候,这时候注意
	//因为在初始化的时候,最初的那个edge[head[u]].next是-1,而这里我们不断向前查找,最终i会等于-1,这时候
	//因为-1的补码是1,对i取反,这样i就会变成0,这样就不满足条件,自然推出循环 
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值