C++ for C Programmers 这门课讲了图论中三个重要的算法: Kruskal's Minimum Spanning Tree, Prim's Minimum Spanning Tree, Dijkstra's Shortest Path.
这里把三个算法实现后作为成员函数写在一个图的类里,图是用邻接矩阵存储的,支持随机生成。
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <fstream>
#include <string>
#include <climits>
using namespace std;
//Enumeration type: D for directed; W for weighted
enum GraphKind { DG, WDG, UDG, WUDG };
class MatrixGraph
{
private:
double **edge; //adjacency matrix
string *vertexlist; //storing the keywords of the vertexes
int vexnum; //number of vertexes
int edgenum; //number of edges
GraphKind kind; //identifier of the graph
const double UNCONNECTED = DBL_MAX;
struct arc //used in Kruskal()
{
int head, end;
double weight;
};
void quickSort_by_weight(arc a[], int i, int j) //Quichsort function modulated for arc
{
if (i<j)
{
int f = i, l = j;
arc k;
k.weight = a[i].weight;
k.head = a[i].head;
k.end = a[i].end;
while (i < j)
{
while (a[j].weight >= k.weight && i < j) j--;
if (i < j)
{
a[i].weight = a[j].weight;
a[i].head = a[j].head;
a[i].end = a[j].end;
i++;
}
while (a[i].weight <= k.weight && i < j) i++;
if (i < j)
{
a[j].weight = a[i].weight;
a[j].head = a[i].head;
a[j].end = a[i].end;
j--;
}
}
a[i].weight = k.weight;
a[i].head = k.head;
a[i].end = k.end;
quickSort_by_weight(a, f, i - 1);
quickSort_by_weight(a, i + 1, l);
}
}
double prob()
{
return (static_cast<double>(rand()) / RAND_MAX);
}
public:
MatrixGraph(int vexNum, GraphKind graphkind) : vexnum(vexNum), edgenum(0), kind(graphkind)
{
vertexlist = new string[vexnum];
edge = new double*[vexnum];
for (int i = 0; i < vexnum; i++)
edge[i] = new double[vexnum];
for (int i = 0; i < vexnum; i++)
for (int j = 0; j < vexnum; j++)
edge[i][j] = UNCONNECTED;
}
MatrixGraph& operator=(MatrixGraph graph)
{
for (int i = 0; i < vexnum; i++)
vertexlist[i] = graph.vertexlist[i];
for (int i = 0; i < vexnum; i++)
for (int j = 0; j < vexnum; j++)
edge[i][j] = graph.edge[i][j];
return *this;
}
void Create()
{
switch (kind)
{
case DG:
{
CreateDG();
break;
}
case WDG:
{
CreateWDG();
break;
}
case UDG:
{
CreateUDG();
break;
}
case WUDG:
{
CreateWUDG();
break;
}
default:
return;
}
}
void RandomCreate()
{
switch (kind)
{
case DG:
{
RandomCreateDG();
break;
}
case WDG:
{
RandomCreateWDG();
break;
}
case UDG:
{
RandomCreateUDG();
break;
}
case WUDG:
{
RandomCreateWUDG();
break;
}
default:
return;
}
}
void RandomCreateDG()
{
Init();
double density;
srand(time(NULL));
cout << "graph density?" << endl;
cin >> density;
for (int i = 0; i < vexnum; i++)
for (int j = 0; j < vexnum; j++)
{
if (i != j && prob() < density)
{
edge[i][j] = true;
edgenum++;
}
}
}
void RandomCreateWDG()
{
Init();
double density;
double upp;
srand(time(NULL));
cout << "graph density [0,1] ?" << endl;
cin >> density;
cout << "the upper bound of edge cost?" << endl;
cin >> upp;
for (int i = 0; i < vexnum; i++)
for (int j = 0; j < vexnum; j++)
{
if (i != j && prob() < density)
{
edge[i][j] = prob()*upp;
edgenum++;
}
}
}
void RandomCreateUDG()
{
Init();
double density;
srand(time(NULL));
cout << "graph density [0,1] ?" << endl;
cin >> density;
for (int i = 0; i < vexnum; i++)
for (int j = i; j < vexnum; j++)
{
if (i != j && prob() < density)
{
edge[i][j] = edge[j][i] = true;
edgenum += 2;
}
}
}
void RandomCreateWUDG()
{
Init();
double density;
double upp;
srand(time(NULL));
cout << "graph density [0,1] ?" << endl;
cin >> density;
cout << "the upper bound of edge cost?" << endl;
cin >> upp;
for (int i = 0; i < vexnum; i++)
for (int j = i; j < vexnum; j++)
{
if (i != j && prob() < density)
{
edge[i][j] = edge[j][i] = prob()*upp;
edgenum += 2;
}
}
}
void Init() //initializer of the graph
{
cout << "Enter the keyword for each of the " << vexnum << " vertex(es):" << endl;
for (int i = 0; i < vexnum; i++)
cin >> vertexlist[i];
}
void CreateDG() //Directed Graph
{
Init();
int vhead, vtail;
cout << "Describe each edge. You can enter a single '/' to quit." << endl;
while ((cout << "Enter the serial number of the head, and that of the terminal:" << endl) && (cin >> vhead >> vtail))
{
edgenum++;
edge[vhead][vtail] = 1;
}
}
void CreateWDG() //weighted directed graph
{
Init();
int vhead, vtail;
double w;
cout << "Describe each edge. You can enter a single '/' to quit." << endl;
while ((cout << "Enter the serial number of the head, and that of the terminal, and then enter the weight:" << endl) && (cin >> vhead >> vtail >> w))
{
edgenum++;
edge[vhead][vtail] = w;
}
}
void CreateUDG() //undirected graph
{
Init();
int vhead, vtail;
cout << "Describe each edge. You can enter a single '/' to quit." << endl;
while ((cout << "Enter the serial number of the head, and that of the terminal:" << endl) && (cin >> vhead >> vtail))
{
edgenum += 2;
edge[vhead][vtail] = edge[vtail][vhead] = 1;
}
}
void CreateWUDG() //weighted undirected graph
{
Init();
int vhead, vtail;
double w;
cout << "Describe each edge. You can enter a single '/' to quit." << endl;
while ((cout << "Enter the serial number of the head, and that of the terminal, and then enter the weight:" << endl) && (cin >> vhead >> vtail >> w))
{
edgenum++;
edge[vhead][vtail] = edge[vtail][vhead] = w;
}
}
void displayGraph()
{
cout << vexnum << " vertex(es) in total." << endl;
cout << edgenum << " edge(s) in total." << endl;
for (int i = 0; i < vexnum; i++)
{
cout << "Vertex " << i + 1 << " is: " << vertexlist[i] << endl;
for (int j = 0; j < vexnum; j++)
if (edge[i][j] != UNCONNECTED)
cout << " " << "- Vertex " << vertexlist[j] << " is connected with edge value of " << edge[i][j] << endl;
}
}
double getEdge(int i, int j)
{
return edge[i][j];
}
string getVertex(int i)
{
return vertexlist[i];
}
GraphKind getGraphkind()
{
return kind;
}
MatrixGraph Prim(int u) //u is the serial number(starting with 0) of the starting point
{
MatrixGraph MST(vexnum, kind);
if (kind != WUDG)
{
cout << "This is not a weighted undirected graph!" << endl;
return MST;
}
for (int i = 0; i < vexnum; i++)
{
int flag = 0;
for (int j = 0; j < vexnum; j++)
if (edge[i][j] == UNCONNECTED)
flag++;
if (flag == (vexnum - 1))
{
cout << "This is not a connected graph!" << endl;
return MST;
}
}
for (int i = 0; i < vexnum; i++)
if (edge[i][i] != UNCONNECTED)
{
cout << "Cannot have loops!" << endl;
return MST;
}
for (int i = 0; i < vexnum; i++)
MST.vertexlist[i] = vertexlist[i];
struct shortcut {
int idx;
double leastcost;
};
shortcut *closedge; //closedge[i].leastcost stores the leastcost edge to the tree(initialed with u) of vertex i
closedge = new shortcut[vexnum]; //closedge[i].idx stores the index of the vertex which the edge above (starting with vi) depends on
for (int i = 0; i < vexnum; i++)
if (i != u) closedge[i] = { u,edge[u][i] }; //initialize the closedge array
closedge[u].leastcost = 0; //close the initial vertex 'u'
closedge[u].idx = -1; //identify the initial vertex
for (int i = 1; i < vexnum; i++)
{
int k = 0;
for (k; k < vexnum; k++)
if (closedge[k].leastcost != 0) break; //find a 'k' which indexes a vertex not in the closed set
double min = closedge[k].leastcost;
for (int j = 0; j < vexnum; j++)
if (closedge[j].leastcost < min && closedge[j].leastcost > 0)
{
min = closedge[j].leastcost;
k = j; //make sure 'k' indexes the vertex with the least cost to link the tree
}
closedge[k].leastcost = 0; //close vertex 'k'
for (int j = 0; j < vexnum; j++)
if (edge[k][j] < closedge[j].leastcost)
closedge[j] = { k,edge[k][j] }; //include 'k' and its leastcost edge into the Minimum Spanning Tree
} //After every inclusion of k, closedge is still directing to a minimum spanning tree.So MST is also one.
for (int i = 0; i < vexnum; i++) //update the closedge(update is needed only when the newly included vertex brings a shorter edge, so just check this)
if (i != u) MST.edge[closedge[i].idx][i] = MST.edge[i][closedge[i].idx] = edge[closedge[i].idx][i];
delete[] closedge;
MST.edgenum = vexnum - 1;
return MST; //MST generated with greedy manner.
}
MatrixGraph Kruskal()
{
MatrixGraph MST(vexnum, kind);
if (kind != WUDG)
{
cout << "This is not a weighted undirected graph!" << endl;
return MST;
}
for (int i = 0; i < vexnum; i++)
{
int flag = 0;
for (int j = 0; j < vexnum; j++)
if (edge[i][j] == UNCONNECTED)
flag++;
if (flag == (vexnum - 1))
{
cout << "This is not a connected graph!" << endl;
return MST;
}
}
for (int i = 0; i < vexnum; i++)
if (edge[i][i] != UNCONNECTED)
{
cout << "Cannot have loops!" << endl;
return MST;
}
for (int i = 0; i < vexnum; i++)
MST.vertexlist[i] = vertexlist[i];
arc *arclist;
arclist = new arc[edgenum];
int *forestlist;
forestlist = new int[vexnum]; //used for vertex[i] to identify to which tree it belongs so as to avoid including an edge of the same tree
for (int i = 0; i < vexnum; i++)
forestlist[i] = i; //initializing each vertex as a independent tree with the serial number of itself
int c = 0;
for (int i = 0; i < vexnum; i++)
for (int j = i + 1; j < vexnum; j++)
{
if (edge[i][j] < UNCONNECTED)
{
arclist[c].head = i;
arclist[c].end = j;
arclist[c].weight = edge[i][j];
c++;
}
}
quickSort_by_weight(arclist, 0, edgenum - 1);
for (int i = 0; i < edgenum; i++)
{
if (forestlist[arclist[i].head] != forestlist[arclist[i].end]) //not in the same tree
{
MST.edge[arclist[i].head][arclist[i].end] = MST.edge[arclist[i].end][arclist[i].head] = arclist[i].weight;
for (int j = 0; j < vexnum; j++)
if (forestlist[j] == forestlist[arclist[i].end] && j != arclist[i].end)
forestlist[j] = forestlist[arclist[i].head];
forestlist[arclist[i].end] = forestlist[arclist[i].head]; //synthesize the tree to which the end vertex belongs into the tree of the head
}
}
MST.edgenum = vexnum - 1;
delete[] forestlist;
delete[] arclist;
return MST;
}
MatrixGraph Dijkstra(int head, int terminal)
{
MatrixGraph SP(vexnum, kind);
if (kind != WUDG)
{
cout << "This is not a weighted undirected graph!" << endl;
return SP;
}
for (int i = 0; i < vexnum; i++)
{
int flag = 0;
for (int j = 0; j < vexnum; j++)
if (edge[i][j] == UNCONNECTED)
flag++;
if (flag == (vexnum - 1))
{
cout << "This is not a connected graph!" << endl;
return SP;
}
}
for (int i = 0; i < vexnum; i++)
if (edge[i][i] != UNCONNECTED)
{
cout << "Cannot have loops!" << endl;
return SP;
}
double *path = new double[vexnum];
int *closed = new int[vexnum];
int *prev = new int[vexnum];
for (int i = 0; i < vexnum; i++)
{
SP.vertexlist[i] = vertexlist[i];
closed[i] = 0; //initialize every vertex as unclosed
path[i] = edge[head][i]; //initialize path[],which stores the minimum cost from vertex i to vertex head
if (path[i] == UNCONNECTED)
prev[i] = -1;
else
prev[i] = head;
}
closed[head] = 1; //close the head vertex
do {
double min = UNCONNECTED;
int tmp = head;
for (int i = 0; i < vexnum; i++)
{
if (closed[i] == 0 && min > path[i])
{
min = path[i];
tmp = i;
}
}
closed[tmp] = 1;
for (int i = 0; i < vexnum; i++)
{
if (closed[i] == 0 && edge[tmp][i] != UNCONNECTED && (path[tmp] + edge[tmp][i]) < path[i])
{
path[i] = edge[tmp][i] + path[tmp];
prev[i] = tmp;
}
}
} while (closed[terminal] == 0);
int i = terminal, j = 0;
while (prev[i] != -1)
{
SP.edge[i][prev[i]] = SP.edge[prev[i]][i] = edge[i][prev[i]];
i = prev[i];
j++;
}
SP.edgenum = j;
delete[] path;
delete[] closed;
delete[] prev;
return SP;
}
};
int main()
{
MatrixGraph graph(6, WUDG);
graph.RandomCreate();
MatrixGraph SP = graph.Prim(0);
graph.displayGraph();
SP.displayGraph();
return 0;
}