赞
踩
优点:空间效率高,适合稀疏图。动态性强,可以方便地添加或删除边。
缺点:查找某条边是否存在的时间复杂度较高。
示例:
A: B -> D
B: A -> C -> D
C: B
D: A -> B
A
连接到 B
和 D
,顶点 B
连接到 A
、C
和 D
,以此类推。假设有以下无向图,其中节点 A、B、C、D、E 表示城市,边表示城市之间的道路,权重表示道路的距离。
在邻接表(链式)表示法中,图的边权重是预先给定的,而不是通过某种计算得到的。它们通常是图的定义的一部分,表示从一个顶点到另一个顶点的距离、时间或其他成本。例如,在地图中的路径权重可以表示两个地点之间的距离。
A----3----B
| |
4 2
| |
C----1----D
|
5
|
E
对应的邻接表为:
A -> B(3) -> C(4)
B -> A(3) -> D(2)
C -> A(4) -> D(1) -> E(5)
D -> B(2) -> C(1)
E -> C(5)
e.g. 顶点 A
的邻接表:
A
连接到顶点 B
,边的权重是 3
。A
再连接到顶点 C
,边的权重是 4
。^
表示链表结束。#include <iostream> #include <vector> #include <unordered_map> using namespace std; // 表示图的结构: `Edge` 结构体表示图的边,包括目的顶点 `dest`、边的权重 `weight` 和指向下一条边的指针 `next`。 struct Edge { int dest; // 目的顶点 int weight; // 边的权重 Edge* next; // 指向下一条边的指针 }; // `Vertex` 结构体表示图的顶点,包括顶点数据 `data` 和指向第一个相邻顶点的指针 `first`。 struct Vertex { char data; // 顶点数据 Edge* first; // 指向第一个相邻顶点的指针 }; // 初始化图: `addEdge` 函数用于向图中添加边,每次添加新边时,会将其插入到链表的头部。 void addEdge(Vertex vertices[], int src, int dest, int weight) { Edge* newEdge = new Edge{dest, weight, vertices[src].first}; vertices[src].first = newEdge; } void printGraph(Vertex vertices[], int V) { for (int i = 0; i < V; ++i) { cout << "顶点 " << vertices[i].data << " 的邻接表: "; Edge* edge = vertices[i].first; while (edge) { cout << " -> " << vertices[edge->dest].data << " (权重 " << edge->weight << ")"; edge = edge->next; } cout << endl; } } int main() { const int V = 5; Vertex vertices[V] = {{'A', nullptr}, {'B', nullptr}, {'C', nullptr}, {'D', nullptr}, {'E', nullptr}}; // 根据图添加边 addEdge(vertices, 0, 1, 3); // A -> B addEdge(vertices, 0, 2, 4); // A -> C addEdge(vertices, 1, 0, 3); // B -> A addEdge(vertices, 1, 3, 2); // B -> D addEdge(vertices, 2, 0, 4); // C -> A addEdge(vertices, 2, 3, 1); // C -> D addEdge(vertices, 2, 4, 5); // C -> E addEdge(vertices, 3, 1, 2); // D -> B addEdge(vertices, 3, 2, 1); // D -> C addEdge(vertices, 4, 2, 5); // E -> C printGraph(vertices, V); return 0; }
封装实现:
优点:
#include <iostream> #include <vector> #include <unordered_map> using namespace std; struct Edge { int dest; int weight; Edge* next; }; struct Vertex { char data; Edge* first; }; class Graph { public: // 构造函数,初始化顶点数量和顶点数组 Graph(int vertices) : V(vertices) { vertex_list = new Vertex[V]; for (int i = 0; i < V; ++i) { vertex_list[i].data = 'A' + i; vertex_list[i].first = nullptr; } } // 析构函数,释放所有动态分配的内存 ~Graph() { for (int i = 0; i < V; ++i) { Edge* current = vertex_list[i].first; while (current) { Edge* temp = current; current = current->next; delete temp; } } delete[] vertex_list; } void AddEdge(int src, int dest, int weight) { Edge* newEdge = new Edge{dest, weight, vertex_list[src].first}; vertex_list[src].first = newEdge; } void PrintGraph() const { for (int i = 0; i < V; ++i) { cout << "顶点 " << vertex_list[i].data << " 的邻接表: "; Edge* edge = vertex_list[i].first; while (edge) { cout << " -> " << vertex_list[edge->dest].data << " (权重 " << edge->weight << ")"; edge = edge->next; } cout << endl; } } private: int V; Vertex* vertex_list; }; int main() { Graph g(5); // 根据图添加边 g.AddEdge(0, 1, 3); // A -> B g.AddEdge(0, 2, 4); // A -> C g.AddEdge(1, 0, 3); // B -> A g.AddEdge(1, 3, 2); // B -> D g.AddEdge(2, 0, 4); // C -> A g.AddEdge(2, 3, 1); // C -> D g.AddEdge(2, 4, 5); // C -> E g.AddEdge(3, 1, 2); // D -> B g.AddEdge(3, 2, 1); // D -> C g.AddEdge(4, 2, 5); // E -> C g.PrintGraph(); return 0; }
执行后, 输出如下:
顶点 A 的邻接表: -> C (权重 4) -> B (权重 3) # 对于顶点 `A` 的邻接表:A -> C(4) -> B(3) 或者 A -> B(3) -> C(4) 都是正确的,它们表示的图结构是一样的。关键在于每个顶点的邻接节点及其对应的边权重是否正确记录。
顶点 B 的邻接表: -> D (权重 2) -> A (权重 3)
顶点 C 的邻接表: -> E (权重 5) -> D (权重 1) -> A (权重 4)
顶点 D 的邻接表: -> C (权重 1) -> B (权重 2)
顶点 E 的邻接表: -> C (权重 5)
优点:
#include <iostream> #include <vector> class Graph { public: Graph(int vertices) : vertices_(vertices) { adj_list_.resize(vertices_); } void AddEdge(int u, int v, int weight) { adj_list_[u].emplace_back(v, weight); adj_list_[v].emplace_back(u, weight); // 对于无向图,需要双向添加边 } void PrintAdjList() const { for (int v = 0; v < vertices_; ++v) { std::cout << static_cast<char>('A' + v) << ": "; for (const auto& edge : adj_list_[v]) { std::cout << static_cast<char>('A' + edge.first) << " (权重 " << edge.second << ") "; } std::cout << std::endl; } } private: int vertices_; std::vector<std::vector<std::pair<int, int>>> adj_list_; }; int main() { Graph g(5); // 根据图添加边 g.AddEdge(0, 1, 3); // A -> B g.AddEdge(0, 2, 4); // A -> C g.AddEdge(1, 3, 2); // B -> D g.AddEdge(1, 4, 2); // B -> E g.AddEdge(2, 3, 1); // C -> D g.AddEdge(3, 4, 5); // D -> E g.PrintAdjList(); return 0; }
在邻接表表示法中,链表中顶点的顺序实际上是不重要的。邻接表的主要目的是表示每个顶点的邻接关系以及对应的边权重,因此,顶点的顺序并不会影响图的表示和算法的正确性。
总体来看,第二种封装方式更符合现代 C++ 编程规范,更加推荐。主要原因如下:
当然, 如果你需要对图进行非常细粒度的控制,或者在非常严格的性能要求下,第一种封装方式可能更适合。
假设有以下有向图,其中节点 A、B、C、D、E 表示城市,边表示城市之间的道路,权重表示道路的距离。
A----3---->B
| |
4 2
| |
v v
C<----1----D
|
5
|
v
E
对应的邻接表为:
A -> B(3) -> C(4)
B -> D(2)
C -> E(5)
D -> C(1)
E ->
#include <iostream> #include <vector> class Graph { public: Graph(int vertices) : vertices_(vertices) { adj_list_.resize(vertices_); } void AddEdge(int u, int v, int weight) { adj_list_[u].emplace_back(v, weight); } /* // 对比无向图的, 向图中添加边: void AddEdge(int u, int v, int weight) { adj_list_[u].emplace_back(v, weight); adj_list_[v].emplace_back(u, weight); // 对于无向图,需要双向添加边 } */ void PrintAdjList() const { for (int v = 0; v < vertices_; ++v) { std::cout << static_cast<char>('A' + v) << ": "; for (const auto& edge : adj_list_[v]) { std::cout << static_cast<char>('A' + edge.first) << " (权重 " << edge.second << ") "; } std::cout << std::endl; } } private: int vertices_; std::vector<std::vector<std::pair<int, int>>> adj_list_; }; int main() { Graph g(5); g.AddEdge(0, 1, 3); // A -> B g.AddEdge(0, 2, 4); // A -> C g.AddEdge(1, 3, 2); // B -> D g.AddEdge(3, 2, 1); // D -> C g.AddEdge(2, 4, 5); // C -> E g.PrintAdjList(); return 0; }
有向图表示的邻接表结构和无向图类似,只是边的方向性需要注意。
// **图的遍历(DFS 和 BFS)** #include <iostream> #include <vector> #include <stack> #include <queue> class Graph { public: Graph(int vertices) : vertices_(vertices) { adj_list_.resize(vertices_); } void AddEdge(int u, int v, int weight) { adj_list_[u].emplace_back(v, weight); } void DFS(int start) { std::vector<bool> visited(vertices_, false); std::stack<int> stack; stack.push(start); while (!stack.empty()) { int v = stack.top(); stack.pop(); if (!visited[v]) { std::cout << static_cast<char>('A' + v) << " "; visited[v] = true; } for (const auto& edge : adj_list_[v]) { if (!visited[edge.first]) { stack.push(edge.first); } } } std::cout << std::endl; } void BFS(int start) { std::vector<bool> visited(vertices_, false); std::queue<int> queue; queue.push(start); visited[start] = true; while (!queue.empty()) { int v = queue.front(); queue.pop(); std::cout << static_cast<char>('A' + v) << " "; for (const auto& edge : adj_list_[v]) { if (!visited[edge.first]) { queue.push(edge.first); visited[edge.first] = true; } } } std::cout << std::endl; } void PrintAdjList() const { for (int v = 0; v < vertices_; ++v) { std::cout << static_cast<char>('A' + v) << ": "; for (const auto& edge : adj_list_[v]) { std::cout << static_cast<char>('A' + edge.first) << " (权重 " << edge.second << ") "; } std::cout << std::endl; } } private: int vertices_; std::vector<std::vector<std::pair<int, int>>> adj_list_; }; int main() { Graph g(5); g.AddEdge(0, 1, 3); // A -> B g.AddEdge(0, 2, 4); // A -> C g.AddEdge(1, 3, 2); // B -> D g.AddEdge(3, 2, 1); // D -> C g.AddEdge(2, 4, 5); // C -> E std::cout << "邻接表:" << std::endl; g.PrintAdjList(); std::cout << "DFS 从 A 开始:" << std::endl; g.DFS(0); std::cout << "BFS 从 A 开始:" << std::endl; g.BFS(0); return 0; }
std::vector
)、哈希表(std::unordered_map
)等数据结构来实现,具体选择取决于需求和编程语言。Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。