数据结构实验报告---图的邻接表及其基本算法

1 篇文章 0 订阅
1 篇文章 0 订阅

邻接表的建立

#include<iostream>
#include<algorithm>
using namespace std;
const int MAX = 20;
typedef char datatype;
//边表结点
struct ArcNode
{
	int adjvex;//结点下标
	int weight;//权重
	ArcNode* next;//下一个表结点
};
//头结点
struct VexNode
{
	datatype data;//头结点数据
	ArcNode* firstedge;//与该头结点相邻的第一条边
};
//邻接表图
typedef struct Graph
{
	int numVertexes;//顶点数
	int numEdges;//边数
	VexNode* vnode;//顶点表
}*GraphAdjList;//图指针
struct Path//存储起点到所有临边的权值
{
	int start;
	int end;
	int weight;
};
Path* myPath;//全局数组保存输入的值

创建邻接表图

有头插法和尾插法两种,这里注释了尾插法。
默认为无向图,可自行注释为有向图

//创建邻接表
void createGraph(GraphAdjList& g)
{
	g = new Graph;
	cout << "输入顶点数与边数:\n";
	cin >> g->numVertexes >> g->numEdges;
	myPath = new Path[g->numEdges];
	g->vnode = new VexNode[g->numVertexes];
	cout << "输入顶点数据:\n";
	for (int i = 0; i < g->numVertexes; i++)
	{
		cin >> g->vnode[i].data;
		g->vnode[i].firstedge = nullptr;
	}
	cout << "请输入每条边之间的顶点编号(顶点编号从0开始),以及该边的权重:\n";
	int start, end, weight;
	for (int i = 0; i < g->numEdges; i++)
	{
		cin >> start >> end >> weight;
		myPath[i].start = start;
		myPath[i].end = end;
		myPath[i].weight = weight;
		ArcNode* p = new ArcNode;//头插法
		p->adjvex = end;
		p->next = g->vnode[start].firstedge;
		p->weight = weight;
		g->vnode[start].firstedge = p;
		//无向图对称
		ArcNode* q = new ArcNode;
		q->adjvex = start;
		q->next = g->vnode[end].firstedge;
		q->weight = weight;
		g->vnode[end].firstedge = q;
		//if (start >= 0 && end >= 0)//尾插法
		//{
		//	ArcNode* h = g->vnode[start].firstedge;
		//	ArcNode* p = new ArcNode;
		//	p->adjvex = end;
		//	p->weight = weight;
		//	p->next = nullptr;
		//	if (!h)
		//	{
		//		g->vnode[start].firstedge = p;
		//	}
		//	else
		//	{
		//		while (h->next)//找到链表最后一个元素
		//		{
		//			h = h->next;
		//		}
		//		h->next = p;
		//	}
		//	//无向图对称
		//	ArcNode* hh = g->vnode[end].firstedge;
		//	ArcNode* q = new ArcNode;
		//	q->adjvex = start;
		//	q->weight = weight;
		//	q->next = nullptr;
		//	if (!hh)
		//	{
		//		g->vnode[end].firstedge = q;
		//	}
		//	else
		//	{
		//		while (hh->next)//找到链表最后一个元素
		//		{
		//			hh = hh->next;
		//		}
		//		hh->next = q;
		//	}
		//}
	}

}

DFS遍历

对于连通图,只需要第一个函数即可,第二个函数是为了解决非连通图

void DFS(GraphAdjList g, int* visited, int adjvex)
{
	if (!g)
		return;
	visited[adjvex] = 1;
	cout << g->vnode[adjvex].data<<" ";
	ArcNode* p = g->vnode[adjvex].firstedge;//获取当前顶点指针
	while (p)
	{
		if (!visited[p->adjvex])//未访问则递归
			DFS(g, visited, p->adjvex);
		p = p->next;//否则到下一条相邻路径
	}
}
void DFSGraphAdjList(GraphAdjList g)
{
	int* visited = new int[g->numVertexes];
	for (int i = 0; i < g->numVertexes; i++)//初始化
		visited[i] = 0;
	for (int i = 0; i < g->numVertexes; i++)//连通图与非连通图都能全部访问到
		if (!visited[i])
			DFS(g, visited, i);
}

BFS遍历

对于连通图,只需要第一个函数即可,第二个函数是为了解决非连通图

void BFS(GraphAdjList g, int* visited, int adjvex)
{
	if (!g)
		return;
	int queue[MAX], front = -1, rear = -1;//构造一个循环队列
	cout << g->vnode[adjvex].data<<" ";
	visited[adjvex] = 1;
	rear = (++rear) % MAX;//出队入队都先自加1取模数组最大值,充分利用空间
	queue[rear] = adjvex;
	while (front != rear)//队列不为空
	{
		front = (++front) % MAX;//出队保存
		int temp = queue[front];
		ArcNode* q = g->vnode[temp].firstedge;//获取当前顶点指针
		while (q)//非空时循环
		{
			if (!visited[q->adjvex])//如果未访问
			{
				visited[q->adjvex] = 1;//标记输出再入队
				cout << g->vnode[q->adjvex].data<<" ";
				rear = (++rear) % MAX;
				queue[rear] = q->adjvex;
			}
			q = q->next;//否则到下一条相邻路径
		}
	}
}
void BFSGraphAdjList(GraphAdjList g)
{
	int* visited = new int[g->numVertexes];
	for (int i = 0; i < g->numVertexes; i++)//初始化
		visited[i] = 0;
	for (int i = 0; i < g->numVertexes; i++)//连通图与非连通图都能全部访问到
		if (!visited[i])
			BFS(g, visited, i);
}

Prim最小生成树

void Prim(GraphAdjList g, int begin)
{
	Path* path = new Path[g->numVertexes];
	for (int i = 0; i < g->numVertexes; i++)
	{
		path[i].weight = 0x7fffffff;
	}
	ArcNode* p = g->vnode[begin].firstedge;
	while (p)//构建path数组
	{
		path[p->adjvex].start = begin;
		path[p->adjvex].end = p->adjvex;
		path[p->adjvex].weight = p->weight;
		p = p->next;
	}
	path[begin].weight = -1;//将起点置为-1表示已添加到集合
	int sum = 0;
	cout << "\n\nPrim最小生成树:\n";
	for (int i = 1; i < g->numVertexes; i++)
	{
		int min = 0x7fffffff;
		int index=0;
		for (int j = 0; j < g->numVertexes; j++)//找到权重最小边
			if (path[j].weight != -1)//如果未被添加到集合
				if (path[j].weight < min)
				{
					min = path[j].weight;
					index = j;
				}
		//输出最小权重边信息
		cout << g->vnode[path[index].start].data << "--->"
			<< g->vnode[path[index].end].data << " "
			<< path[index].weight << "\n";
		//将该边终点添加到集合
		sum += path[index].weight;
		path[index].weight = -1;
		//更新path数组
		ArcNode* q = g->vnode[path[index].end].firstedge;
		while (q)
		{
			if (path[q->adjvex].weight > q->weight)
			{
				path[q->adjvex].start = index;
				path[q->adjvex].end = q->adjvex;
				path[q->adjvex].weight = q->weight;
			}
			q = q->next;
		}
	}
	cout << "总权值:" << sum << endl;
}

Kruskal最小生成树

这里需要用到并查集
这里选择使用之前输入的数据
可注释掉自行输入

bool operator < (const Path& a, const Path& b)//直接写cmp函数使用sort函数也可
{
	return a.weight < b.weight;
}
int Find(int* p, int f)
{
	while (p[f] > 0)
		f = p[f];
	return f;
}
void Kruskal(GraphAdjList g)
{
	int n=g->numVertexes, m=g->numEdges;
	int root1, root2;
	Path* path = new Path[m];
	for (int i = 0; i < m; i++)//复制顶点下标及权重
	{
		path[i].start = myPath[i].start;
		path[i].end = myPath[i].end;
		path[i].weight = myPath[i].weight;
	}
	/*cout << "请输入每条边之间的顶点编号(顶点编号从0开始),以及该边的权重:\n";
	for (int i = 0; i < m; i++)
		cin >> path[i].start >> path[i].end >> path[i].weight;*/
	sort(path, path + m);
	int* b=new int[n];     //存放最小生成树的顶点
	int sum = 0;
	memset(b, 0, sizeof(b));
	cout << "\nKruskal最小生成树:\n";
	for (int i = 0; i < m; i++)
	{
		root1 = Find(b, path[i].start);
		root2 = Find(b, path[i].end);
		if (root1 != root2)         //root1==root2代表会形成环
		{
			sum += path[i].weight;
			b[root1] = root2;
			cout << g->vnode[path[i].start].data << "--->" << g->vnode[path[i].end].data << " " << path[i].weight << endl;
		}
	}
	cout << "总权值:" << sum << endl;
}

Dijkstra最短路径

void Dijkstra(GraphAdjList g, int v)
{
	int n = g->numVertexes;
	int* lens=new int[n];//保存最短长度
	int* path= new int[n];
	int* temp = new int[n];//保存临时最短路径
	const int max = ~(1 << 31);//0x7fffffff有符号整型最大值
	//初始化
	for (int i = 0; i < n; i++)
	{
		//lens表示节点是否已确定最短路径
		//这里用-1表示未确定,确认后用实际的最短路径进行填充。
		lens[i] = -1;
		temp[i] = max;//初始所有节点不可达
		path[i] = -1;//保存源节点到目的节点的路径上,目的节点的前一个节点
	}
	ArcNode* p = g->vnode[v].firstedge;
	//使用源节点的邻接边初始化一部分路径长度以及path
	while (p)
	{
		temp[p->adjvex] = p->weight;
		path[p->adjvex] = v;
		p = p->next;
	}
	lens[v] = 0;
	path[v] = 0;
	//开始主循环,每次求得源点到某个顶点的最短路径,并将此节点加入到已确定最短路径的集合中
	for (int i = 1; i < n; i++)
	{
		int index = -1;
		int min = max;
		//选择一条当前最短路径
		for (int j = 0; j < n; j++)
		{
			if (lens[j] < 0 && temp[j] < min)
			{
				min = temp[j];
				index = j;
			}
		}
		if (index == -1)//若没有可达路径,则退出
			break;
		lens[index] = min;//设置节点最短路径为已确定,并将最短路径保存起来

		//根据新确定最短路径的节点的邻接边,更新临时最短路径
		ArcNode* q = g->vnode[index].firstedge;
		while (q)
		{
			//若更小,则更新
			if (lens[q->adjvex] < 0 && min + q->weight < temp[q->adjvex])
			{
				temp[q->adjvex] = min + q->weight;
				path[q->adjvex] = index;
			}
			q = q->next;
		}
	}
	cout << "\nDijkstra最短路径:\n";
	for (int i = 0; i < n; i++)
		cout << g->vnode[v].data <<"--->"<<g->vnode[i].data<<" "<<lens[i] <<endl;
}

拓扑排序

这里用到之前的全局数组myPath来表示有向图

void TopologicalSort(GraphAdjList g)//拓扑排序
{
	int count = 0;
	int* myStack = new int[g->numVertexes];//创建简单的栈
	int top = -1;
	int* inPoint = new int[g->numVertexes];//保存各个顶点的入队
	for (int i = 0; i < g->numVertexes; i++)
		inPoint[i] = 0;
	for (int i = 0; i < g->numEdges; i++)//计算所有顶点入度
	{
		inPoint[myPath[i].end]++;
	}
	//for (int i = 0; i < g->numVertexes; i++)
	//	cout << inPoint[i] << " ";
	for (int i = 0; i < g->numVertexes; i++)//入度为0入栈
	{
		if (inPoint[i] == 0)
			myStack[++top] = i;
	}
	cout << "\n拓扑排序:\n";
	while (top != -1)//栈不为空
	{
		int index=myStack[top--];//出栈
		cout << g->vnode[index].data<<" ";
		count++;
		for (int i = 0; i < g->numEdges; i++)//将该出边终点顶点入队减1,减1后为0的入栈
		{
			if (myPath[i].start == index)
			{
				inPoint[myPath[i].end]--;
				if (inPoint[myPath[i].end] == 0)
					myStack[++top] = myPath[i].end;
			}		
		}
	}
	if (count == g->numVertexes)
		cout << "\n能够完成拓扑排序!\n";
	else
		cout<< "\n不能够完成拓扑排序!\n";
}

打印邻接表

void print(GraphAdjList g)
{
	cout << "\n输出邻接表:\n";
	for (int i = 0; i < g->numVertexes; i++)
	{
		cout << g->vnode[i].data << " ";
		ArcNode* p = g->vnode[i].firstedge;
		while (p)
		{
			cout << "<" << g->vnode[i].data << "," << g->vnode[p->adjvex].data << "> =" << p->weight << " ";
			p = p->next;
		}
		cout << "^\n";
	}
}

主函数

int main()
{
	GraphAdjList g;
	createGraph(g);//无向图
	print(g);
	cout << "\n\nDFS:";
	DFSGraphAdjList(g);
	cout << "\n\nBFS:";
	BFSGraphAdjList(g);
	Prim(g, 0);//最小生成树仅针对无向连通图,有向图可能不连通,不能求最小生成树
	Kruskal(g);
	Dijkstra(g, 0);
	TopologicalSort(g);//针对有向图,所以需要处理对称的数据,变成有向图
	return 0;
}
//测试数据
//6 10
//abcdef
//0 1 6
//0 2 1
//0 3 5
//1 2 5
//1 4 3
//2 3 5
//2 4 6
//2 5 4
//3 5 2
//4 5 6

测试结果

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值