目的要求
1.掌握图的存储策略及其存储实现。
2.掌握图的深度、广度优先遍历的算法策略及其程序实现。
3.掌握图的常见算法策略及其程序实现。
实验内容
1.键入或随机生成数据,建立一个有向图的邻接表。
2.输出该邻接表。
3.以有向图邻接表为基础上,计算各顶点的度并输出。
4.以有向图邻接表为基础,输出其拓扑排序序列。
5.采用邻接表存储,实现无向图的非递归 DFS 遍历。
6.采用邻接表存储,实现无向图的 BFS 优先遍历。
7.判断无向图任意两个顶点间是否有路径,若有则输出路径上的顶点序列。
8.在主函数中设计一个简单的菜单,调用上述算法。
实验说明
1.类型定义(邻接表存储)
const int MAX_ VERTEX_NUM=8; // 顶点的最大个数
typedef struct ArcNode;
{
int adjvex ;
struct ArcNode *nextarc ;
int weight; // 边的权
} ArcNode ; // 表结点
typedef char VertexType; // 顶点元素类型
typedef struct VNode
{
int degree, indegree; // 顶点的度,入度
VertexType data;
ArcNode *firstarc;
} VNode /*头结点*/, AdjList[MAX_VERTEX_NUM] ;
typedef struct
{
AdjList vertices;
int vexnum, arcnum; // 顶点的实际数,边的实际数
} ALGraph ;
2.上述类型定义可根据情况适当调整。
源代码
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
#define MAX_VERTEX_NUM 8 // 顶点的最大个数
bool visited[MAX_VERTEX_NUM];//访问标志数组,已访问的顶点标记为true
typedef char VertexType;
typedef struct arcNode //图的边表结点
{
int adjvex; //存储该顶点在顶点表中的下标
struct arcNode * nextarc; //指向下一个结点的指针
int weight; // 边的权
}ArcNode;
typedef struct vNode //图的顶点表结点
{
int degree, indegree; //顶点的度,入度
VertexType data;
ArcNode * firstarc; //边表头指针
}VNode,AdjList[MAX_VERTEX_NUM];
typedef struct
{
AdjList vertices; //图的顶点表
int vexnum, arcnum; //顶点的实际数,边的实际数
}*ALGraph;
//图的邻接表初始化算法
ALGraph InitGraph(int vernum, int edgenum)
{
ALGraph graphList = (ALGraph)malloc(sizeof(ALGraph));
graphList->vexnum = vernum; //初始化定点数
graphList->arcnum = edgenum; //初始化边数
for (int i = 0;i < vernum;i++)
{
graphList->vertices[i].firstarc = NULL; //初始化每条链表为空
graphList->vertices[i].degree = 0; //初始化顶点的度为0
graphList->vertices[i].indegree = 0;
}
return graphList;
}
//判断顶点是否存在于顶点表中,存在返回下标位置,不存在返回-1
int LocateVex(ALGraph graphList, VertexType v)
{
int i;
for (i = 0; i < graphList->vexnum; i++)
{
if (v == graphList->vertices[i].data)
break;
}
if (i >= graphList->vexnum) //表示没有找到顶点v
{
printf("not exist this vertex!\n");
return -1;
}
return i;
}
//图的邻接表构建算法
void CreateGraph(ALGraph graphList)
{
int i, w;
VertexType v1, v2;
ArcNode * tempNode = NULL;
ArcNode * tempNode2 = NULL;
//输入顶点
for (i = 0;i < graphList->vexnum;i++)
{
printf("顶点%d:", i);
graphList->vertices[i].data = getchar();
while (graphList->vertices[i].data == '\n')
{
graphList->vertices[i].data = getchar();
}
}
getchar();
//建立边表
printf("输入边(vi,vj)上的顶点和权值(如:vi vj w)\n");
for (i = 0;i < graphList->arcnum;i++)
{
printf("第%d条边:", i + 1);
scanf("%c %c %d", &v1, &v2, &w);
getchar();
int m = LocateVex(graphList, v1);//判断输入的顶点是否在顶点表中
int n = LocateVex(graphList, v2);
if (m == -1 || n == -1)
return;
tempNode = (ArcNode*)malloc(sizeof(struct arcNode));//申请空间,生成边表结点
if (tempNode == NULL) {
printf("Out of space!\n");
return;
}
tempNode->adjvex = n;
tempNode->weight = w;
tempNode->nextarc = graphList->vertices[m].firstarc; //单链表的头插法
graphList->vertices[m].firstarc = tempNode;
}
}
//输出邻接表
void PrintGrapth(ALGraph graphList)
{
int i;
for (i = 0; i < graphList->vexnum; i++)
{
printf("顶点%c ", graphList->vertices[i].data);
ArcNode *arc = graphList->vertices[i].firstarc;
while (arc != NULL)
{
printf("—> <%c, %c> %d ", graphList->vertices[i].data, graphList->vertices[arc->adjvex].data,arc->weight);
arc = arc->nextarc;
}
putchar('\n');
}
}
//计算各顶点的度及入度
void degree(ALGraph graphList)
{
ArcNode * arc;
for (int i = 0;i < graphList->vexnum;i++)
{
graphList->vertices[i].degree = 0;
graphList->vertices[i].indegree = 0;
}
for (int i = 0;i < graphList->vexnum;i++)
{
for (arc = graphList->vertices[i].firstarc;arc;arc = arc->nextarc)
{
graphList->vertices[i].degree++; //出度
graphList->vertices[arc->adjvex].degree++;
graphList->vertices[arc->adjvex].indegree++;
}
}
}
//输出各顶点的度
void print_degree(ALGraph graphList)
{
printf("Index data degree indegree");
for (int i = 0;i < graphList->vexnum;i++)
printf("\n%3d%7c%9d%9d", i, graphList->vertices[i].data, graphList->vertices[i].degree, graphList->vertices[i].indegree);
printf("\n");
}
//拓扑排序算法
int topologicalsort(ALGraph graphList)
{
int count = 0; //计算输出顶点个数
int nodenum;
stack<int> nodestack;
ArcNode * temp;
degree(graphList); //计算每个顶点的入度
for (int i = 0;i < graphList->vexnum;i++)
{
if (graphList->vertices[i].indegree == 0)
nodestack.push(i);
}
while (!nodestack.empty())
{
nodenum = nodestack.top(); //取栈顶元素
nodestack.pop();
printf("%c ", graphList->vertices[nodenum].data); //输出拓扑序列
temp = graphList->vertices[nodenum].firstarc;
while (temp)
{
if (--graphList->vertices[temp->adjvex].indegree == 0) //若入度减少到0,则入栈
nodestack.push(temp->adjvex);
temp = temp->nextarc;
}
count++;
}
printf("\n");
if (count == graphList->vexnum)
return true;
if (count != graphList->vexnum) {
printf("此图有环,无拓扑序列!\n");
return false; //说明这个图有环
}
}
//无向图的非递归 DFS 遍历
void DFS(ALGraph graphList)
{
int i, j;
stack<int>s;
ArcNode * p;
memset(visited, 0, sizeof(visited));
for (i = 0;i < graphList->vexnum;i++)
{
if (!visited[i]) {
visited[i] = true;
printf("%c ", graphList->vertices[i].data);
s.push(i);
p = graphList->vertices[i].firstarc;
while (s.empty())
{
while (p) {
if (!visited[p->adjvex]) {
visited[p->adjvex] = true;
printf("%c ", graphList->vertices[p->adjvex].data);
s.push(p->adjvex);
p = graphList->vertices[p->adjvex].firstarc;
}
else
p = p->nextarc;
}
p = graphList->vertices[s.top()].firstarc;
s.pop();
}
}
}
printf("\n");
}
//邻接表的BSF算法
void BFS(ALGraph graphList, int i)
{
int tempno;
ArcNode * p = NULL;
queue<VertexType> Queue; //队列的数据类型为char
visited[i] = true; //设置标记,表明已被访问
printf("%c ", graphList->vertices[i].data); //输出访问的顶点的值
Queue.push(i); //将刚访问的顶点入队
while (!Queue.empty())
{
tempno = Queue.front();
Queue.pop();
//依次访问于当前结点相邻的点
p = graphList->vertices[tempno].firstarc;
while (p != NULL)
{
//如果其他顶点于当前顶点存在边且未被访问过
if (!visited[p->adjvex])
{
visited[p->adjvex] = 1; //做标记
Queue.push(p->adjvex); //入队
printf("%c ", graphList->vertices[p->adjvex].data);
}
p = p->nextarc; //移动到下一个顶点
}
}
printf("\n");
}
void BFSGraphList(ALGraph graphList)
{
int i;
memset(visited, 0, sizeof(visited)); //初始化为点都没有被访问过
for (i = 0;i < graphList->vexnum;i++)
if (!visited[i]) //对未访问过的顶点调用DFS
BFS(graphList, i);
}
bool record[MAX_VERTEX_NUM] = { 0 };
void dfsPath(ALGraph graph, int s, int end, int path[], int cnt)
{
int i, j = 1;
visited[s] = record[s] = true;
path[cnt++] = s;
ArcNode* edge = graph->vertices[s].firstarc;
if (s == end)
{
for (i = 0; i < cnt - 1; i++)
printf("%c-->", graph->vertices[path[i]].data);
printf("%c", graph->vertices[path[i]].data);
printf("\n");
return;
}
for (; edge; edge = edge->nextarc)
{
if (!visited[edge->adjvex])
{
dfsPath(graph, edge->adjvex, end, path, cnt);
}
//回溯
visited[edge->adjvex] = false;
}
if (s != end && record[end] == 0)
{
printf("不存在此路径!\n");
return;
}
}
//输出顶点i到顶点j之间的所有简单路径(不包含回路)
void findAllPath(ALGraph graph, int start, int end)
{
printf("%c顶点和%c顶点之间的路径:\n", graph->vertices[start].data, graph->vertices[end].data);
if (start == end)
printf("%c ",graph->vertices[start].data);
else
{
memset(visited, 0, sizeof(visited)); //初始化为点都没有被访问过
int path[MAX_VERTEX_NUM];
dfsPath(graph, start, end, path, 0);
}
}
int main()
{
int Index;
ALGraph g = NULL;
printf(" 图的应用\n");
printf(" ****************************************************************\n");
printf(" 1.键入或随机生成数据,建立一个有向图的邻接表\n");
printf(" 2.输出该邻接表\n");
printf(" 3.以有向图邻接表为基础,计算各顶点的度并输出\n");
printf(" 4.以有向图邻接表为基础,输出其拓扑排序序列\n");
printf(" 5.采用邻接表存储,实现无向图的非递归 DFS 遍历\n");
printf(" 6.采用邻接表存储,实现无向图的 BFS 优先遍历\n");
printf(" 7.判断无向图任意两个顶点间是否有路径,若有则输出路径上的顶点序列\n");
printf(" 0.退出\n");
printf(" ****************************************************************\n");
while (true)
{
printf("\n请输入一个序号:");
scanf("%d", &Index);
switch (Index)
{
case 0:
return 0;
case 1:
{
int vex, arc;
printf("输入顶点数和边数(如 x,y):\n");
scanf("%d,%d", &vex, &arc);
g = InitGraph(vex, arc);
CreateGraph(g);
printf("建立成功!\n");
}break;
case 2:
{
if (g) {
printf("\n邻接表:\n");
PrintGrapth(g);
}
else
printf("The Graph is NULL!\n");
}break;
case 3:
{
if (g) {
degree(g);
print_degree(g);
}
else
printf("The Graph is NULL!\n");
}break;
case 4:
{
if (g) {
printf("\n拓扑序列:");
topologicalsort(g);
}
else
printf("The Graph is NULL!\n");
}break;
case 5:
{
if (g) {
printf("\n深度优先遍历:");
DFS(g);
}
else
printf("The Graph is NULL!\n");
}break;
case 6:
{
if (g) {
printf("\n广度优先遍历:");
BFSGraphList(g);
}
else
printf("The Graph is NULL!\n");
}break;
case 7:
{
if (g) {
int num1, num2;
printf("请输入任意两个顶点的编号(如1,2):\n");
scanf("%d,%d", &num1, &num2);
if (num1 >= 0 && num1 < 6 && num2 >= 0 && num2 < 6)
findAllPath(g, num1, num2);
else
printf("输入有误!\n");
}
else
printf("The Graph is NULL!\n");
}break;
default:
printf("输入有误,请输入数字0-7!\n");
break;
}
}
printf("\n");
system("pause");
return 0;
}