在数据结构中图算是个较为难理解的结构形式了。
大致我们可以分为两个大类:
1、通过数组实现
2、通过链表实现
而链表的实现方式还可以继续细分:邻接表、邻接多重表、十字链表
所以关于图的结构的存储这里我们介绍四种基本形式:
1、邻接矩阵(数组)
2、邻接表(链表)
3、邻接多重表(链表)
4、十字链表(链表)
在C++中,图的存储方式通常有两种:邻接矩阵和邻接表。
-
邻接矩阵:
#include <iostream> #include <vector>using namespace std;const int MAX_VERTICES = 100;class Graph { private:int vertices;vector<vector<int>> adjacencyMatrix;public:Graph(int V) : vertices(V), adjacencyMatrix(V, vector<int>(V, 0)) {}void addEdge(int start, int end) {adjacencyMatrix[start][end] = 1;adjacencyMatrix[end][start] = 1; }void printGraph() {for (int i = 0; i < vertices; ++i) {for (int j = 0; j < vertices; ++j) {cout << adjacencyMatrix[i][j] << " ";}cout << endl;}} };int main() {Graph g(5);g.addEdge(0, 1);g.addEdge(0, 2);g.addEdge(1, 3);g.addEdge(3, 4);g.printGraph();return 0; }
-
邻接表:
#include <iostream> #include <list>using namespace std;class Graph { private:int vertices;list<int> *adjacencyList;public:Graph(int V) : vertices(V), adjacencyList(new list<int>[V]) {}void addEdge(int start, int end) {adjacencyList[start].push_back(end);}void printGraph() {for (int i = 0; i < vertices; ++i) {cout << i << ": ";for (const auto &neighbor : adjacencyList[i]) {cout << neighbor << " ";}cout << endl;}} };int main() {Graph g(5);g.addEdge(0, 1);g.addEdge(0, 2);g.addEdge(1, 3);g.addEdge(3, 4);g.printGraph();return 0; }
在这里,顶点从0到V-1编号。你可以根据需要选择合适的表示方式,邻接矩阵适合稠密图,而邻接表适合稀疏图。
当涉及到图的存储时,除了邻接矩阵和邻接表之外,还有其他一些高级的表示方式,如邻接多重表和十字链表。
3.邻接多重表:
邻接多重表主要用于存储无向图,其中每条边都有一个表结点。每个表结点包含两个指针,分别指向该边的两个顶点,并包含一些边的信息。这种表示方法在处理无向图时更为直观。下面是一个简化的例子:
class Graph {
private:struct EdgeNode {int vertex1, vertex2;EdgeNode* next1;EdgeNode* next2;EdgeNode(int v1, int v2) : vertex1(v1), vertex2(v2), next1(nullptr), next2(nullptr) {}};vector<EdgeNode*> edgeList;public:Graph(int V) : edgeList(V, nullptr) {}void addEdge(int start, int end) {EdgeNode* edge = new EdgeNode(start, end);// Update pointersedge->next1 = edgeList[start];edgeList[start] = edge;edge->next2 = edgeList[end];edgeList[end] = edge;}void printGraph() {for (int i = 0; i < edgeList.size(); ++i) {cout<< i << ": ";EdgeNode* edge = edgeList[i];while (edge != nullptr) {cout << "(" << edge->vertex1 << "," << edge->vertex2 << ") ";edge = edge->next1;}cout << endl;}}
};
4.** 十字链表**:
十字链表适用于有向图,它在邻接表的基础上进一步改进,以有效地表示有向边。每个结点包含两个指针,分别指向入边和出边,以及一些边的信息。下面是一个简化的例子:
class Graph {
private:struct Node {int vertex;Node* next;Node(int v) : vertex(v), next(nullptr) {}};struct ArcNode {int endVertex;ArcNode* nextIn;ArcNode* nextOut;ArcNode(int end) : endVertex(end), nextIn(nullptr), nextOut(nullptr) {}};vector<Node*> nodeList;public:Graph(int V) : nodeList(V, nullptr) {}void addEdge(int start, int end) {ArcNode* arc = new ArcNode(end);arc->nextOut = nodeList[start];nodeList[start] = arc;Node* node = new Node(start);node->next = nodeList[end];nodeList[end] = node;}void printGraph() {for (int i = 0; i < nodeList.size(); ++i) {cout << i << ": ";ArcNode* arcOut = nodeList[i];while (arcOut != nullptr) {cout << arcOut->endVertex << " ";arcOut = arcOut->nextOut;}cout << endl;cout << i << ": ";Node* nodeIn = nodeList[i];while (nodeIn != nullptr) {cout << nodeIn->vertex << " ";nodeIn = nodeIn->next;}cout << endl;}}
};
下面是一些练习题:
1.无向图的连通分量
【问题描述】求解无向图的连通分量。
【输入形式】第一行:顶点数 边数;第二行:顶点;第三行及后面:边(每一行一条边)
【输出形式】分量:顶点集合(顶点按从小到大排序输出)(每个连通分量输出占用一行)
【样例输入】
6 5
ABCDEF
A B
A E
B E
A F
B F
【样例输出】
1:ABEF
2:C
3:D
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <algorithm>using namespace std;class Graph {
private:map<char, vector<char> > adjacencyList;map<char, bool> visited;public:void addEdge(char u, char v) {adjacencyList[u].push_back(v);adjacencyList[v].push_back(u);}void DFS(char vertex, set<char>& component) {visited[vertex] = true;component.insert(vertex);for (vector<char>::iterator it = adjacencyList[vertex].begin(); it != adjacencyList[vertex].end(); ++it) {char neighbor = *it;if (!visited[neighbor]) {DFS(neighbor, component);}}}vector<set<char> > getConnectedComponents() {vector<set<char> > components;visited.clear();for (map<char, vector<char> >::const_iterator it = adjacencyList.begin(); it != adjacencyList.end(); ++it) {char vertex = it->first;if (!visited[vertex]) {set<char> component;DFS(vertex, component);components.push_back(component);}}return components;}
};int main() {int vertices, edges;cin >> vertices >> edges;Graph graph;string verticesStr;cin >> verticesStr;for (string::iterator it = verticesStr.begin(); it != verticesStr.end(); ++it) {char vertex = *it;graph.addEdge(vertex, vertex);}for (int i = 0; i < edges; ++i) {char u, v;cin >> u >> v;graph.addEdge(u, v);}vector<set<char> > components = graph.getConnectedComponents();for (int i = 0; i < components.size(); ++i) {set<char> component = components[i];cout << i + 1 << ":";for (set<char>::iterator it = component.begin(); it != component.end(); ++it) {cout << *it;}cout << endl;}return 0;
}
- 无向图顶点的度
【问题描述】已知一个无向图,求解该无向图中顶点的度。输入:无向图的顶点数及边数,各顶点及边,某顶点;输出:该顶点的度。
【输入形式】第一行:顶点数、边数,第二行:顶点;第三行开始:边(一条边占用一行),最后一行:顶点(求该顶点的度)
6 8
ABCDEF
A B
A C
A D
B C
B D
C D
C E
E F
A
【输出形式】3
#include <iostream>
#include <vector>
#include <map>using namespace std;// 计算指定顶点的度数
int calculateDegree(int vertex, const vector<vector<bool> > &adjacencyMatrix)
{int degree = 0;for (size_t i = 0; i < adjacencyMatrix[vertex].size(); ++i){if (adjacencyMatrix[vertex][i]){degree++;}}return degree;
}int main()
{int vertices, edges;cin >> vertices >> edges;// 构建顶点映射表map<char, int> vertexMap;vector<char> vertexList(vertices);for (int i = 0; i < vertices; ++i){cin >> vertexList[i];vertexMap[vertexList[i]] = i;}// 构建邻接矩阵vector<vector<bool> > adjacencyMatrix(vertices, vector<bool>(vertices, false));for (int i = 0; i < edges; ++i){char start, end;cin >> start >> end;adjacencyMatrix[vertexMap[start]][vertexMap[end]] = true;adjacencyMatrix[vertexMap[end]][vertexMap[start]] = true;}// 输入要查询的顶点char queryVertex;cin >> queryVertex;// 计算顶点度数并输出int degree = calculateDegree(vertexMap[queryVertex], adjacencyMatrix);cout << degree << endl;return 0;
}
- 图的存储结构
【问题描述】已知无向图的邻接矩阵存储结构,构造该图的邻接表存储结构。其中函数createMGraph用于创建无向图的邻接矩阵存储结构;函数createALGraph(ALGraph &G,MGraph G1)根据无向图的邻接矩阵存储结构G1,构建图的链接表存储结构G;函数printAdjustVex实现遍历邻接表,打印各顶点及邻接点。请根据上下文,将程序补充完整。
【输入形式】第一行:顶点数n和边数e;第一行:顶点序列;接着的e行:边依附的两个顶点在顶点数组中的索引
【输出形式】n行。每一行内容为:顶点:邻接点序列
【样例输入】
6 5
ABCDEF
0 1
0 4
1 4
0 5
1 5
【样例输出】
A:BEF
B:AEF
C:
D:
E:AB
F:AB
#include <iostream>
using namespace std;
#define MaxVexNum 20 //最大顶点数设为20
struct MGraph
{char vexs[MaxVexNum]; //顶点表int arcs[MaxVexNum][MaxVexNum]; //邻接矩阵int vexnum,arcnum; //图中顶点数和边数
}; //MGragh是以邻接矩阵存储的图类型
struct ArcNode
{int adjvex; //邻接点域struct ArcNode * nextarc; //指向下一个邻接点的指针域
} ; //可增加一个数据域info表示边或弧的信息
struct VNode
{char data; //顶点信息ArcNode * firstarc; //边表头指针
};
struct ALGraph
{VNode vertices[MaxVexNum];int vexnum,arcnum; //顶点数和边数
} ;
void createMGraph(MGraph &G)
{int u,v;cin>>G.vexnum>>G.arcnum;for(int i=0; i<G.vexnum; i++)cin>>G.vexs[i];for(int i=0; i<G.vexnum; i++)for(int j=0; j<G.vexnum; j++)G.arcs[i][j]=0;for(int i=1; i<=G.arcnum; i++){cin>>u>>v;G.arcs[u][v]=1;G.arcs[v][u]=1;}
}
void createALGraph(ALGraph &G, MGraph G1)
{G.vexnum = G1.vexnum;G.arcnum = G1.arcnum;for (int i = 0; i < G1.vexnum; i++){G.vertices[i].data = G1.vexs[i];G.vertices[i].firstarc = NULL;}for (int i = G1.vexnum - 1; i >= 0; i--){for (int j = G1.vexnum - 1; j >= 0; j--){if (G1.arcs[i][j] == 1){ArcNode *newArc = new ArcNode;newArc->adjvex = j;newArc->nextarc = G.vertices[i].firstarc;G.vertices[i].firstarc = newArc;}}}
}void printAdjustVex(ALGraph G)
{for(int i=0;i<G.vexnum;i++){cout<<G.vertices[i].data<<":";ArcNode *p=G.vertices[i].firstarc;while(p){cout<<G.vertices[p->adjvex].data;p=p->nextarc;}cout<<endl;}}
int main()
{MGraph G;ALGraph G1;createMGraph(G);createALGraph(G1,G);printAdjustVex(G1);
}