图(Graph)是由节点(Node)和边(Edge)组成的一种数据结构,用于表示对象之间的关系。其中,节点表示对象,边表示对象之间的关系。
基本术语:
节点(Node):图中的基本单元,用来表示对象。
边(Edge):两个节点之间的连接线,用来表示对象之间的关系。
有向图(Directed Graph):边是有方向的图。
无向图(Undirected Graph):边是无方向的图。
权重(Weight):边上的数值表示该边的权重大小。
度(Degree):节点相邻边的个数,度为0的节点称为孤立节点。
路径(Path):从一个节点到另一个节点所经过的边的序列。
连通性(Connectivity):指图中任意两个节点之间是否存在一条路径。
子图(Subgraph):一个图的子集,包含该图中的部分节点和边。
图的存储结构通常有两种方式:邻接矩阵和邻接表。
邻接矩阵:用一个二维数组G[V][V]G[V][V]来表示有|V|∣V∣个顶点的图。如果(i,j)(i,j)是一条边,则G[i][j]=1G[i][j]=1。否则,G[i][j]=0G[i][j]=0(对于无向图,还需要设置G[j][i]G[j][i]的值)。如果图是带权的,则可以将G[i][j]G[i][j]赋为边的权重。
邻接表:使用一个数组adjlist[|V|]adjlist[∣V∣]来表示图。其中每个adjlist[i]adjlist[i]都是一个单向链表(或者是数组),存储从第ii个节点出发的所有边(对于无向图,需要把每条边都存储两次)。
// 定义邻接表节点结构体
typedef struct _node {
int vertex;
struct _node *next;
} Node;
// 定义邻接表结构体
typedef struct _graph {
int n; // 图中顶点数量
Node *adj[MAX_VERTICES]; // 邻接表数组
} Graph;
// 初始化邻接表
void init(Graph *g, int n) {
g->n = n;
for (int i = 0; i < n; i++) {
g->adj[i] = NULL;
}
}
// 向邻接表中添加一条边
void add_edge(Graph *g, int u, int v) {
Node *p = (Node*)malloc(sizeof(Node));
p->vertex = v;
p->next = g->adj[u];
g->adj[u] = p;
}
// 打印邻接表
void print_graph(Graph *g) {
for (int i = 0; i < g->n; i++) {
printf("%d: ", i);
for (Node *p = g->adj[i]; p != NULL; p = p->next) {
printf("%d -> ", p->vertex);
}
printf("NULL\n");
}
}
1、顺序存储结构:所有数据元素在存储器中占有一整块存储空间,两个逻辑上相邻的元素在存储器中的位置同样相邻。通俗来说,就是每个逻辑序号对应一个数据元素,通过次序号可直接找到对应元素的存储地址,进而获得元素值。
2、链式存储结构:每个逻辑元素用一个内存结点存储,每个结点是单独分配的,通过指针域将所有结点连接起来实现数据之间的逻辑关系。通俗来说,就是数据通过指针域相连(用到指针和结构体),可以假想成数据通过一铁链相连接,铁链前后数据相关联。
3、索引存储结构:在存储数据元素信息的同时还建立附加索引表,索引表中的索引项包含数据元素和存储地址
4、哈希存储结构:根据元素的关键字通过哈希函数直接计算出一个值,并将这个值作为该元素的存储地址