图的操作
实验内容:
(1)采用邻接矩阵/邻接表建立图;
(2)采用深度优先/广度优先搜索方式遍历图;
(3)编程实现Dijkstra最短路径算法。
1.采用邻接表建立图的代码如下
#include<iostream>
#include<cstring>
#include<malloc.h>
#include <algorithm>
#include <queue>
#define MAX_VERTEX 20
#define GQSIZE 20
#define maxnum 120
#define INF 10000000
using namespace std;
typedef char VertexType; //各个结构体的定义
typedef struct ANode{
int AdjVex;
struct ANode *NextArc;
}ArcNode;
typedef struct VNode{
VertexType Data;
ArcNode *FirstArc;
}VerNode,AdjList[MAX_VERTEX];
typedef struct Graph{
AdjList Vertices; //相当于VerNode Vertices[MAX_VERTEX];
int VerNum, ArcNum;
}ALGraph;
typedef struct Queue{
int Base[GQSIZE];
int front, rear;
}GCQueue;
int LocateVex(ALGraph G, VertexType V)
{
int i = 0;
while (i<G.VerNum && V!=G.Vertices[i].Data)
{
i++;
}
if (i < G.VerNum)
return i;
else
{
return -1;
}
}
void CreateGraph(ALGraph &G) //创建无向图(包括生成相应的邻接表)
{
int VNum, ANum;
VertexType V1, V2;
cout << "请分别输入顶点数和边数:" << endl;
cin >> VNum >> ANum;
G.VerNum = VNum;
G.ArcNum = ANum;
cout << "请输入各顶点:" << endl;
for (int i = 0; i != G.VerNum; i++)
{
cin >> G.Vertices[i].Data;
G.Vertices[i].FirstArc = NULL;
}
for (int j = 0; j != G.ArcNum; j++)
{
cout << "请输入边所在的两顶点:" << endl;
cin >> V1 >> V2;
int k = LocateVex(G, V1);
int l = LocateVex(G, V2);
if (k >= 0 && l >= 0)
{
ArcNode *P = (ArcNode*)malloc(sizeof(ArcNode));
P->AdjVex = l;
P->NextArc = NULL;
if (!G.Vertices[k].FirstArc)
G.Vertices[k].FirstArc = P;
else
{
ArcNode *L = G.Vertices[k].FirstArc;
while (L->NextArc)
{
L = L->NextArc;
}
L->NextArc = P;
}
}
else
cout << "没有这条边!!" << endl;
}
cout << endl;
cout << "输出邻接表:" << endl;
for (int i = 0; i != G.VerNum; i++)
{
cout <<i << G.Vertices[i].Data;
ArcNode *H = G.Vertices[i].FirstArc;
while (H)
{
cout <<"->"<< H->AdjVex;
H = H->NextArc;
}
cout << endl;
}
}
int FirstAdjvex(ALGraph G, int v) //查找第一个邻接点
{
if (G.Vertices[v].FirstArc)
return G.Vertices[v].FirstArc->AdjVex;
else return -1;
}
int NextAdjvex(ALGraph G, int v, int w) //查找下一个邻接点
{
int flag = 0;
ArcNode *P = G.Vertices[v].FirstArc;
while (P)
{
if (P->AdjVex == w)
{
flag = 1;
break;
}
else
P = P->NextArc;
}
if (flag&&P->NextArc)
return P->NextArc->AdjVex;
else return -1;
}
bool Visited[MAX_VERTEX];
void DFS(ALGraph G, int v) //递归的深度优先搜索
{
Visited[v] = true;
cout << G.Vertices[v].Data << " ";
for (int w = FirstAdjvex(G, v); w >= 0; w = NextAdjvex(G, v, w))
{
if (!Visited[w])
DFS(G, w);
}
}
void DFSTraverse(ALGraph G)
{
for (int v = 0; v != G.VerNum; v++) //此处for语句不可和下面for语句合并,这是先前的初始化,放在下面会导致初始化前就会被赋值
{
Visited[v] = false;
}
for (int v = 0; v !=G.VerNum; v++)
{
if (!Visited[v])
DFS(G, v);
}
cout << endl;
}
void BFSTraverse(ALGraph G) //基于队列的广度优先搜索
{
GCQueue Q;
Q.front = Q.rear = 0;
for (int v = 0; v != G.VerNum; v++)
{
Visited[v] = false;
}
for (int v = 0; v != G.VerNum; v++)
{
if (!Visited[v])
{
Visited[v]=true;
cout << G.Vertices[v].Data << " ";
if (((Q.rear + 1) % GQSIZE) == Q.front)
{
cout << "队满,不能入队!!!" << endl;
return;
}
Q.Base[Q.rear] = v;
Q.rear = (Q.rear + 1) % GQSIZE;
while (Q.rear!=Q.front)
{
int u = Q.Base[Q.front];
Q.front = (Q.front + 1) % GQSIZE;
for (int w = FirstAdjvex(G, u); w >= 0; w = NextAdjvex(G, u, w))
{
if (!Visited[w])
{
Visited[w] = true;
cout << G.Vertices[w].Data << " ";
if (((Q.rear + 1) % GQSIZE) == Q.front)
{
cout << "队满,不能入队!!!" << endl;
return;
}
Q.Base[Q.rear] = w;
Q.rear = (Q.rear + 1) % GQSIZE;
}
}
}
}
}
}
int main()
{
ALGraph G;
CreateGraph(G);
cout << "深度优先遍历为:";
DFSTraverse(G);
cout << endl;
cout << "广度优先遍历为:";
BFSTraverse(G);
cout << endl;
return 0;
}
2.采用邻接矩阵建立图的代码如下:
#include <stdio.h>
#define MAX_VERTEX_NUM 20
#define MaxVex 100 //最大顶点数
#define INFINITY 65535 //表示∞
#define TRUE 1
#define FALSE 0
typedef char VertexType; //顶点类型
typedef int EdgeType; //权值类型
typedef int Bool;
Bool visited[MaxVex];
typedef struct {
VertexType vexs[MaxVex]; //顶点数组
EdgeType arc[MaxVex][MaxVex]; //邻接矩阵
int numVertexes, numEdges; //当前图中的结点数以及边数
}MGraph;
//广度优先遍历需要的循环队列
typedef struct {
int data[MaxVex];
int front, rear;
}Queue;
/****************************************/
//队列的相关操作
//初始化
void InitQueue(Queue *Q)
{
Q->front = Q->rear = 0;
}
//入队
void EnQueue(Queue *Q, int e)
{
if ((Q->rear+1)%MaxVex == Q->front)
return ;
Q->data[Q->rear] = e;
Q->rear = (Q->rear+1)%MaxVex;
}
//判空
Bool QueueEmpty(Queue *Q)
{
if (Q->front == Q->rear)
return TRUE;
else
return FALSE;
}
//出队
void DeQueue(Queue *Q, int *e)
{
if (Q->front == Q->rear)
return ;
*e = Q->data[Q->front];
Q->front = (Q->front+1)%MaxVex;
}
/****************************************/
//建立图的邻接矩阵
void CreateMGraph(MGraph *G)
{
int i, j, k, w;
printf("输入顶点数和边数: ");
scanf("%d%d", &G->numVertexes,&G->numEdges);
fflush(stdin);
printf("==============================\n");
printf("输入各个顶点:\n");
for (i=0; i<G->numVertexes; ++i)
{
printf("顶点%d: ",i+1);
scanf("%c", &G->vexs[i]);
fflush(stdin);
}
for (i=0; i<G->numVertexes; ++i)
{
for (j=0; j<G->numVertexes; ++j)
G->arc[i][j] = INFINITY;
}
printf("==============================\n");
for (k=0; k<G->numEdges; ++k)
{
printf("输入边(vi, vj)中的下标i和j和权W: ");
scanf("%d%d%d", &i,&j,&w);
G->arc[i][j] = w;
G->arc[j][i] = G->arc[i][j];
}
}
//输出
void DisMGraph(MGraph *G)
{
int i, j, k;
k = G->numVertexes;
for (i=0; i<k; ++i)
{
for (j=0; j<k; ++j)
{
printf("%5d ", G->arc[i][j]);
}
putchar('\n');
}
}
/****************************************/
//图的深度优先遍历
void DFS(MGraph G, int i)
{
int j;
visited[i] = TRUE;
printf("%c ", G.vexs[i]);
for (j=0; j<G.numVertexes; ++j)
{
if (G.arc[i][j]!=INFINITY && !visited[j])
DFS(G, j);
}
}
void DFSTraverse(MGraph G)
{
int i;
for (i=0; i<G.numVertexes; ++i)
visited[i] = FALSE;
for (i=0; i<G.numVertexes; ++i)
{
if (!visited[i])
DFS(G, i);
}
}
//图的广度优先遍历
void BFSTraverse(MGraph *G)
{
int i, j;
Queue Q;
for (i=0; i<G->numVertexes; ++i)
visited[i] = FALSE;
InitQueue(&Q);
for (i=0; i<G->numVertexes; ++i)
{
if (!visited[i])
{
visited[i] = TRUE;
printf("%c ", G->vexs[i]);
EnQueue(&Q, i);
while (!QueueEmpty(&Q))
{
DeQueue(&Q, &i);
for (j=0; j<G->numVertexes; ++j)
{
if (!visited[j] && G->arc[i][j]!=INFINITY)
{
visited[j] = TRUE;
printf("%c ", G->vexs[j]);
EnQueue(&Q, j);
}
}
}
}
}
}
/****************************************/
//程序入口
int main(){
MGraph G;
CreateMGraph(&G);
printf("\n图的深度优先遍历为: ");
DFSTraverse(G);
printf("\n图的广度优先遍历为: ");
BFSTraverse(&G);
printf("\n");
return 0;
}
3.迪杰特斯拉求最短路径代码如下:
#include <stdio.h>
#include <stdlib.h>
#define INFINITY 10000 /* 假定弧上权值无穷大时为10000 */
#define MAX_VERTEX_NUM 100 /* 图中最大节点数 */
typedef char VexType; /* 顶点类型设置为字符型 */
typedef int weight; /* 弧上权值类型设置为整型 */
typedef struct /* 弧表节点 */
{
VexType vex[MAX_VERTEX_NUM]; /* 图中节点 */
weight edges[MAX_VERTEX_NUM][MAX_VERTEX_NUM];/* 邻接矩阵 */
int vexnum; /* 节点的数目 */
int edgenum; /* 弧的数目 */
}MGraph;
void CreateDG(MGraph * DG); /* 邻接矩阵法创建有向图(directed graph) */
void PrintDG(MGraph DG); /* 邻接矩阵形式输出图DG */
void ShortestPath_Dijkstra(MGraph DG, VexType StartVex);
/* 从节点StartVex开始求最短路径 */
void locateVex(MGraph DG, VexType vex, int * index);
/* 定位节点vex的下标并赋给index */
int main(void)
{
MGraph g;
CreateDG(&g);
printf("------------------------------\n");
printf("vexnum = %d ; edgenum = %d\n", g.vexnum, g.edgenum);
printf("------------------------------\n");
PrintDG(g);
printf("------------------------------\n");
ShortestPath_Dijkstra(g, '0');
return 0;
}
void CreateDG(MGraph * DG)
{
int i = 0, j, k, w; /* w:权值 */
char ch;
printf("请依次输入顶点数、弧数:");
scanf("%d %d", &(DG->vexnum), &(DG->edgenum));
printf("请依次输入顶点(以回车结束输入):");
getchar();
while ((ch = getchar()) != '\n') /* 输入顶点信息 */
DG->vex[i++] = ch;
for (i = 0; i < DG->vexnum; i++) /* 初始化邻接矩阵 */
for (j = 0; j < DG->vexnum; j++)
DG->edges[i][j] = INFINITY;
printf("顶点 | 下标\n");
for (i = 0; i < DG->vexnum; i++) /* 显示图中顶点及其对应下标 */
{
printf("%3c%6d\n", DG->vex[i], i);
}
printf("请输入依次每条弧的弧尾下标(不带箭头)、弧头下标(带箭头)、权值(格式:i j w):\n");
for (k = 0; k < DG->edgenum; k++) /* 建立邻接矩阵 */
{
scanf("\n%d%d%d", &i, &j, &w); /* 输入弧的两个节点及权值 */
DG->edges[i][j] = w; /* 将矩阵对应位置元素置为权值 */
}
}
void PrintDG(MGraph DG)
{
int i, j;
for (i = 0; i < DG.vexnum; i++) /* 输出邻接矩阵 */
{
for (j = 0; j < DG.vexnum; j++)
{
if (DG.edges[i][j] == INFINITY) /* 节点不连通时,输出无穷大 */
printf(" ∞");
else /* 节点连通时,输出弧上权值 */
printf("%5d", DG.edges[i][j]);
}
printf("\n");
}
}
void ShortestPath_Dijkstra(MGraph DG, VexType StartVex)
{
int i, j, v, index,k; /* index:开始节点下标 */
int min; /* 开始节点到指定节点的最短路径权值和 */
int final[DG.vexnum]; /* 集合S,元素值为1:下标为i的节点以加入集合S;为0:未加入 */
int P[DG.vexnum][DG.vexnum];/* 开始节点到各节点的最短路径 */
int D[DG.vexnum]; /* 开始节点到下标为i的节点的最短路径权值之和 */
locateVex(DG, StartVex, &index);
for (i = 0; i < DG.vexnum; i++)
{
final[i] = 0; /* 初始化,刚开始集合S为空 */
D[i] = DG.edges[index][i];
for (j = 0; j < DG.vexnum; j++) /* 初始化路径,假设所有节点都不连通 */
P[i][j] = 0;
if (D[i] < INFINITY) /* 若节点连通,则在数组P[i]中标明路径 */
{
P[i][index] = 1;
P[i][i] = 1;
}
}
D[index] = 0; final[index] = 1; /* 开始节点加入S集 */
for (i = 1; i < DG.vexnum; i++)
{
min = INFINITY;
for (j = 0; j < DG.vexnum; j++)
if (!final[j])
if (D[j] < min) { v = j; min = D[j]; }
final[v] = 1; /* 把离StartVex最近的下标为v的节点加入S */
for (j = 0; j < DG.vexnum; j++)
if (!final[j] && (min + DG.edges[v][j] < D[j]))
{/* 若下标为j的节点为加入S且有更短路径,则更新D[j]和最短路径 */
D[j] = min + DG.edges[v][j];
for ( k = 0; k < DG.vexnum; k++)
P[j][k] = P[v][k];
P[j][j] = 1;
}
}
printf("节点 %c 到各节点的最短路径:\n", StartVex);
for (i = 0; i < DG.vexnum; i++)
{
for (j = 0; j < DG.vexnum; j++)
printf("%5d", P[i][j]);
printf("\n");
}
printf("节点 %c 到各节点的最短路径值:", StartVex);
for (i = 0; i < DG.vexnum; i++)
{
if (D[i] == INFINITY)
printf("∞, ");
else
printf("%d, ", D[i]);
}
}
void locateVex(MGraph DG, VexType vex, int * index)
{
int i;
for (i = 0; i < DG.vexnum; i++)
{
if (DG.vex[i] == vex)
{
*index = i;
return;
}
}
printf("节点定位失败!\n");
}