实验三 图的操作与实现
一、实验目的
1、图的邻接表和邻接矩阵存储
2、图的各种遍历算法实现
3、最小生成树的算法实现
4、最短路径的算法实现
二、实验类型:验证性
三、实验学时:4学时
四、实验教学的重点和难点
重点:图的基本操作和遍历
难点:邻接表和邻接矩阵的操作
五、实验内容:
利用图的邻接表或邻接矩阵存储结构设计并实现各种操作算法(任选一种存储结构来实现算法)。
1、图的邻接表和邻接矩阵存储
建立下图的邻接表或邻接矩阵,并输出之。
2、图的各种遍历算法实现
以0结点为起点实现上述图的深度优先和广度优先遍历算法。
3、最小生成树的算法实现
利用普里姆(Prim)算法或克鲁斯卡尔(Kruskal)算法求上图的最小生成树,算法实现代码必须有注释。
4、最短路径的算法实现
利用狄克斯特拉(Dijkstra)算法求上图中0结点到其它结点的最短路径,算法实现代码必须有注释。
#include<iostream>
#include<iomanip>
#include<cstdio>
// #include<queue>
using namespace std;
int graph[1000][1000];
int vis[1000], pre[1000],dis[1000];
const int inf = 0x3f3f3f3f;
struct Edge {
int u, v, w;
}edge[1000];
int n, m, cnt;
struct QueueNode {
public:
int data;
QueueNode() { this->next = NULL; }
QueueNode* next;
};
struct Queue {
public:
QueueNode* begin, * end; //设置头指针begin,尾指针end,左开右闭,移动end指针存储数据
Queue() {
//定义构造函数,初始化指针begin,尾指针end
begin = new QueueNode;
end = begin;
end->next = NULL;
}
//返回第一个节点的数据
int Front() {
return begin->next->data;
}
//返回最后一个节点的数据
int Back() {
return end->data;
}
//判断队列是否为空,由于初始化时,从首指针begin的下一个节点开始存储数据,只需判断头尾是否相等就可以判断是否为空
bool IsEmpty() {
return begin == end ? 1 : 0;
}
//往队列中添加新节点数据
void Push(int data) {
QueueNode* tempNode = new QueueNode();
tempNode->data = data;
end->next = tempNode;
end = tempNode;
end->next = NULL;
}
//弹出队列首个节点
int Pop() {
if (IsEmpty())return -1;
QueueNode* tempNode = this->begin->next;
//移动队列中第一个有效的节点位置
this->begin->next = this->begin->next->next;
//判断该节点是否是唯一个的节点,若是则弹出后队列为空,尾指针end=头指针begin
if (tempNode == this->end) this->end = this->begin;
return tempNode->data;
}
};
void ShowGraph() {
cout << "输出原图:" << endl;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (graph[i][j] != inf) {
cout << i << "->" << j << "的权值为:" << graph[i][j] << endl;
}
}
}
}
//图的深度优先
void dfs(int u,int fa) {
vis[u] = 1;
cout << u << " ";
for (int i = 0; i < n; i++) {
if (i != u && i != fa && !vis[i] && graph[u][i] != inf) {
dfs(i, u);
}
}
}
void bfs(int x) {
Queue que;
que.Push(x);
while (!que.IsEmpty())
{
int u = que.Front(); que.Pop();
if (vis[u])continue;
vis[u] = 1;
cout << u << " ";
for (int i = 0; i < n; i++) {
if (i != u && !vis[i] && graph[u][i] != inf) {
que.Push(i);
}
}
}
}
//按照边权值从小到大排序
void sort(Edge edge[],int m) {
for (int i = 0; i < m; i++) {
int k = i;
for (int j = i + 1; j < m; j++) {
if (edge[k].w > edge[j].w) {
k = j;
}
}
if (k != i) {
Edge temp = edge[i];
edge[i] = edge[k];
edge[k] = temp;
}
}
}
//并查集
int Find(int x) {
return pre[x] == x ? x : pre[x] = Find(pre[x]);
}
//Kruskal算法求最小生成树
int Kruskal(int n, int m) {
for (int i = 0; i < n; i++) {
pre[i] = i;
vis[i] = 0;
}
int tmpcnt = 0, cost = 0;
sort(edge, m);
for (int i = 0; i < m; i++) {
int fu = Find(edge[i].u), fv = Find(edge[i].v);
if (fu != fv) {
pre[fv] = fu;
cost += edge[i].w;
tmpcnt++;
if (tmpcnt == n - 1)break;
}
}
return !cost ? -1 : cost;
}
void Dijkstra(int x) {
for (int i = 0; i < n; i++) {
dis[i] = 0x3f3f3f3f;
vis[i] = 0;
}
int start = x;
dis[start] = 0;
for (int i = 0; i < n; i++) {
if (graph[start][i] != inf) {
if (dis[i] > graph[start][i]) {
dis[i] = graph[start][i];
}
}
}
for (int i = 0; i < n-1; i++) {
int min = 0x3f3f3f3f;
for (int j = 0; j < n; j++) {
if (!vis[j] && dis[j] < min) {
min = dis[j];
start = j;
}
}
vis[start] = 1;
for (int k = 0; k < n; k++) {
if (dis[k] > dis[start] + graph[start][k]) {
dis[k] = dis[start] + graph[start][k];
}
}
}
}
int main() {
memset(graph, inf, sizeof(graph));
cout << "输入非负无向图的点数n(下标从0开始)和边数m:" << endl;
// 7 9
cin >> n >> m;
cout << "输入m条边的各个两顶点和权值:" << endl;
/*
0 1 28
0 5 10
1 2 16
1 6 14
2 3 12
3 4 22
3 6 18
4 5 25
4 6 24
*/
for (int i = 0; i < m; i++) {
int u, v, w; cin >> u >> v >> w;
graph[u][v] = w;
graph[v][u] = w;
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt++].w = w;
}
ShowGraph();
memset(vis, 0, sizeof(vis));
cout << "图的深度优先:";
dfs(0,-1);
cout << endl;
memset(vis, 0, sizeof(vis));
cout << "图的广度优先:";
bfs(0);
cout << endl;
cout << "输出最小生成树权值:" << Kruskal(n, m) << endl;
cout << "输出图中 0 结点到其它结点的最短路径:";
Dijkstra(0);
for (int i = 1; i < n; i++)cout << dis[i] << " ";
return 0;
}