邻接表的建立
#include<iostream>
#include<algorithm>
using namespace std;
const int MAX = 20;
typedef char datatype;
//边表结点
struct ArcNode
{
int adjvex;//结点下标
int weight;//权重
ArcNode* next;//下一个表结点
};
//头结点
struct VexNode
{
datatype data;//头结点数据
ArcNode* firstedge;//与该头结点相邻的第一条边
};
//邻接表图
typedef struct Graph
{
int numVertexes;//顶点数
int numEdges;//边数
VexNode* vnode;//顶点表
}*GraphAdjList;//图指针
struct Path//存储起点到所有临边的权值
{
int start;
int end;
int weight;
};
Path* myPath;//全局数组保存输入的值
创建邻接表图
有头插法和尾插法两种,这里注释了尾插法。
默认为无向图,可自行注释为有向图
//创建邻接表
void createGraph(GraphAdjList& g)
{
g = new Graph;
cout << "输入顶点数与边数:\n";
cin >> g->numVertexes >> g->numEdges;
myPath = new Path[g->numEdges];
g->vnode = new VexNode[g->numVertexes];
cout << "输入顶点数据:\n";
for (int i = 0; i < g->numVertexes; i++)
{
cin >> g->vnode[i].data;
g->vnode[i].firstedge = nullptr;
}
cout << "请输入每条边之间的顶点编号(顶点编号从0开始),以及该边的权重:\n";
int start, end, weight;
for (int i = 0; i < g->numEdges; i++)
{
cin >> start >> end >> weight;
myPath[i].start = start;
myPath[i].end = end;
myPath[i].weight = weight;
ArcNode* p = new ArcNode;//头插法
p->adjvex = end;
p->next = g->vnode[start].firstedge;
p->weight = weight;
g->vnode[start].firstedge = p;
//无向图对称
ArcNode* q = new ArcNode;
q->adjvex = start;
q->next = g->vnode[end].firstedge;
q->weight = weight;
g->vnode[end].firstedge = q;
//if (start >= 0 && end >= 0)//尾插法
//{
// ArcNode* h = g->vnode[start].firstedge;
// ArcNode* p = new ArcNode;
// p->adjvex = end;
// p->weight = weight;
// p->next = nullptr;
// if (!h)
// {
// g->vnode[start].firstedge = p;
// }
// else
// {
// while (h->next)//找到链表最后一个元素
// {
// h = h->next;
// }
// h->next = p;
// }
// //无向图对称
// ArcNode* hh = g->vnode[end].firstedge;
// ArcNode* q = new ArcNode;
// q->adjvex = start;
// q->weight = weight;
// q->next = nullptr;
// if (!hh)
// {
// g->vnode[end].firstedge = q;
// }
// else
// {
// while (hh->next)//找到链表最后一个元素
// {
// hh = hh->next;
// }
// hh->next = q;
// }
//}
}
}
DFS遍历
对于连通图,只需要第一个函数即可,第二个函数是为了解决非连通图
void DFS(GraphAdjList g, int* visited, int adjvex)
{
if (!g)
return;
visited[adjvex] = 1;
cout << g->vnode[adjvex].data<<" ";
ArcNode* p = g->vnode[adjvex].firstedge;//获取当前顶点指针
while (p)
{
if (!visited[p->adjvex])//未访问则递归
DFS(g, visited, p->adjvex);
p = p->next;//否则到下一条相邻路径
}
}
void DFSGraphAdjList(GraphAdjList g)
{
int* visited = new int[g->numVertexes];
for (int i = 0; i < g->numVertexes; i++)//初始化
visited[i] = 0;
for (int i = 0; i < g->numVertexes; i++)//连通图与非连通图都能全部访问到
if (!visited[i])
DFS(g, visited, i);
}
BFS遍历
对于连通图,只需要第一个函数即可,第二个函数是为了解决非连通图
void BFS(GraphAdjList g, int* visited, int adjvex)
{
if (!g)
return;
int queue[MAX], front = -1, rear = -1;//构造一个循环队列
cout << g->vnode[adjvex].data<<" ";
visited[adjvex] = 1;
rear = (++rear) % MAX;//出队入队都先自加1取模数组最大值,充分利用空间
queue[rear] = adjvex;
while (front != rear)//队列不为空
{
front = (++front) % MAX;//出队保存
int temp = queue[front];
ArcNode* q = g->vnode[temp].firstedge;//获取当前顶点指针
while (q)//非空时循环
{
if (!visited[q->adjvex])//如果未访问
{
visited[q->adjvex] = 1;//标记输出再入队
cout << g->vnode[q->adjvex].data<<" ";
rear = (++rear) % MAX;
queue[rear] = q->adjvex;
}
q = q->next;//否则到下一条相邻路径
}
}
}
void BFSGraphAdjList(GraphAdjList g)
{
int* visited = new int[g->numVertexes];
for (int i = 0; i < g->numVertexes; i++)//初始化
visited[i] = 0;
for (int i = 0; i < g->numVertexes; i++)//连通图与非连通图都能全部访问到
if (!visited[i])
BFS(g, visited, i);
}
Prim最小生成树
void Prim(GraphAdjList g, int begin)
{
Path* path = new Path[g->numVertexes];
for (int i = 0; i < g->numVertexes; i++)
{
path[i].weight = 0x7fffffff;
}
ArcNode* p = g->vnode[begin].firstedge;
while (p)//构建path数组
{
path[p->adjvex].start = begin;
path[p->adjvex].end = p->adjvex;
path[p->adjvex].weight = p->weight;
p = p->next;
}
path[begin].weight = -1;//将起点置为-1表示已添加到集合
int sum = 0;
cout << "\n\nPrim最小生成树:\n";
for (int i = 1; i < g->numVertexes; i++)
{
int min = 0x7fffffff;
int index=0;
for (int j = 0; j < g->numVertexes; j++)//找到权重最小边
if (path[j].weight != -1)//如果未被添加到集合
if (path[j].weight < min)
{
min = path[j].weight;
index = j;
}
//输出最小权重边信息
cout << g->vnode[path[index].start].data << "--->"
<< g->vnode[path[index].end].data << " "
<< path[index].weight << "\n";
//将该边终点添加到集合
sum += path[index].weight;
path[index].weight = -1;
//更新path数组
ArcNode* q = g->vnode[path[index].end].firstedge;
while (q)
{
if (path[q->adjvex].weight > q->weight)
{
path[q->adjvex].start = index;
path[q->adjvex].end = q->adjvex;
path[q->adjvex].weight = q->weight;
}
q = q->next;
}
}
cout << "总权值:" << sum << endl;
}
Kruskal最小生成树
这里需要用到并查集
这里选择使用之前输入的数据
可注释掉自行输入
bool operator < (const Path& a, const Path& b)//直接写cmp函数使用sort函数也可
{
return a.weight < b.weight;
}
int Find(int* p, int f)
{
while (p[f] > 0)
f = p[f];
return f;
}
void Kruskal(GraphAdjList g)
{
int n=g->numVertexes, m=g->numEdges;
int root1, root2;
Path* path = new Path[m];
for (int i = 0; i < m; i++)//复制顶点下标及权重
{
path[i].start = myPath[i].start;
path[i].end = myPath[i].end;
path[i].weight = myPath[i].weight;
}
/*cout << "请输入每条边之间的顶点编号(顶点编号从0开始),以及该边的权重:\n";
for (int i = 0; i < m; i++)
cin >> path[i].start >> path[i].end >> path[i].weight;*/
sort(path, path + m);
int* b=new int[n]; //存放最小生成树的顶点
int sum = 0;
memset(b, 0, sizeof(b));
cout << "\nKruskal最小生成树:\n";
for (int i = 0; i < m; i++)
{
root1 = Find(b, path[i].start);
root2 = Find(b, path[i].end);
if (root1 != root2) //root1==root2代表会形成环
{
sum += path[i].weight;
b[root1] = root2;
cout << g->vnode[path[i].start].data << "--->" << g->vnode[path[i].end].data << " " << path[i].weight << endl;
}
}
cout << "总权值:" << sum << endl;
}
Dijkstra最短路径
void Dijkstra(GraphAdjList g, int v)
{
int n = g->numVertexes;
int* lens=new int[n];//保存最短长度
int* path= new int[n];
int* temp = new int[n];//保存临时最短路径
const int max = ~(1 << 31);//0x7fffffff有符号整型最大值
//初始化
for (int i = 0; i < n; i++)
{
//lens表示节点是否已确定最短路径
//这里用-1表示未确定,确认后用实际的最短路径进行填充。
lens[i] = -1;
temp[i] = max;//初始所有节点不可达
path[i] = -1;//保存源节点到目的节点的路径上,目的节点的前一个节点
}
ArcNode* p = g->vnode[v].firstedge;
//使用源节点的邻接边初始化一部分路径长度以及path
while (p)
{
temp[p->adjvex] = p->weight;
path[p->adjvex] = v;
p = p->next;
}
lens[v] = 0;
path[v] = 0;
//开始主循环,每次求得源点到某个顶点的最短路径,并将此节点加入到已确定最短路径的集合中
for (int i = 1; i < n; i++)
{
int index = -1;
int min = max;
//选择一条当前最短路径
for (int j = 0; j < n; j++)
{
if (lens[j] < 0 && temp[j] < min)
{
min = temp[j];
index = j;
}
}
if (index == -1)//若没有可达路径,则退出
break;
lens[index] = min;//设置节点最短路径为已确定,并将最短路径保存起来
//根据新确定最短路径的节点的邻接边,更新临时最短路径
ArcNode* q = g->vnode[index].firstedge;
while (q)
{
//若更小,则更新
if (lens[q->adjvex] < 0 && min + q->weight < temp[q->adjvex])
{
temp[q->adjvex] = min + q->weight;
path[q->adjvex] = index;
}
q = q->next;
}
}
cout << "\nDijkstra最短路径:\n";
for (int i = 0; i < n; i++)
cout << g->vnode[v].data <<"--->"<<g->vnode[i].data<<" "<<lens[i] <<endl;
}
拓扑排序
这里用到之前的全局数组myPath来表示有向图
void TopologicalSort(GraphAdjList g)//拓扑排序
{
int count = 0;
int* myStack = new int[g->numVertexes];//创建简单的栈
int top = -1;
int* inPoint = new int[g->numVertexes];//保存各个顶点的入队
for (int i = 0; i < g->numVertexes; i++)
inPoint[i] = 0;
for (int i = 0; i < g->numEdges; i++)//计算所有顶点入度
{
inPoint[myPath[i].end]++;
}
//for (int i = 0; i < g->numVertexes; i++)
// cout << inPoint[i] << " ";
for (int i = 0; i < g->numVertexes; i++)//入度为0入栈
{
if (inPoint[i] == 0)
myStack[++top] = i;
}
cout << "\n拓扑排序:\n";
while (top != -1)//栈不为空
{
int index=myStack[top--];//出栈
cout << g->vnode[index].data<<" ";
count++;
for (int i = 0; i < g->numEdges; i++)//将该出边终点顶点入队减1,减1后为0的入栈
{
if (myPath[i].start == index)
{
inPoint[myPath[i].end]--;
if (inPoint[myPath[i].end] == 0)
myStack[++top] = myPath[i].end;
}
}
}
if (count == g->numVertexes)
cout << "\n能够完成拓扑排序!\n";
else
cout<< "\n不能够完成拓扑排序!\n";
}
打印邻接表
void print(GraphAdjList g)
{
cout << "\n输出邻接表:\n";
for (int i = 0; i < g->numVertexes; i++)
{
cout << g->vnode[i].data << " ";
ArcNode* p = g->vnode[i].firstedge;
while (p)
{
cout << "<" << g->vnode[i].data << "," << g->vnode[p->adjvex].data << "> =" << p->weight << " ";
p = p->next;
}
cout << "^\n";
}
}
主函数
int main()
{
GraphAdjList g;
createGraph(g);//无向图
print(g);
cout << "\n\nDFS:";
DFSGraphAdjList(g);
cout << "\n\nBFS:";
BFSGraphAdjList(g);
Prim(g, 0);//最小生成树仅针对无向连通图,有向图可能不连通,不能求最小生成树
Kruskal(g);
Dijkstra(g, 0);
TopologicalSort(g);//针对有向图,所以需要处理对称的数据,变成有向图
return 0;
}
//测试数据
//6 10
//abcdef
//0 1 6
//0 2 1
//0 3 5
//1 2 5
//1 4 3
//2 3 5
//2 4 6
//2 5 4
//3 5 2
//4 5 6