前一篇文章的图的存储结构用的邻接矩阵,这篇使用邻接表。不同的数据结构特点也不同,要根据算法需要选出合适的。
- 图规模比较小时,推荐邻接矩阵;
- 稠密图推荐用邻接矩阵;
- 需要快速判断两点间是否右边,推荐邻接矩阵;
- 稀疏图推荐用邻接表;
继续用这篇文章中的例子,数据结构用邻接表,图和代码如下:
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
struct edge;
struct vertex;
struct vertex {
int id;
edge *first;
};
struct edge {
int weight;
int peer;
edge *next;
};
struct node {
int ver;
int distance; // 距离起点的距离
node *previous;
bool isVisited;
bool operator< (const node &a) const{
return distance > a.distance;
} // for 最小堆
};
#define INFINITY 65536
class Graph {
private:
int vertexNum;
vertex *verArray;
public:
Graph(int vertexNum) {
this->vertexNum = vertexNum;
verArray = new vertex[vertexNum];
for (int i = 0; i < vertexNum; i++) {
verArray[i].id = i;
verArray[i].first = NULL;
}
}
/* 有方向, start->end */
void insertEdge(int start, int end, int weight) {
if (start >= vertexNum || start < 0) {
printf("invalid edge start\n");
return;
}
if (end >= vertexNum || end < 0) {
printf("invalid edge end\n");
return;
}
edge *e = new edge;
e->peer = end;
e->weight = weight;
e->next = NULL;
if (verArray[start].first == NULL) {
verArray[start].first = e;
} else {
edge *p = verArray[start].first;
while(p->next) p++;
p->next = e;
}
}
void printGraph() {
printf("graph base on adjacency list:\n");
for (int i = 0; i < vertexNum; i++) {
printf("vertex: %d", i);
edge *p = verArray[i].first;
while (p) {
printf(" -> %d(weight %d)", p->peer, p->weight);
p = p->next;
}
printf("\n");
}
}
void getShortPath(int start, int end, int &distance, std::vector<int> &path) {
if (start >= vertexNum || start < 0) {
printf("start invalid\n");
return;
}
if (end >= vertexNum || end < 0) {
printf("end invalid\n");
return;
}
node *nodes = new node[vertexNum];
for (int i = 0; i < vertexNum; i++) {
nodes[i].ver = i;
nodes[i].isVisited = false;
nodes[i].distance = INFINITY;
nodes[i].previous = NULL;
}
nodes[start].distance = 0;
std::priority_queue<node> que;
que.push(nodes[start]);
while (!que.empty() && !nodes[end].isVisited) {
// 找出未访问过的离 start 的最近的点
int minDistance = que.top().distance;
int minVertex = que.top().ver;
que.pop();
// 注意点,同一个点可能压进去多次
if (nodes[minVertex].isVisited)
continue;
// 对所有从 minVertex 出发的边进行松弛
nodes[minVertex].isVisited = true;
edge *p = verArray[minVertex].first;
while (p) {
if (!nodes[p->peer].isVisited && nodes[p->peer].distance > minDistance + p->weight) {
nodes[p->peer].distance = minDistance + p->weight;
nodes[p->peer].previous = &nodes[minVertex];
que.push(nodes[p->peer]);
}
p = p->next;
}
}
if (!nodes[end].isVisited)
printf("no path\n");
distance = nodes[end].distance;
node *pnode = &nodes[end];
while (pnode != NULL) {
path.push_back(pnode->ver);
pnode = pnode->previous;
}
std::reverse(path.begin(), path.end());
delete [] nodes;
}
virtual ~Graph() {
for (int i = 0; i < vertexNum; i++) {
edge *p = verArray[i].first;
while (p) {
edge *tmp = p->next;
delete p;
p = tmp;
}
}
delete [] verArray;
}
};
void printPath(int start, int end, int distance, std::vector<int> &path) {
printf("distance from %d to %d is %2d, ", start, end , distance);
printf("path: ");
for (unsigned int i = 0; i < path.size(); i++) {
printf("%d ", path[i]);
}
printf("\n");
}
int main() {
int N = 65536, num = 9;
int graph[9][9] = {
{0, 1, 5, N, N, N, N, N, N},
{1, 0, 3, 7, 5, N, N, N, N},
{5, 3, 0, N, 1, 7, N, N, N},
{N, 7, N, 0, 2, N, 3, N, N},
{N, 5, 1, 2, 0, 3, 6, 9, N},
{N, N, 7, N, 3, 0, N, 5, N},
{N, N, N, 3, 6, N, 0, 2, 7},
{N, N, N, N, 9, 5, 2, 0, 4},
{N, N, N, N, N, N, 7, 4, 0}
};
Graph testGraph(num);
for (int i = 0; i < num; i++) {
for (int j = 0; j < num; j++) {
if (graph[i][j] != 0 && graph[i][j] != N)
testGraph.insertEdge(i, j, graph[i][j]);
}
}
testGraph.printGraph();
printf("\n");
int start = 0, end = 0, distance = 0;
std::vector<int> path;
for (end = 0; end < num; end++) {
path.clear();
testGraph.getShortPath(start, end, distance, path);
printPath(start, end, distance, path);
}
printf("\n");
return 0;
}
运行结果如下:
graph base on adjacency list:
vertex: 0 -> 1(weight 1) -> 2(weight 5)
vertex: 1 -> 0(weight 1) -> 2(weight 3) -> 3(weight 7) -> 4(weight 5)
vertex: 2 -> 0(weight 5) -> 1(weight 3) -> 4(weight 1) -> 5(weight 7)
vertex: 3 -> 1(weight 7) -> 4(weight 2) -> 6(weight 3)
vertex: 4 -> 1(weight 5) -> 2(weight 1) -> 3(weight 2) -> 5(weight 3) -> 6(weight 6) -> 7(weight 9)
vertex: 5 -> 2(weight 7) -> 4(weight 3) -> 7(weight 5)
vertex: 6 -> 3(weight 3) -> 4(weight 6) -> 7(weight 2) -> 8(weight 7)
vertex: 7 -> 4(weight 9) -> 5(weight 5) -> 6(weight 2) -> 8(weight 4)
vertex: 8 -> 6(weight 7) -> 7(weight 4)
distance from 0 to 0 is 0, path: 0
distance from 0 to 1 is 1, path: 0 1
distance from 0 to 2 is 4, path: 0 1 2
distance from 0 to 3 is 7, path: 0 1 2 4 3
distance from 0 to 4 is 5, path: 0 1 2 4
distance from 0 to 5 is 8, path: 0 1 2 4 5
distance from 0 to 6 is 10, path: 0 1 2 4 3 6
distance from 0 to 7 is 12, path: 0 1 2 4 3 6 7
distance from 0 to 8 is 16, path: 0 1 2 4 3 6 7 8
Program ended with exit code: 0