数据结构实验三: 图的操作与实现

数据结构实验一:线性表,堆栈和队列实现
数据结构实验二 :二叉树的操作与实现
数据结构实验三: 图的操作与实现
数据结构实验四 : 查找和排序算法实现

一、实验目的:

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的邻接表。

图8.54 一个带权有向图

#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出发的一棵最小生成树。

图8.55 一个带权连通图

#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的最短路径。

图8.56 一个有向图

#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)的所有迷宫路径。

图3.28 迷宫示意图

#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;
}
  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值