数据结构实验一:线性表,堆栈和队列实现
数据结构实验二 :二叉树的操作与实现
数据结构实验三: 图的操作与实现
数据结构实验四 : 查找和排序算法实现
文章目录
- 一、实验目的:
- 二、使用仪器、器材
- 三、实验内容及原理
- 1、教材P310实验题1:实现图的邻接矩阵和邻接表的存储
- 2、教材P310实验题2:实现图的遍历算法
- 3、教材P311实验题5:采用Prim算法求最小生成树
- 4、教材P311实验题10:求有向图的简单路径
- 5、教材P313实验题14:用图搜索方法求解如图3.28(教材P119)的迷宫问题(也可以自建迷宫)
一、实验目的:
1、领会图的两种主要存储结构和图的基本运算算法设计;
2、领会图的两种遍历算法;
3、领会Prim算法求带权连通图中最小生成树的过程和相关算法设计;
4、掌握深度优先遍历和广度优先遍历算法在图路径搜索问题中的应用;
5、深入掌握图遍历算法在求解实际问题中的应用。
二、使用仪器、器材
微机一台
操作系统:WinXP
编程软件:C/C++编程软件
三、实验内容及原理
1、教材P310实验题1:实现图的邻接矩阵和邻接表的存储
编写一个程序graph.cpp,设计带权图的邻接矩阵与邻接表的创建和输出运算,并在此基础上设计一个主程序exp8-1.cpp完成以下功能。
(1)建立如图8.54所示的有向图G的邻接矩阵,并输出之。
(2)建立如图8.54所示的有向图G的邻接表,并输出之。
(3)销毁图G的邻接表。
#include <iostream>
#include<iomanip>
using namespace std;
#define maxn 1024 //最大顶点数
int matrix[maxn][maxn]; //邻接矩阵
int n, m; //顶点数,边数
/*
边结点
*/
struct ArcNode {
int adjvex; //该边所指向的顶点的位置
int lowcost; //权值
ArcNode* next; //指向的下一条边的指针
};
ArcNode* ArcList[maxn * (maxn - 1)]; //所有边结点
int in = 0; //下标
/*
顶点
*/
struct {
ArcNode* firstarc;
}AdjList[maxn];
/*
增加一条从i指向j的权值为k的顶点
*/
void add(int i, int j, int k) {
matrix[i][j] = k;
ArcNode* p = new ArcNode();
p->adjvex = j; //它指向的是j顶点
p->lowcost = k; //权值为k
//p插入到链表头部
p->next = AdjList[i].firstarc;
AdjList[i].firstarc = p;
ArcList[in++] = p; //把这个边结点存储到数组中,顺便完成遍历
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = 6;
m = 10;
//初始化邻接矩阵
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
matrix[i][j] = -1;
}
}
//初始化AdjList
for (int i = 0; i < n; ++i) {
AdjList[i].firstarc = NULL;
}
//增加m条边
add(0, 1, 5);
add(0, 3, 3);
add(1, 2, 4);
add(2, 0, 8);
add(2, 5, 9);
add(3, 2, 5);
add(3, 5, 6);
add(4, 3, 5);
add(5, 4, 1);
add(5, 0, 3);
//打印邻接矩阵
cout << "adjacent matrix:" << endl;
for (int i = 0; i < n; ++i) {
cout << i << " :";
for (int j = 0; j < n; ++j) {
cout << setw(5)<<matrix[i][j] ;
}
cout << endl;
}
//打印邻接表
cout << "adjacency list:" << endl;
ArcNode* p = 0;
for (int i = 0; i < n; ++i) {
p = AdjList[i].firstarc;
cout << i << " : ";
while (p) {
cout << p->adjvex << "(" << p->lowcost << ")" << " -> ";
p = p->next;
}
cout << "^" << endl;
}
p = 0;
//销毁邻接表
for (int i = 0; i < in; ++i) {
delete ArcList[i]; //删除
ArcList[i] = 0; //指针置空
}
//修改每个顶点的firstarc为空
for (int i = 0; i < n; ++i) {
AdjList[i].firstarc = 0;
}
in = 0;
return 0;
}
2、教材P310实验题2:实现图的遍历算法
编写一个程序travsal.cpp实现图的两种遍历运算,并在此基础上设计一个程序exp8-2.cpp完成以下功能。
(1)输出如图8.54的有向图G从顶点0开始的深度优先遍历序列(递归算法)。
(2)输出如图8.54的有向图G从顶点0开始的深度优先遍历算法(非递归算法)。
(3)输出如图8.54的有向图G从顶点0开始的广度优先遍历序列。
#include<iostream>
#include<vector>
using namespace std;
#define maxn 128
//标记数组 标记是否被访问
int tag[maxn];
//带权节点 用数组表示
struct edgeNode {
int value;
int valueOfLine;
edgeNode* next;
edgeNode(int v, int V) {
this->value = v;
this->valueOfLine = V;
this->next = NULL;
}
};
//节点链表
struct Vl {
edgeNode * first;
}VList[maxn];
//插入链表 连接节点
void Link(int i, int j, int value) {
edgeNode* p = new edgeNode(j, value);
p->next = VList[i].first;
VList[i].first = p;
}
//深度优先 递归
void DFS(int i = 0) {
tag[i] = 1;
edgeNode* temp = VList[i].first;
cout << temp->value << " ";
while (temp) {
if (tag[temp->value] != 1) {
DFS(temp->value);
}
temp = temp->next;
}
}
/*
* 深度优先 非递归
*/
int stk[maxn]; //模拟栈
int stkSum;//栈的元素个数
//非递归
void DFS2() {
memset(tag, 0, sizeof(tag)); //清空访问标记
//当前顶点
int v = 0;
//栈元素
stkSum = 0;
tag[v] = 1;//标记
stk[stkSum++] = v;//入栈
cout << "深度优先 非递归:" ;
edgeNode* temp = NULL;//取第一个元素
while (stkSum) {
//只要栈不空 说明没有遍历完 继续
v = stk[--stkSum];//出栈栈顶
cout << v << " ";//出栈输出
temp = VList[v].first;
while (temp) {
if (!tag[temp->value]) {
//如果没有访问过
tag[temp->value] = 1;//标记
stk[stkSum++] = temp->value;//入栈
}
temp = temp->next;//遍历
}
}
cout << endl;
}
/*
* 广度优先
*/
//模拟队列
int quen[maxn];
//左右指针 int left = 0,int right=0
void BFS(int v = 0, int left = 0, int right = 0) {
//标记0
memset(tag, 0, sizeof(tag)); //清空访问标记
tag[v] = 1; //标记初始顶点
cout << "bfs:";
quen[right++] = v; //入队
edgeNode* p = 0;
while (left != right) {
//只要队列不空,就一直循环
v = quen[left++]; //出队
cout << v << " "; //输出顶点
left %= maxn; //如果qi >= maxn,则从0开始
p = VList[v].first;
while (p) {
if (!tag[p->value]) {
//只要没被访问过就入栈
quen[right++] = p->value;
right %= maxn;
tag[p->value] = 1; //标记
}
p = p->next; //指针后移
}
p = 0; //指针置空
}
}
/*
销毁邻接表
*/
void destoryArc(int n) {
//销毁邻接表
for (int i = 0; i < n; i++) {
while (VList[i].first) {
edgeNode* head = VList[i].first;
VList[i].first = head->next;
delete head;
}
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int v = 6;
int L = 10;
//初始化AdjList
for (int i = 0; i < v; ++i) {
VList[i].first = NULL;
}
//增加m条边
Link(0, 1, 5);
Link(0, 3, 3);
Link(1, 2, 4);
Link(2, 0, 8);
Link(2, 5, 9);
Link(3, 2, 5);
Link(3, 5, 6);
Link(4, 3, 5);
Link(5, 4, 1);
Link(5, 0, 3);
//-----------深度优先(递归)----------
memset(tag, 0, sizeof(tag)); //清空访问标记
cout << "dfs(recursion):";
DFS();
cout << endl;
memset(tag, 0, sizeof(tag)); //清空访问标记
//-----------深度优先(非递归)----------
DFS2();
//-----------广度优先----------
BFS();
cout << endl;
destoryArc(6); //销毁邻接表
return 0;
}
3、教材P311实验题5:采用Prim算法求最小生成树
编写一个程序exp8-5.cpp,实现求带权连通图中最小生成树的Prim算法,如图8.55所示的带权连通图G,输出从顶点0出发的一棵最小生成树。
#include <iostream>
using namespace std;
typedef long long ll;
# define maxn 1024 //最大顶点数量
int matrix[maxn][maxn]; //邻接矩阵
bool tag[maxn]; //标记,tag[i]=1表示顶点i在集合U中
int n, m; //n:顶点个数(其中顶点标号从1到n) m:边的个数
struct {
int adjvex; //最小边在U中的那个顶点
int lowcost; //最小边上的权值
}closedge[maxn];
/*
增加一条连接n1顶点和n2顶点的边,权值为k
*/
void link(int n1, int n2, int k) {
matrix[n1][n2] = k;
matrix[n2][n1] = k;
}
/*
初始化
*/
void init() {
//初始化邻接矩阵
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
matrix[i][j] = -1;
}
}
memset(tag, 0, sizeof(maxn));
n = 6; //一共有6个顶点
m = 10; //10条边
link(0, 3, 7);
link(0, 5, 3);
link(0, 2, 8);
link(0, 1, 5);
link(1, 2, 4);
link(2, 3, 5);
link(2, 5, 9);
link(3, 5, 6);
link(3, 4, 5);
link(4, 5, 1);
//下面m行都是在建图
tag[0] = 1; //从该顶点出发(把该顶点加入集合U)
//对没有加入集合U中的顶点,都初始化closedge
for (int i = 1; i < n; ++i) {
closedge[i].adjvex = 0;
closedge[i].lowcost = matrix[0][i];
}
};
/*
Prim算法开始
*/
/
void prim() {
int T = n - 1;
//循环执行n-1次
while (T--) {
//首先寻找【不在U集合中】并且【closedge中权值最小】的边
int mi = maxn; //记录当前最小的权值
int k = 0; //最小权值时候的顶点
for (int i = 0; i < n; ++i) {
if (!tag[i]/*保证不在集合U中*/ && closedge[i].lowcost != -1/*保证边存在*/ && closedge[i].lowcost < mi/*保证权值最小*/) {
mi = closedge[i].lowcost;
k = i;
}
}
cout << k << " <---> " << closedge[k].adjvex << endl; //找到一条边
tag[k] = 1; //标记
//更新closedge
for (int i = 0; i < n; ++i) {
if (closedge[i].lowcost == -1/*原来的边不存在*/ || (matrix[k][i] != -1 && matrix[k][i] < closedge[i].lowcost)/*保证k--i的边存在并且比原来记录的要小*/) {
closedge[i].adjvex = k;
closedge[i].lowcost = matrix[k][i];
}
}
}
}
/
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
init(); //初始化
prim(); //普利姆算法
return 0;
}
4、教材P311实验题10:求有向图的简单路径
编写一个程序exp8-10.cpp,设计相关算法完成以下功能。
(1)输出如图8.56的有向图G从顶点5到顶点2的所有简单路径。
(2)输出如图8.56的有向图G从顶点5到顶点2的所有长度为3的简单路径。
(3)输出如图8.56的有向图G从顶点5到顶点2的最短路径。
#include<iostream>
#include<queue>
using namespace std;
# define maxn 128
//邻接矩阵
int matrix[maxn][maxn];
//标记数组
int tag[maxn];
//路径数组
int path[maxn];
//边数 点数
int n, m;
void Link(int i,int j,int value=1) {
matrix[i][j] = value;//有向的
}
/*
* 深度优先找所有路径
*/
void DFS_all_destination(int nowV,int destV,int j=0) { //起点 终点 记录的路径数组下标
if (j == 0) {
tag[nowV] = 1;
}
//当前点入路径数组
path[j] = nowV;
//如果到达目标 遍历输出path 返回
if (nowV == destV) {
for (int i = 0; i <= j; i++) {
if (i != j) {
cout << path[i] << "->";
}else {
cout << path[i]<<endl;
}
}
return;
}
//没有到达目标 for矩阵 与该点相连的 没有被标记过的 标记
//递归
//递归结束找到一条 tag清空
for (int i = 0; i < n; i++) {
if (!tag[i] && matrix[nowV][i] != -1) {
tag[i] = 1;
DFS_all_destination(i, destV, j + 1);
tag[i] = 0;
}
}
}
/*
* 深度优先遍历找指定长度的所有路径
now:当前顶点
dest:目标
len:指定长度
i:当前长度
*/
void DFS_fix_dest(int nowV,int destV,int length,int j=0) {
//标记第一个
if (j == 0) {
tag[nowV] == 1;
}
//当前节点入路径数组
path[j] = nowV;
//如果到达目的 长度满足 打印path 返回
if (nowV == destV && j == length) {
for (int i = 0; i <= j; i++) {
if (i != j) {
cout << path[i] << "->";
}
else {
cout << path[i] << endl;
}
}
return;
}
//如果没到达目的地并且i<length 继续
//i>=length,nowV!=destV 返回
if (nowV != destV) {//还没到达
if (j < length) {//路径小于 还有机会 递归
for (int i = 0; i < n; i++) {
if (!tag[i] && matrix[nowV][i] != -1) {
tag[i] = 1;
DFS_fix_dest(i, destV, length, j + 1);
tag[i] = 0;
}
}
}
else {//没有机会 直接返回
return;
}
}
}
/*
* 广度优先 求最短
*/
int LastV[maxn];//记录上一个顶点 方便溯源
void BFS_to_dest(int start,int end) {//开始点 结束点
//清空数组
for (int i = 0; i < maxn; i++) {
tag[i] = 0;
LastV[i] = -1;
}
//队列
queue<int> que;
//当前点入队
que.push(start);
// 标记
tag[start] = 1;
//当队列不空 出队
int v;
while (!que.empty()) {
v = que.front();
que.pop();
if (v == end) {//如果到达结尾 跳出循环
break;
}
//如果还没到达 循环 未标记且连线存在 标记 加入last数组 入队
for (int i = 0; i < n; i++) {
if (!tag[i] && matrix[v][i] != -1) {
tag[i] = 1;
LastV[i] = v;
que.push(i);
}
}
}
int i = 0;
while (v != -1) {//拿队尾 循环当值不是-1 记录路径
path[i++] = v;//反向路径数组
v = LastV[v];//溯源寻找上一个节点
}
while (--i) {
cout << path[i] << "->";
}
cout <<end <<endl;
//反向遍历输出
}
void init() {
for (int i = 0; i < maxn; i++) {
tag[i] = 0;//初始化tag数组
for (int j = 0; j < maxn; j++) {
matrix[i][j] = -1;//初始化 邻接矩阵
}
}
//顶点数
n = 6;
//连接图
Link(0, 3);
Link(0, 1);
Link(1, 2);
Link(2, 0);
Link(2, 5);
Link(3, 2);
Link(3, 5);
Link(4, 3);
Link(5, 4);
Link(5, 0);
}
int main() {
init();
cout << "深度优先: 5 -> 2 所有路径:" << endl;
DFS_all_destination(5, 2);
cout << endl;
memset(tag, 0, sizeof(tag));
cout << "深度优先: 5 -> 2 长度为3所有路径:" << endl;
DFS_fix_dest(5, 2, 3);
cout << endl;
memset(tag, 0, sizeof(tag));
cout << "广度优先: 5 -> 2 最短路径:" << endl;
BFS_to_dest(5, 2);
return 0;
}
5、教材P313实验题14:用图搜索方法求解如图3.28(教材P119)的迷宫问题(也可以自建迷宫)
编写一个程序exp8-14.cpp,完成以下功能。
(1)建立一个迷宫对应的邻接表表示。
(2)采用深度优先遍历算法输出从入口(1,1)到出口(M,N)的所有迷宫路径。
#include<iostream>
#include<vector>
using namespace std;
//标记数组
int tag[6][6] = { 0 };
//迷宫
vector<vector<int>> m{
{0,0,0,0,0,0},
{0,1,1,1,0,0},
{0,1,0,1,1,0},
{0,1,1,1,0,0},
{0,0,1,1,1,0},
{0,0,0,0,0,0}
};
struct Proint;
struct vNode;
//顶点
struct Point {
int x;
int y;
vNode* firstNode;
Point(int i, int j) {
this->x = i;
this->y = j;
this->firstNode = 0;
}
}*Plist[6][6];
//邻接表节点
struct vNode {
Point* p;//顶点的邻点
vNode* next;//构建链表
};
//初始化函数
void init(vector<vector<int>>arr, int xlen, int ylen) {
//初始化顶点矩阵
for (int i = 0; i < xlen; i++) {
for (int j = 0; j < ylen; j++) {
if (arr[i][j]) {//如果有就赋值
Plist[i][j] = new Point(i,j);
}
else {
Plist[i][j] = 0;
}
}
}
//
int four[2][4] = { {0,0,1,-1} ,{1,-1,0,0} };
int x, y;
//初始化邻接表
for (int i = 0; i < xlen; i++) {
for (int j = 0; j < ylen; j++) {
if (arr[i][j]) {//当前有值 判断四周
for (int k = 0; k < 4; k++) {
x = i + four[0][k];
y = j + four[1][k];
if (x >= 0 && x <= xlen && y >= 0 && y <= ylen&&m[x][y]!=0) {//在矩阵范围内
vNode* closeNode = new vNode();//邻节点
closeNode->p = Plist[x][y];//连接邻近 point
closeNode->next = Plist[i][j]->firstNode;//接入邻接表
Plist[i][j]->firstNode = closeNode;
}
}
}
}
}
}
/*
* 深度优先搜索
*/
Point*path[36]; //顶点指针 内含坐标
void DFS(int x, int y, int endx, int endy, int i = 0) {
//记录路径
if (i == 0) {
tag[x][y] = 1;
}
path[i] = Plist[x][y];
//如果到达终点 打印数组 返回
if (x == endx && y == endy) {
for (int j = 0; j <=i; j++) {
if (j != i) {
cout << "(" << path[j]->x << "," << path[j]->y << ")->";
}
else {
cout << "(" << path[j]->x << "," << path[j]->y <<")" << endl;
}
}
return;
}
//没有达到终点
vNode* temp = Plist[x][y]->firstNode;
//遍历邻接表 没有标记 就标记 递归 去掉标记
int mx, my;
while (temp) {
mx = temp->p->x;
my = temp->p->y;
if (!tag[mx][my]) {
tag[mx][my] = 1;
DFS(mx, my, endx, endy, i + 1);
tag[mx][my] = 0;
}
temp = temp->next;
}
}
int main() {
init(m, m[0].size(), m.size());//赋初值0
DFS(1, 1, 4, 4);//深度优先查找
return 0;
}