图、图的存储、拓扑排序

图的定义:图是比树更为复杂的非线性结构,是任意两个数据之间可能存在某种特定关系的数据结构。图是一个顶点集合V和一个顶点间的关系集合E组成,记为G = (V,E)。

一些常见的图:

无向图:u,v之间有一条边,从u可以到v,从v可以到u。

有向图:u,v之间有一条边从u指向v,只能从u到v,不能从v到u。

完全图:在一个无向图中,如果任意两个顶点都有一条边,则称该图为无向完全图,在含有n个顶点的无向完全图中共有n * (n-1) / 2条边(等差数列求和)。在一个有向图中,如果任意两个顶点都有相反的两条边连接,则称改图为有向完全图,在含n个顶点的有向完全图中共有n * (n-1) 个顶点。

带权图:根据需要边可以附带一个数值信息,通常称为边权,边上带权值得图为带权图。

稠密图/稀疏图:若一个图接近完全图的边数,则称这样的图为稠密图;若一个图的边数远小于完全图的边数,则称这样的图为稀疏图。

连通图:图中任意两个顶点都是连通的图(从任意一个点可以到剩余所有顶点)。

顶点的度:顶点的度是指依附于该顶点的边数,有向图中还分出度和入度,由这个点出去的边的数目为这个点的出度;由别的点指向这个点的边数为这个点的入度。

环(回路):由起点可以回到起点的路径,无向图的环路长度是大于3的。

 存图的三种方式:

1、邻接矩阵:用二维数组来存,u到v有一条边则G[u][v]=1,不连通则G[u][v]=无穷大;带权图中u到v带权重w,则G[u][v]=w。(邻接矩阵空间浪费较大,通常适用于稠密图)

无向图的临界矩阵是对称的。

const int maxn=1e4;
const int inf=0x3f3f3f3f;
int G[maxn][maxn];

memset(G,inf,sizeof(G));   //初始化距离为inf,表示不连通
int from,to,weight;
cin >> from >> to >> weight;

G[from][to]=weight;  //有向图 

G[from][to]=G[to][from]=weight;   //无向图 

2、邻接表:邻接表,存储方法跟树的孩子链表示法相类似,是图的一种顺序存储和链式存储相结合的存储结构。对于图G的每一个顶点vi,将所有与vi连通的顶点vj链成一个单链表,这个单链表称为顶点vi 的邻接表,再将所有点的邻接表表头放到一个数组中,就构成了图的邻接表。(如图v2到v1,v4,v5都有边,则把v1,v4,v5放在v2后面表示从v2出去的点)

邻接表存图用vector非常方便,vector相当于是动态扩容的二位数组,但是vector动态扩容直接括两倍可能会浪费较大空间。

//不带权
const int maxn=1e4;
vector<int> G[maxn];
int from,to;
cin >> from >> to;

//有向图 
G[from].push_back(to)   //表示从from到to有一条边

//无向图 
G[from].push_back(to);   //from到to和to到from同样的 
G[to].push_back(from); 




//带权图
const int maxn=1e4;
struct Node{
	int to,weight;
	Node(int t,int w){   //构造函数 
		to=t;
		weight=w; 
	}
}; 
vector<node> G[maxn];
int from,to,Weight;
cin >> from >> to >> Weight;

//有向图 
G[from].push_back(Node(to,Weight)   //表示从from到to有一条带权值为weight的边 

//无向图 
G[from].push_back(Node(to,Weight);   //from到to和to到from同样的 
G[to].push_back(Node(from,Weight)); 

3、链式前向星:链式前向星是静态的邻接表,空间浪费很少,推荐使用。

链式前向星是通过存每条边的状态来存一个图的,其核心是head数组和node。

const int maxn=1e4;
int node;     //边的数目
int head[maxn]   //存这个节点的最后一条边的编号 
struct Node{
	int pre,to,weight;  //pre表示前一条边的信息,to是当前边到达的点,weight是权重 
}edge[maxn]; 

//初始化 
void init(){
	memset(head,-1,sizeof(head));  //初始化为-1表示这个点没有边 
	node=0;    //边的数目初始化为0 
}

//加边 
void add_edge(int u,int v,int w){
	edge[node].to=v;      //将第node条边的终点和权重存下来 
	edge[node].weight=w;
	
	edge[node].pre=head[u];   //记录起点u上一条边的编号
	
	head[u]=node++;   //边的数目加1,更改下一次边的编号 
}

//遍历顶点u连接的点
for(int i=head[u];i!=-1;i=edge[i].pre){    //head[u]为顶点u最后一条边的编号,不等于-1说明还有边 
	int v=edge[i].to;;
	int w=edge[i].weight;
	cout << u << "到" << v << "有一条权重为:" << w << "的边" << endl; 
} 

 图的拓扑排序:对有向无环图进行拓扑排序,使得对于有向图无环图的每一条边来说,边的起始顶点总是排在边的结束顶点之前。即如果存在一条从顶点A到顶点B的路径,那么在排序中B出现在A的后面。

通俗的来说,就是入度为0的节点先输出(如果有多个度为0的点,按照字典序小的先输出),然后删除由这个点连出去的边,删除之后再在剩下的点中找入度为0的点输出,然后再删除,以此类推,直到输出所有顶点。

举个例子:

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烊@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值