图的概念
- 图的定义
一个图G是一个二元组,即序偶<V,E>,或记作G=<V,E> ,其中V是有限非空集合,称为G的顶点集,V中的元素称为顶点或结点;E称为 G的边的集合,所有的边ei都属于E,都有v中的结点与之对应,称ei为 G的边。
- 图的基本概念
l 无向图:每条边都是无向边的图。
l 有向图:每条边都是有向边的图。
l 混合图:在一个图中,有些边是有向边,另一些边是无向边,则该图为混合图。
l 有限图:一个图的点集和边集都是有穷集的图。
l 零图:边集为空集的图。
l 平凡图:仅有一个结点而没有边构成的图。
l 关联:若有ei=(u,v) 且ei属于E ,则称u是和v相关联的。
l 孤立点:无边关联的点。
l 自环:若一条边所关联的两个结点重合,则称此边为自环。
l 邻接:关联于同一条边的两个点 和 称为邻接的;关联于同一个点的两条边 和 是邻接的(或相邻的)。
图的存储结构
最简单的存储——邻接矩阵
邻接矩阵的英文名是 adjacency matrix。它的形式是 bool adj[n][n],这里面n是节点个数,adj[i][j]表示i和j之间是否有边。
如果边有权值,也可以直接用 int adj[n][n] ,直接把边权存进去。
它的优点是可以在O(1)时间内得到一条边是否存在,缺点是需要占用O(n^2)的空间。对于一个稀疏的图(边相对于点数的平方比较少)来说,用邻接矩阵来存储的话,成本偏高。
其代码可以表示为(假设各边长度均为1):
#include<iostream>
using namespace std;
const int maxn=105;
int adj[maxn][maxn]={0}; //定义邻接矩阵
int x,y; //输入两条边
int n,m; //供输入n对边 ,m个顶点 (x,y <= m)
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>x>>y;
adj[x-1][y-1]=1;
adj[y-1][x-1]=1;
}
for(int i=0;i<m;i++){
for(int j=0;j<m;j++){
cout<<adj[i][j]<<' ';
}
cout<<endl;
}
return 0;
}
邻接矩阵创建无向网
邻接表
邻接表(Adjacency List)顾名思义,就是通过链表或者利用数组模拟链表的方式将图的相连接关系表示的一种方法,存储方法跟树的孩子链表示法相类似,是一种顺序分配和链式分配相结合的存储结构。如这个表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。
这里必须要特别注意邻接表的结尾以空或者一个特殊的标记,表示到达结尾。在一些需要快速表达概念的场合,可以将空结点的指向忽略不表达。
那么如果是双向图,意思是1与2联通,可以由1走向2,同时也可以由2走向1的情况时,这张表又是如下表示
邻接表的创建
#include<stdio.h>
#include<iostream>
#include<malloc.h>
#define maxSize 1000
using namespace std;
typedef struct ArcNode {
int adjvex;
struct ArcNode *nextarc;
} ArcNode;
typedef struct {
int data;
ArcNode *firstarc;
} Vnode;
//可以利用结构体整体结构,也可以拆分结构体变为单独搜索
typedef struct {
Vnode adjlist[maxSize];
int n, e;
} AGraph;
AGraph *graph;
//插入链表末尾
void insertNode(ArcNode *node, ArcNode *newNode) {
ArcNode *p = node;
while(p->nextarc != NULL) {
p = p->nextarc;
}
p->nextarc = newNode;
}
void create() {
graph = (AGraph*)malloc(sizeof(AGraph));
cout << "输入顶点的数目: " << endl;
cin >> graph->n;
cout << "输入图中边的数目: " << endl;
cin >> graph->e;
int u = -1, v = -1;
for(int i = 0; i < graph->n; i++) {
graph->adjlist[i].firstarc = NULL;
}
ArcNode *node;
for(int i = 0; i < graph->e; i++) {
cout<<"请输入联通点A与B"<<endl;
cin >> u >> v ;
node = (ArcNode *)malloc(sizeof(ArcNode));
node->adjvex = v;
node->nextarc = NULL;
graph->adjlist[u].data = u;
if(graph->adjlist[u].firstarc == NULL) {
//边
graph->adjlist[u].firstarc = node;
} else {
//插入边
insertNode(graph->adjlist[u].firstarc, node);
}
}
}
void travseTree() {
for(int i = 0; i < graph->n; i++) {
if(graph->adjlist[i].firstarc != NULL) {
cout <<"与"<< i << "连接的点有:";
ArcNode *p = graph->adjlist[i].firstarc;
while(p != NULL) {
cout << p->adjvex << " ";
p = p->nextarc;
}
cout << endl;
}
}
}
int main(void) {
create();
travseTree();
return 0;
}
邻接表创建无向网
邻接矩阵和邻接表
十字链表
链式向前星
链式前向星
在理解链式向前星之前我们需要了解什么是向前星,前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序,并记录下以某个点为起点的所有边在数组中的起始位置和存储长度,那么前向星就构造好了。
链式向前星的本质是利用链表的特性(一个结点指向另一个结点),以达到不需要像向前星那样排序(排序的平均情况需要O(nlogn)的代价)就可以直接搜索到目标,从而达到减少计算机运算时间使用的情况。