山东大学数据结构与算法实验12图(图论基础)

题目描述

创建无向图类,存储结构使用邻接链表,提供操作:插入一条边,删除一条边,BFS,DFS。

输入输出格式

输入

第一行四个整数 n,m,s,t。n (10≤n≤100000) 代表图中点的个数,m (10≤m≤200000) 代表接下来共有 m 个操作,s 代表起始点,t 代表终点。
接下来 m 行,每行代表一次插入或删除边的操作,操作格式为:

  • 0 u v 在点 u 和 v 之间增加一条边

  • 1 u v 删除点 u 和 v 之间的边

输出

第一行输出图中有多少个连通分量

第二行输出所有连通子图中最小点的编号(升序),编号间用空格分隔

第三行输出从 s 点开始的 dfs 序列长度

第四行输出从 s 点开始的字典序最小的 dfs 序列

第五行输出从 t 点开始的 bfs 序列的长度

第六行输出从 t 点开始字典序最小的 bfs 序列

第七行输出从 s 点到 t 点的最短路径,若是不存在路径则输出-1

数据结构与算法描述    

创建无向图类,私有成员有点和边的个数、邻接链表。公有成员中:

插入一条边的函数,先一个终点的节点,然后找与起点相连的点,如果没有则直接连接,如果有,则找一个与起点相连的大于终点的点,将终点插在该点前面,保证字典序从小到大。因为是无向图,将终点和起点交换再重复上面的过程。

void graph::insert(int u, int v) {//插入一条边
	chainNode* insertnode = new chainNode(v);//创造一个终点的节点 
	chainNode* nextnode = list[u].next;//与起点相连的点 
	//按大小连接点,方柏霓按字典序输出 
	if (nextnode == NULL) {//没有相连的点,直接连 
		list[u].next = insertnode;
	}
	else if (nextnode->element >= v) {//如果相连的点大于终点 
		list[u].next = insertnode;//将较小的终点连在该点前面 
		insertnode->next = nextnode;
	}
	else {
		chainNode* pronextnode = nextnode->next;//从下一个相连的点找 
		while(pronextnode != NULL && pronextnode->element < v) {//找到比终点大的点 
			nextnode = nextnode->next;
			pronextnode = pronextnode->next;
		}
		nextnode->next = insertnode;//将较小的终点连在该点前面 
		insertnode->next = pronextnode;
	}
	chainNode* insertnode1 = new chainNode(u);//因为是无向图,所以反向再来一遍 
	chainNode* nextnode1 = list[v].next;
	if (nextnode1 == NULL) {
		list[v].next = insertnode1;
	}
	else if (nextnode1->element >= u) {
		list[v].next = insertnode1;
		insertnode1->next = nextnode1;
	}
	else {
		chainNode* pronextnode1 = nextnode1->next;
		while (pronextnode1 != NULL && pronextnode1->element < u) {
			nextnode1 = nextnode1->next;
			pronextnode1 = pronextnode1->next;
		}
		nextnode1->next = insertnode1;
		insertnode1->next = pronextnode1;
	}
	numedges++;//边数加一 
}

删除一条边的函数:从与起点相连的第一个点开始找,找到后将邻接链表的该节点删除,并将其他与起点相连的点接上,保持链表。因为是无向图,将终点和起点交换再重复上面的过程。

void graph::erase(int u, int v) {//删除点u和v之间的边
	chainNode* erasenode = list[u].next;//从起点相连的第一个点开始找 
	if (erasenode->element == v ) {//如果是第一个 
		list[u].next = erasenode->next;
		delete erasenode;
	}
	else {
		chainNode* proerasenode = erasenode->next;//从下一个相连的点找 
		while(proerasenode->element != v) {//找到要删除的终点 
			erasenode = erasenode->next;
			proerasenode = proerasenode->next;
		}
		erasenode->next = proerasenode->next;
		delete proerasenode;
	}
	
	chainNode* erasenode1 = list[v].next;//因为是双向所以反过来再删除一次 
	if (erasenode1->element == u) {
		list[v].next = erasenode1->next;
		delete erasenode1;
	}
	else {
		chainNode* proerasenode1 = erasenode1->next;
		while(proerasenode1->element != u) {
			erasenode1 = erasenode1->next;
			proerasenode1 = proerasenode1->next;
		}
		erasenode1->next = proerasenode1->next;
		delete proerasenode1;
	}
	numedges--;//边数减一 
}

dfs递归函数,先将该点标记为已到达,然后找到与该点相连的点,进入循环,循环截止条件是没有与该点相连的点,在循环中如果该点没到达,则递归。

	void dfs(int s) {//深度优先搜索 
		reach[s] = 0;//标记为已到达 
		chainNode* nextnode = list[s].next;//与s点相连的点 
		while (nextnode) {
			if (reach[nextnode->element] != 0) {//如果没有到达 
				dfs(nextnode->element);//找该点相连的点 
			}
			nextnode = nextnode->next;//下一个与s相连的点 
		}
	}

Dfs的输出函数、dfs的长度函数,都是在每一次递归的时候进行输出或者长度加一。

void ldfs(int s) {//计算长度 
		length++;
		reach[s] = 0;//标记为已到达 
		chainNode* nextnode = list[s].next;//与s点相连的点 
		while (nextnode) {
			if (reach[nextnode->element] != 0) {//如果没有到达 
				ldfs(nextnode->element);//找该点相连的点 
			}
			nextnode = nextnode->next;//下一个与s相连的点 
		}
	}
	void outdfs(int s) {//输出从s点开始的字典序最小的dfs序列
		reach[s] = 0;//标记为已到达 
		cout << s;
		chainNode* nextnode = list[s].next;//与s点相连的点 
		while (nextnode) {
			if (reach[nextnode->element] != 0) {//如果没有到达 
				cout << " ";
				outdfs(nextnode->element);//找该点相连的点
			}
			nextnode = nextnode->next;//下一个与s相连的点 
		}
	}

统计连通分量个数,由于每一次dfs递归函数的调用就会形成一个连通分支,则每经过一次dfs且判断点是否已到达,统计个数就加一。Bfs和dfs的长度相等。

void component() {//统计连通分量个数 
		int num = 0;
		for (int i = 1; i <= numvertices; i++) {
			if (reach[i] != 0) {//一次深深度优先搜索成一个连通树 
				dfs(i);
				num++;
			}
		}
		cout << num << endl;
	}

输出连通子图的最小点编号函数,同上一个函数,将统计个数加一,改为输出。

	void subgraph() {//输出连通子图的最小点编号 
		for (int i = 1; i <= numvertices; i++) {
			if (reach[i] != 0) {//一次深度优先搜索形成一个连通树 
				cout << i << " ";
				dfs(i);
			}
		}
		cout << endl;
	}

Bfs函数,利用队列先进先出,则=先将起点压入队列中,并标记已到达,进入循环,知道队列为空循环结束,先将队列中的数取出并输出删除,然后将该点所有相连且没到达的点压入队列中。

void graph::bfs(int t) {//从t点开始字典序最小的bfs序列 
	queue q(numvertices);
	q.push(t);
	reach[t]=0;//标记已到达 
	while (!q.empty()) {
		int w=q.front();//从队列中取出并输出 
		q.pop();
		cout << w << " ";
		for(chainNode* u = list[w].next;u!=NULL;u=u->next){//将该点所有相连且没到达的点压入队列中 
			if (reach[u->element] != 0) {
				q.push(u->element);
				reach[u->element] = 0;//标记为已经到达 
			}
		}
	}
	cout << endl;
}

寻找最短路径,因为是无向图且没有加权,所以可以用bfs。比bfs多一个数组,统计“层数”即路径长度,并都初始化为0。在将该点所有相连且没到达的点压入队列中的循环中,要先判断一下是否到终点,并且将“层数“改为上一层加一。

void graph::minpath(int s, int t) {//用bfs找最短路径 
	queue q(numvertices);
	q.push(s);
	reach[s] = 0;
	int path[numvertices + 1];//统计“层数”即路径长度 
	for (int i = 1; i <= numvertices; i++) {
		path[i] = 0;//所有点初始化 
	}
	while (!q.empty()) {
		int w = q.front();
		q.pop();
		for(chainNode* u = list[w].next;u!=NULL;u = u->next){//将该点所有相连且没到达的点压入队列中 
			if (reach[u->element] != 0) {
				if (u->element == t) {//已经到终点则输出 
					cout << path[w] + 1<<endl;
					return;
				}
				path[u->element] = path[w] + 1;//该节点的“层数”为上一层加一 
				q.push(u->element);
				reach[u->element] = 0;//标记为已经到达 
			}
		}
	}
	cout << -1 << endl;
}

完整代码(含注释)
 

#include<iostream>
using namespace std;
struct chainNode {
	int element;//节点的元素
	chainNode* next;//指向下一节点的指针
	chainNode() {
	}
	chainNode(const int& element) {
		this->element = element;
		this->next = NULL;
	}
	chainNode(const int& element, chainNode* next) {
		this->element = element;
		this->next = next;
	}
};
class queue {
private:
	chainNode* queuefront;//第一个数前一个位置的索引
	chainNode* queueback;//最后一个数的索引
	int queuelength;//队列长度
public:
	queue(int n) {
		queuelength = 0;
		queuefront = queueback = NULL;
	}
	bool empty()const { return (queuefront)?false:true; }//是否为空
	int front() { return queuefront->element; }//第一个位置
	void pop() {//删除
		chainNode* deletenode = queuefront->next;
		delete queuefront;
		queuefront = deletenode;
		queuelength--;
	}
	void push(int theelement) {//插入
		chainNode* newnode = new chainNode(theelement);
		if (queuelength == 0) {
			queuefront = newnode;
			queueback = newnode;
		}
		else {
			queueback->next = newnode;
			queueback = newnode;
		}
		queuelength++;
	}
};
int reach[1000000]={1};
int length = 0;
class graph {
private:
	int numvertices;//点的个数 
	int numedges;//边的个数 
	chainNode* list;//邻接链表 
public:
	graph(int n) {
		numvertices = n;
		numedges = 0;
		list = new chainNode[n + 1];
		for (int i = 1; i <= n; i++) {
			list[i].element = i;
			list[i].next = NULL;
		}
	}
	void insert(int u, int v);
	void erase(int u, int v);
	void component() {//统计连通分量个数 
		int num = 0;
		for (int i = 1; i <= numvertices; i++) {
			if (reach[i] != 0) {//一次深深度优先搜索成一个连通树 
				dfs(i);
				num++;
			}
		}
		cout << num << endl;
	}
	void subgraph() {//输出连通子图的最小点编号 
		for (int i = 1; i <= numvertices; i++) {
			if (reach[i] != 0) {//一次深度优先搜索形成一个连通树 
				cout << i << " ";
				dfs(i);
			}
		}
		cout << endl;
	}
	void dfs(int s) {//深度优先搜索 
		reach[s] = 0;//标记为已到达 
		chainNode* nextnode = list[s].next;//与s点相连的点 
		while (nextnode) {
			if (reach[nextnode->element] != 0) {//如果没有到达 
				dfs(nextnode->element);//找该点相连的点 
			}
			nextnode = nextnode->next;//下一个与s相连的点 
		}
	}
	void ldfs(int s) {//计算长度 
		length++;
		reach[s] = 0;//标记为已到达 
		chainNode* nextnode = list[s].next;//与s点相连的点 
		while (nextnode) {
			if (reach[nextnode->element] != 0) {//如果没有到达 
				ldfs(nextnode->element);//找该点相连的点 
			}
			nextnode = nextnode->next;//下一个与s相连的点 
		}
	}
	void outdfs(int s) {//输出从s点开始的字典序最小的dfs序列
		reach[s] = 0;//标记为已到达 
		cout << s;
		chainNode* nextnode = list[s].next;//与s点相连的点 
		while (nextnode) {
			if (reach[nextnode->element] != 0) {//如果没有到达 
				cout << " ";
				outdfs(nextnode->element);//找该点相连的点
			}
			nextnode = nextnode->next;//下一个与s相连的点 
		}
	}
	void bfs(int t);
	void minpath(int s, int t);
};
void graph::insert(int u, int v) {//插入一条边
	chainNode* insertnode = new chainNode(v);//创造一个终点的节点 
	chainNode* nextnode = list[u].next;//与起点相连的点 
	//按大小连接点,方柏霓按字典序输出 
	if (nextnode == NULL) {//没有相连的点,直接连 
		list[u].next = insertnode;
	}
	else if (nextnode->element >= v) {//如果相连的点大于终点 
		list[u].next = insertnode;//将较小的终点连在该点前面 
		insertnode->next = nextnode;
	}
	else {
		chainNode* pronextnode = nextnode->next;//从下一个相连的点找 
		while(pronextnode != NULL && pronextnode->element < v) {//找到比终点大的点 
			nextnode = nextnode->next;
			pronextnode = pronextnode->next;
		}
		nextnode->next = insertnode;//将较小的终点连在该点前面 
		insertnode->next = pronextnode;
	}
	chainNode* insertnode1 = new chainNode(u);//因为是无向图,所以反向再来一遍 
	chainNode* nextnode1 = list[v].next;
	if (nextnode1 == NULL) {
		list[v].next = insertnode1;
	}
	else if (nextnode1->element >= u) {
		list[v].next = insertnode1;
		insertnode1->next = nextnode1;
	}
	else {
		chainNode* pronextnode1 = nextnode1->next;
		while (pronextnode1 != NULL && pronextnode1->element < u) {
			nextnode1 = nextnode1->next;
			pronextnode1 = pronextnode1->next;
		}
		nextnode1->next = insertnode1;
		insertnode1->next = pronextnode1;
	}
	numedges++;//边数加一 
}
void graph::erase(int u, int v) {//删除点u和v之间的边
	chainNode* erasenode = list[u].next;//从起点相连的第一个点开始找 
	if (erasenode->element == v ) {//如果是第一个 
		list[u].next = erasenode->next;
		delete erasenode;
	}
	else {
		chainNode* proerasenode = erasenode->next;//从下一个相连的点找 
		while(proerasenode->element != v) {//找到要删除的终点 
			erasenode = erasenode->next;
			proerasenode = proerasenode->next;
		}
		erasenode->next = proerasenode->next;
		delete proerasenode;
	}
	
	chainNode* erasenode1 = list[v].next;//因为是双向所以反过来再删除一次 
	if (erasenode1->element == u) {
		list[v].next = erasenode1->next;
		delete erasenode1;
	}
	else {
		chainNode* proerasenode1 = erasenode1->next;
		while(proerasenode1->element != u) {
			erasenode1 = erasenode1->next;
			proerasenode1 = proerasenode1->next;
		}
		erasenode1->next = proerasenode1->next;
		delete proerasenode1;
	}
	numedges--;//边数减一 
}
void graph::bfs(int t) {//从t点开始字典序最小的bfs序列 
	queue q(numvertices);
	q.push(t);
	reach[t]=0;//标记已到达 
	while (!q.empty()) {
		int w=q.front();//从队列中取出并输出 
		q.pop();
		cout << w << " ";
		for(chainNode* u = list[w].next;u!=NULL;u=u->next){//将该点所有相连且没到达的点压入队列中 
			if (reach[u->element] != 0) {
				q.push(u->element);
				reach[u->element] = 0;//标记为已经到达 
			}
		}
	}
	cout << endl;
}
void graph::minpath(int s, int t) {//用bfs找最短路径 
	queue q(numvertices);
	q.push(s);
	reach[s] = 0;
	int path[numvertices + 1];//统计“层数”即路径长度 
	for (int i = 1; i <= numvertices; i++) {
		path[i] = 0;//所有点初始化 
	}
	while (!q.empty()) {
		int w = q.front();
		q.pop();
		for(chainNode* u = list[w].next;u!=NULL;u = u->next){//将该点所有相连且没到达的点压入队列中 
			if (reach[u->element] != 0) {
				if (u->element == t) {//已经到终点则输出 
					cout << path[w] + 1<<endl;
					return;
				}
				path[u->element] = path[w] + 1;//该节点的“层数”为上一层加一 
				q.push(u->element);
				reach[u->element] = 0;//标记为已经到达 
			}
		}
	}
	cout << -1 << endl;
}
int main() {
	int n, m, s, t;
	cin >> n >> m >> s >> t;
	graph g(n);
	for (int i = 0; i < m; i++) {
		int x, u, v;
		cin >> x >> u >> v;
		if (x == 0) {
			g.insert(u, v);
		}
		else if (x == 1) {
			g.erase(u, v);
		}
	}
	for (int i = 1; i <= n; i++) {
		reach[i] = 1;
	}
	g.component();
	for (int i = 1; i <= n; i++) {
		reach[i] = 1;
	}
	g.subgraph();
	for (int i = 1; i <= n; i++) {
		reach[i] = 1;
	}
	g.ldfs(s);
	cout << length << endl;
	for (int i = 1; i <= n; i++) {
		reach[i] = 1;
	}
	g.outdfs(s);
	cout << endl;
	for (int i = 1; i <= n; i++) {
		reach[i] = 1;
	}
	length = 0;
	g.ldfs(t);//dfs和bfs长度相同 
	cout << length << endl;
	for (int i = 1; i <= n; i++) {
		reach[i] = 1;
	}
	g.bfs(t);
	for (int i = 1; i <= n; i++) {
		reach[i] = 1;
	}
	g.minpath(s, t);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值