文章目录
- 回顾
- 提要
- 图的定义和表示
- 图的表示
- 完全图和子图
- 顶点的度
- 路径与回路
- 连通图
- 邻接矩阵
- 权和网
- 邻接表
- 示例
- 深度优先遍历 (DFS)
- 广度优先遍历 (BFS)
- 广度优先遍历过程
- 总结
- 邻接矩阵存储结构
- 邻接表存储结构
回顾
- 线索化二叉树:在某种次序遍历过程中创建线索,将空指针指向遍历序列中的前驱或后继结点。
- 线索二叉树:经过线索化处理的二叉树。
- 哈夫曼树(最优二叉树):带权路径长度WPL最小的二叉树。
提要
- 图的定义和表示。
- 图的相关概念。
- 图的存储结构与实现。
- 图的遍历方法。
图的定义和表示
图G
由顶点集合V
和边集合E
组成,记为G = (V, E)
。
-
V是顶点元素的有限集合,
-
E是顶点间关系——边的有限集合。
-
(边是顶点的无序对或有序对)
-
无向图:边没有方向,由顶点的无序对组成。
-
由没有方向的边构成的图。
-
无向图中的边由顶点的无序对组成。(用圆括号表示)
-
邻接点:无向图中,若存在一条边(Vi, Vj),则称Vi和Vj互为邻接点。
-
右图中的边是无方向的,
-
即 (V1, V2) 和 (V2, V1)
-
表示同一条边。
-
有向图:边有方向,由顶点的有序对组成,称为弧。
-
由有方向的边构成的图。
-
弧:有向图中的边由顶点的有序对组成,也称作弧。(用尖括号表示)
-
有向图中,顶点的有序对<Vi, Vj>表示从Vi指向Vj的一条有向边,其中Vi是起点,Vj是终点。
-
右图中的边是有方向的,
-
<V1, V2> 和 <V2, V1>
-
表示两条不同的边。
图的表示
- 邻接矩阵:使用二维数组表示,其中
matrix[i][j]
表示顶点i
到顶点j
的边的信息。 - 邻接表:使用链式存储结构,包含顶点表和边表。
完全图和子图
顶点的度
无向图中,顶点的度指与每个顶点相连的边数。
有向图中,顶点的度分成入度与出度。
入度:以该顶点为终点的弧的数目
出度:以该顶点为起点的弧的数目
路径与回路
连通图
邻接矩阵
有向图和无向图的邻接矩阵表示方法。
权和网
邻接表
-
顶点表:存放图中每个顶点的信息及指向边表的头指针。由顶点表和边表组成,是链式存储结构。
-
顶点表:存放图中每个顶点的信息以及指向该顶点边表的头指针。顶点表通常采用顺序存储结构。
- 顶点表的结点结构:
- 顶点域data存放顶点信息,head为边表头指针。
-
边表:为图中每个顶点建立的单链表,存放相邻接的邻接点。
-
边表的结点结构:
-
邻接点域adjVex存放邻接点在顶点表中的序号,next为指向下一个邻接点的指针。
-
-
示例
- 逆邻接表:结构与邻接表完全相同,只是边表中每个结点存放的是每条弧的弧尾顶点。
深度优先遍历 (DFS)
遍历过程:
- 从任一顶点出发,访问此顶点。
- 选择一个未访问的邻接点,再从w出发进行深度优先遍历(递归),直至图中所有和v连通的顶点都被访问过为止;
- 访问所有连通顶点后,若存在未访问顶点,重复以上步骤。直至图中所有顶点都被访问为止。
示例:深度优先遍历无向图
示例:深度优先遍历有向图
广度优先遍历 (BFS)
遍历过程:
- 从任一顶点出发,访问此顶点。
- 访问所有未访问的邻接点,然后是它们的邻接点,依此类推。
- 若存在未访问顶点,则另选图中一个未被访问的顶点作起点,重复以上步骤。
示例:广度优先遍历无向图
示例:广度优先遍历有向图
广度优先遍历过程
使用队列完成广度优先遍历。
总结
- 图的结构特点。
- 图与线性表和树的区别。
- 图的两种存储结构:邻接矩阵和邻接表。
- 图的两种遍历方法:深度优先和广度优先。
以下是代码示例,展示如何使用C++实现图的邻接矩阵和邻接表存储结构,以及深度优先遍历和广度优先遍历的算法。
邻接矩阵存储结构
#include <iostream>
#include <vector>
const int MAX_V = 100; // 假设顶点数不超过100
void dfs(int v, std::vector<std::vector<int>>& graph, std::vector<bool>& visited) {
visited[v] = true;
std::cout << v << " "; // 访问顶点
// 遍历所有邻接顶点
for (int i = 0; i < graph[v].size(); ++i) {
int nextV = graph[v][i];
if (!visited[nextV]) {
dfs(nextV, graph, visited);
}
}
}
int main() {
std::vector<std::vector<int>> graph = {
{1, 2},
{0, 3},
{0, 4},
{0},
{1}
};
std::vector<bool> visited(MAX_V, false);
dfs(0, graph, visited); // 从顶点0开始深度优先遍历
return 0;
}
邻接表存储结构
struct AdjListNode {
int dest;
AdjListNode* next;
};
struct AdjList {
AdjListNode* head;
};
struct Graph {
int numVertices;
AdjList* arrays;
};
void addEdge(Graph* graph, int src, int dest) {
AdjListNode* newNode = new AdjListNode{dest, graph->arrays[src].head};
graph->arrays[src].head = newNode;
}
void dfs(Graph* graph, int v, std::vector<bool>& visited) {
visited[v] = true;
std::cout << v << " "; // 访问顶点
AdjListNode* node = graph->arrays[v].head;
while (node) {
int nextV = node->dest;
if (!visited[nextV]) {
dfs(graph, nextV, visited);
}
node = node->next;
}
}
// 广度优先遍历使用队列的示例
void bfs(Graph* graph, int start, std::vector<bool>& visited) {
std::queue<int> queue;
visited[start] = true;
queue.push(start);
while (!queue.empty()) {
int v = queue.front();
queue.pop();
std::cout << v << " "; // 访问顶点
AdjListNode* node = graph->arrays[v].head;
while (node) {
int nextV = node->dest;
if (!visited[nextV]) {
visited[nextV] = true;
queue.push(nextV);
}
node = node->next;
}
}
}