课题摘要:
图(Graph)是一种非常重要的数据结构,用于表示对象之间的复杂关系。与线性结构(如数组、链表)和树形结构不同,图中的元素(称为顶点或节点)之间可以存在任意的连接关系。图在计算机科学、网络分析、人工智能等领域有着广泛的应用。
图(Graph)是一种非常重要的数据结构,用于表示对象之间的复杂关系。与线性结构(如数组、链表)和树形结构不同,图中的元素(称为顶点或节点)之间可以存在任意的连接关系。图在计算机科学、网络分析、人工智能等领域有着广泛的应用。接下来,将详细介绍图的基本概念、表示方法、常见操作以及一些经典的应用场景。
图 ( G ) 由两个集合组成:
形式化表示为 ( G = (V, E) ),其中 ( V ) 是有限非空集合,( E ) 是顶点对的集合。
根据边的性质,图可以分为以下几类:
在C++中,图(Graph)是一种重要的数据结构,用于表示对象之间的关系。图由顶点(Vertices)和边(Edges)组成,顶点表示对象,边表示对象之间的关系。在C++中,图可以通过多种方式表示,常见的表示方法包括邻接矩阵、邻接表和边列表。以下分别介绍这三种表示方法及其优缺点和代码示例。
原理:
邻接矩阵是一个二维数组,用于表示图中顶点之间的连接关系。对于无向图,矩阵是对称的;对于有向图,矩阵不一定对称。矩阵的行和列分别表示图中的顶点,矩阵中的值表示顶点之间的边是否存在,以及边的权重(如果是加权图)。
优点:
缺点:
代码示例(C++):
#include
#include
class Graph {
private:
int V;
std::vector<std::vector<int>> adj_matrix;
public:
Graph(int vertices) : V(vertices), adj_matrix(V, std::vector<int>(V, 0)) {}
void add_edge(int u, int v, int weight = 1) {
adj_matrix[u][v] = weight;
// 如果是无向图,还需要添加以下行:
// adj_matrix[v][u] = weight;
}
void print_matrix() const {
for (const auto& row : adj_matrix) {
for (int val : row) {
std::cout << val << " ";
}
std::cout << std::endl;
}
}
};
int main() {
Graph g(4);
g.add_edge(0, 1, 5);
g.add_edge(0, 2, 3);
g.add_edge(1, 2, 2);
g.add_edge(2, 3, 4);
g.print_matrix();
return 0;
}
原理:
邻接表是一个数组或列表,每个元素对应一个顶点,每个顶点的元素是一个链表或列表,存储与该顶点相邻的顶点及其边的权重(如果是加权图)。
优点:
缺点:
代码示例(C++):
#include
#include
#include
class Graph {
private:
int V;
std::vector<std::list<std::pair<int, int>>> adj_list;
public:
Graph(int vertices) : V(vertices), adj_list(V) {}
void add_edge(int u, int v, int weight = 1) {
adj_list[u].emplace_back(v, weight);
// 如果是无向图,还需要添加以下行:
// adj_list[v].emplace_back(u, weight);
}
void print_list() const {
for (int i = 0; i < V; ++i) {
std::cout << "Vertex " << i << ": ";
for (const auto& edge : adj_list[i]) {
std::cout << "(" << edge.first << ", " << edge.second << ") ";
}
std::cout << std::endl;
}
}
};
int main() {
Graph g(4);
g.add_edge(0, 1, 5);
g.add_edge(0, 2, 3);
g.add_edge(1, 2, 2);
g.add_edge(2, 3, 4);
g.print_list();
return 0;
}
原理:
边列表是一个数组或列表,每个元素表示图中的一条边,包含边的起点、终点和权重(如果是加权图)。
优点:
缺点:
代码示例(C++):
#include
#include
class Graph {
private:
std::vector<std::tuple<int, int, int>> edges;
public:
void add_edge(int u, int v, int weight = 1) {
edges.emplace_back(u, v, weight);
}
void print_edges() const {
for (const auto& edge : edges) {
std::cout << "(" << std::get<0>(edge) << ", " << std::get<1>(edge) << ", " << std::get<2>(edge) << ")" << std::endl;
}
}
};
int main() {
Graph g;
g.add_edge(0, 1, 5);
g.add_edge(0, 2, 3);
g.add_edge(1, 2, 2);
g.add_edge(2, 3, 4);
g.print_edges();
return 0;
}
选择哪种图表示方法取决于具体的应用场景和图的特性:
在实际编程中,可以根据问题的具体需求选择最合适的图表示方法。
图是一种非常灵活且强大的数据结构,能够表示复杂的对象关系。通过不同的表示方法和操作算法,图可以应用于各种实际问题。无论是最短路径、最小生成树,还是网络流、图着色,图论中的经典算法都为解决这些问题提供了高效的解决方案。
图(Graph)的常见操作主要围绕顶点(Vertex)和边(Edge)的管理以及图的遍历展开。以下是图的一些基本操作,按类别进行详细说明:
向图中添加一个新的顶点。
void add_vertex_adj_matrix(std::vector<std::vector<int>>& matrix) {
int n = matrix.size();
std::vector<int> new_row(n + 1, 0); // 新顶点的行
for (auto& row : matrix) {
row.push_back(0); // 在每行末尾添加一个0
}
matrix.push_back(new_row); // 添加新行
}
void add_vertex_adj_list(std::vector<std::list<std::pair<int, int>>>& adj_list) {
adj_list.emplace_back(); // 添加一个空的链表
}
从图中移除一个顶点及其所有相关边。
void remove_vertex_adj_matrix(std::vector<std::vector<int>>& matrix, int vertex) {
matrix.erase(matrix.begin() + vertex); // 删除顶点对应的行
for (auto& row : matrix) {
row.erase(row.begin() + vertex); // 删除每行中对应的列
}
}
void remove_vertex_adj_list(std::vector<std::list<std::pair<int, int>>>& adj_list, int vertex) {
adj_list.erase(adj_list.begin() + vertex); // 删除顶点
for (auto& v : adj_list) {
v.remove_if([vertex](const std::pair<int, int>& p) { return p.first == vertex; }); // 删除所有指向该顶点的边
}
}
在图中添加一条边。
void add_edge_adj_matrix(std::vector<std::vector<int>>& matrix, int u, int v, int weight = 1) {
matrix[u][v] = weight; // 无向图:matrix[v][u] = weight;
}
void add_edge_adj_list(std::vector<std::list<std::pair<int, int>>>& adj_list, int u, int v, int weight = 1) {
adj_list[u].emplace_back(v, weight); // 无向图:adj_list[v].emplace_back(u, weight);
}
从图中移除一条边。
void remove_edge_adj_matrix(std::vector<std::vector<int>>& matrix, int u, int v) {
matrix[u][v] = 0; // 无向图:matrix[v][u] = 0;
}
void remove_edge_adj_list(std::vector<std::list<std::pair<int, int>>>& adj_list, int u, int v) {
adj_list[u].remove_if([v](const std::pair<int, int>& p) { return p.first == v; }); // 无向图:adj_list[v].remove_if([u](const std::pair& p) { return p.first == u; });
}
从某个顶点开始,尽可能深入地访问顶点,直到无法继续为止,然后回溯。
#include
#include
#include
#include
void dfs(const std::vector<std::list<int>>& graph, int start) {
std::unordered_set<int> visited;
std::stack<int> stack;
stack.push(start);
while (!stack.empty()) {
int vertex = stack.top();
stack.pop();
if (visited.find(vertex) == visited.end()) {
visited.insert(vertex);
std::cout << vertex << " ";
for (auto it = graph[vertex].rbegin(); it != graph[vertex].rend(); ++it) {
if (visited.find(*it) == visited.end()) {
stack.push(*it);
}
}
}
}
}
int main() {
std::vector<std::list<int>> graph = {
{1, 2},
{2, 3},
{3},
{}
};
dfs(graph, 0);
return 0;
}
从某个顶点开始,逐层访问所有顶点。
#include
#include
#include
#include
void bfs(const std::vector<std::list<int>>& graph, int start) {
std::unordered_set<int> visited;
std::queue<int> queue;
queue.push(start);
while (!queue.empty()) {
int vertex = queue.front();
queue.pop();
if (visited.find(vertex) == visited.end()) {
visited.insert(vertex);
std::cout << vertex << " ";
for (int neighbor : graph[vertex]) {
if (visited.find(neighbor) == visited.end()) {
queue.push(neighbor);
}
}
}
}
}
int main() {
std::vector<std::list<int>> graph = {
{1, 2},
{2, 3},
{3},
{}
};
bfs(graph, 0);
return 0;
}
对于无向图,返回与该顶点相连的边的数量;对于有向图,返回入度和出度。
int get_degree_adj_matrix(const std::vector<std::vector<int>>& matrix, int vertex) {
return std::accumulate(matrix[vertex].begin(), matrix[vertex].end(), 0); // 无向图
}
int get_degree_adj_list(const std::vector<std::list<std::pair<int, int>>>& adj_list, int vertex) {
return adj_list[vertex].size(); // 无向图
}
检查两个顶点之间是否存在边。
bool has_edge_adj_matrix(const std::vector<std::vector<int>>& matrix, int u, int v) {
return matrix[u][v] != 0;
}
bool has_edge_adj_list(const std::vector<std::list<std::pair<int, int>>>& adj_list, int u, int v) {
for (const auto& edge : adj_list[u]) {
if (edge.first == v) {
return true;
}
}
return false;
}
返回图中的所有顶点。
std::vector<int> get_vertices_adj_matrix(const std::vector<std::vector<int>>& matrix) {
std::vector<int> vertices;
for (int i = 0; i < matrix.size(); ++i) {
vertices.push_back(i);
}
return vertices;
}
std::vector<int> get_vertices_adj_list(const std::vector<std::list<std::pair<int, int>>>& adj_list) {
std::vector<int> vertices;
for (int i = 0; i < adj_list.size(); ++i) {
vertices.push_back(i);
}
return vertices;
}
返回图中的所有边。
std::vector<std::tuple<int, int, int>> get_edges_adj_matrix(const std::vector<std::vector<int>>& matrix) {
std::vector<std::tuple<int, int, int>> edges;
for (int i = 0; i < matrix.size(); ++i) {
for (int j = 0; j < matrix.size(); ++j) {
if (matrix[i][j] != 0) {
edges.emplace_back(i, j, matrix[i][j]);
}
}
}
return edges;
}
std::vector<std::tuple<int, int, int>> get_edges_adj_list(const std::vector<std::list<std::pair<int, int>>>& adj_list) {
std::vector<std::tuple<int, int, int>> edges;
for (int u = 0; u < adj_list.size(); ++u) {
for (const auto& edge : adj_list[u]) {
edges.emplace_back(u, edge.first, edge.second);
}
}
return edges;
}
计算从一个顶点到另一个顶点的最短路径。
#include
#include
#include
#include
std::vector<int> dijkstra(const std::vector<std::vector<std::pair<int, int>>>& graph, int start) {
int n = graph.size();
std::vector<int> distances(n, std::numeric_limits<int>::max());
distances[start] = 0;
std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, std::greater<>> pq;
pq.push({0, start});
while (!pq.empty()) {
int current_distance = pq.top().first;
int current_vertex = pq.top().second;
pq.pop();
if (current_distance > distances[current_vertex]) {
continue;
}
for (const auto& edge : graph[current_vertex]) {
int neighbor = edge.first;
int weight = edge.second;
int distance = current_distance + weight;
if (distance < distances[neighbor]) {
distances[neighbor] = distance;
pq.push({distance, neighbor});
}
}
}
return distances;
}
int main() {
std::vector<std::vector<std::pair<int, int>>> graph = {
{{1, 5}, {2, 3}},
{{2, 2}, {3, 4}},
{{3, 4}},
{}
};
std::vector<int> distances = dijkstra(graph, 0);
for (int dist : distances) {
std::cout << dist << " ";
}
return 0;
}
生成图的最小生成树。
#include
#include
#include
#include
std::vector<std::tuple<int, int, int>> prim(const std::vector<std::vector<std::pair<int, int>>>& graph, int start) {
int n = graph.size();
std::vector<bool> visited(n, false);
std::vector<std::tuple<int, int, int>> mst;
std::priority_queue<std::pair<int, std::pair<int, int>>, std::vector<std::pair<int, std::pair<int, int>>>, std::greater<>> pq;
pq.push({0, {start, -1}});
while (!pq.empty()) {
int weight = pq.top().first;
int current = pq.top().second.first;
int parent = pq.top().second.second;
pq.pop();
if (visited[current]) {
continue;
}
visited[current] = true;
if (parent != -1) {
mst.emplace_back(parent, current, weight);
}
for (const auto& edge : graph[current]) {
int neighbor = edge.first;
int edge_weight = edge.second;
if (!visited[neighbor]) {
pq.push({edge_weight, {neighbor, current}});
}
}
}
return mst;
}
int main() {
std::vector<std::vector<std::pair<int, int>>> graph = {
{{1, 5}, {2, 3}},
{{2, 2}, {3, 4}},
{{3, 4}},
{}
};
std::vector<std::tuple<int, int, int>> mst = prim(graph, 0);
for (const auto& edge : mst) {
std::cout << "(" << std::get<0>(edge) << ", " << std::get<1>(edge) << ", " << std::get<2>(edge) << ")" << std::endl;
}
return 0;
}
对有向无环图(DAG)进行拓扑排序。
#include
#include
#include
#include
std::vector<int> topological_sort(const std::vector<std::vector<int>>& graph) {
int n = graph.size();
std::vector<int> in_degree(n, 0);
std::vector<int> topo_order;
for (int u = 0; u < n; ++u) {
for (int v : graph[u]) {
in_degree[v]++;
}
}
std::queue<int> queue;
for (int u = 0; u < n; ++u) {
if (in_degree[u] == 0) {
queue.push(u);
}
}
while (!queue.empty()) {
int u = queue.front();
queue.pop();
topo_order.push_back(u);
for (int v : graph[u]) {
in_degree[v]--;
if (in_degree[v] == 0) {
queue.push(v);
}
}
}
if (topo_order.size() == n) {
return topo_order;
} else {
throw std::runtime_error("Graph has a cycle");
}
}
int main() {
std::vector<std::vector<int>> graph = {
{1, 2},
{2, 3},
{3},
{}
};
try {
std::vector<int> topo_order = topological_sort(graph);
for (int vertex : topo_order) {
std::cout << vertex << " ";
}
} catch (const std::runtime_error& e) {
std::cout << e.what() << std::endl;
}
return 0;
}
图的操作涵盖了从基本的顶点和边的管理到复杂的图算法。通过这些操作,可以实现对图的高效管理和分析,从而解决实际问题中的各种需求。
图(Graph)作为一种强大的数据结构,能够有效表示对象之间的复杂关系,因此在众多领域有着广泛而深刻的应用。以下是图在不同领域的一些典型应用,按应用场景分类详细介绍:
图作为一种强大的数据结构,在计算机科学、交通运输、社交网络、生物医学、金融、项目管理、游戏开发、地理信息系统、电力系统、物流与供应链管理等诸多领域都有着广泛而深刻的应用。通过图结构和图算法,可以有效地表示和分析对象之间的复杂关系,解决实际问题中的各种需求,为各领域的发展提供了重要的支持和保障。