数据结构:图的存储结构和遍历

大一学的数据结构现在印象有点模糊了,于是想重新整理一下那些我用的还不是很熟练的数据结构

一些概念

  • 连通分量:无向图中的极大连通子图。一个连通图只有一个极大连通子图,即它自身。一个非连通图可以有多个连通分量。极大是因为如果此时加入任何一个不在该连通分量中的点集中的点都会导致它不再连通
  • 极小连通子图:一个连通图的生成树是一个极小连通子图,它含有图中全部n个顶点,有n-1条边。极小是因为如果此时删除生成树中的任何一条边都会导致生成树不再连通
目录
1. 图的邻接矩阵
#define MAXVERTEX 100   //图的最大顶点数
#define INFINITY 32767  //用有符号的int最大值表示无穷大
typedef char vertextype;    //定义定点的存储信息为字符型
typedef int arctype;    //定义边的权值为int型
 
//图的邻接矩阵的存储结构
typedef struct
{
    vertextype vertex[MAXVERTEX];   //顶点表
    arctype arc[MAXVERTEX][MAXVERTEX];  //邻接矩阵
    int vertexnum;  //图的当前顶点数
    int arcnum; //图的当前边数
}MGraph;
 
//创建无向网
void CreateMGraph(MGraph &G)
{
    cin >> G.vertexnum >> G.arcnum; //输入顶点数目和边数
    for(int i = 0; i < G.vertexnum; i++)    //输入顶点信息
        cin >> G.vertex[i];
    for(int i = 0; i < G.vertexnum; i++)    //将所有边初始化为无穷大
        for(int j = 0; j < G.vertexnum; j++)
            G.arc[i][j] = INFINITY;
    for(int k = 0; k < G.arcnum; k++)
    {
        int i, j, w;
        cin >> v1 >> v2 >>w;  //输入构成边的两个顶点及对应权值
        i = LocateVex(G,v1);
        j = LocateVex(G,v2);
        G.arc[i][j] = w;
        G.arc[j][i] = G.arc[i][j];  //无向图的邻接矩阵为对称矩阵
    }
}

2. 图的邻接表

首先来看一下结构体定义

#define MAX_VERTEX_NUM 10
typedef struct EdgeNode{  //边表结点
	int adjvex;  //结点的索引
	struct EdgeNode* next;  //这条边指向的下一个顶点
}EdgeNode;

typedef struct VertexNode { //各顶点信息
	char data;
	struct EdgeNode* firstEdge;
}VertexNode,AdjList[MAX_VERTEX_NUM];

typedef struct ALGragh{
	AdjList vertices;  //邻接表
	int vertexNum, edgeNum;  //顶点数和边数
}ALGragh;

接下来是创建邻接表的函数,这里给出的是无向图的邻接表的创建函数

void CreateGragh(ALGragh &G)
{
	char s, e;
	int j, k;
	EdgeNode* node1=NULL;
	EdgeNode* node2 = NULL;
	cout << "输入结点个数和边数:";
	cin >> G.vertexNum >> G.edgeNum;  //输入结点个数和边数
	cout << "输入各个顶点:";
	for (int i = 0; i < G.vertexNum; i++) {
		cin >> G.vertices[i].data;
		G.vertices[i].firstEdge = NULL;
	}
	cout<<"输入边的起点和终点"<<endl;
	for (int i = 0; i < G.edgeNum/2; i++) {  
		cin >> s >> e;
		j = locateVex(s,G);
		k = locateVex(e,G);
		node1 = new EdgeNode;
		node1->adjvex = k;
		node1->next =G.vertices[j].firstEdge;  //头插法
		G.vertices[j].firstEdge = node1;
		node2 = new EdgeNode;
		node2->adjvex = j;
		node2->next = G.vertices[k].firstEdge;
		G.vertices[k].firstEdge = node2;
		
	}
}

int locateVex(char data,ALGragh G)
{
	for (int i = 0; i < G.vertexNum; i++) {
		if (G.vertices[i].data == data)
			return i;
	}
}

2. 图的深度优先遍历

需要注意的是图可能是连通的也可能是非连通的。因此我们将图遍历一次后必须要确保所有的结点都被访问过了,否则需要进行二次遍历。深度优先遍历借助于栈操作。
加一张图片方便理解
在这里插入图片描述

int* visited;  //访问数组的标志
void DFS(ALGragh G, char start)//递归深度遍历
{
	int s = locateVex(start,G);
	cout << G.vertices[s].data << " ";
	visited[s] = 1;
	EdgeNode* p = G.vertices[s].firstEdge;
	while (p) {
		if (!visited[p->adjvex])
			DFS(G, G.vertices[p->adjvex].data);
		p = p->next;
	}
}

void DFSTraverse(ALGragh G)
{
	int i ;
	visited = new int[G.vertexNum];
	for (int j = 0; j < G.vertexNum; j++)
		visited[j] = 0;  //将访问数组初始化为0
	for (i = 0; i < G.vertexNum; i++) {
		if (!visited[i])  //没有被访问的顶点,将从这个顶点开始继续深度遍历
			DFS(G, G.vertices[i].data);
	}	
}
3. 图的广度优先遍历

同样图可能是连通的也可能是非连通的。因此我们将图遍历一次后必须要确保所有的结点都被访问过了,否则需要进行二次遍历。广度优先遍历借助于队列操作。
在这里插入图片描述

int* bfs_visited;
void BFSTraverse(ALGragh G)
{
	queue<int> Q;
	EdgeNode* p;
	bfs_visited = new int[G.vertexNum];
	for (int i = 0; i < G.vertexNum; i++)
		bfs_visited[i] = 0;

	for (int i = 0; i < G.vertexNum; i++) {
		if (!bfs_visited[i]) {
			bfs_visited[i] = 1;
			cout << G.vertices[i].data << " ";
			Q.push(i);

			while (!Q.empty()) {
				int loc = Q.front();  //获取队头元素
				Q.pop();  //将队头元素弹出,C++标准库中的pop无返回值
				 p = G.vertices[loc].firstEdge;
				if (!bfs_visited[p->adjvex]) {
					while (p) {
						if (!bfs_visited[p->adjvex]) {
							bfs_visited[p->adjvex] = 1;
							Q.push(p->adjvex);
							cout << G.vertices[p->adjvex].data << " ";
						}
						p = p->next;
					}
				}
			
			}
		}
	}
}

4.拓扑排序

拓扑排序是将偏序变为全序,有向无环图才可能有拓扑排序。基本思路就是首先将入度为0的结点全部加入一个栈(或者队列)中,然后栈顶出栈,将该结点的相邻结点的入度减一。重复上述过程,直到所有入度为0的结点出栈。注意图的存储结构,VertexNode需要补充一个入度信息in

void Topological(ALGragh G)
{
	bool flag=true;
	stack<int> S;
	int i,count=0; //count用于记录输出的顶点的个数
	for (i = 0; i < G.vertexNum; i++) {//将所有入度为0的结点入栈
		if (G.vertices[i].in == 0) {
			S.push(i);
			flag = false;
		}
	}
	if (flag) {
		cout << "该图不是有向图" << endl;
		return;
	 }
	while (!S.empty()) {
		int loc = S.top();  
		S.pop();  //栈顶结点出栈
		cout << G.vertices[loc].data << " ";
		count++;  
		EdgeNode* p = G.vertices[loc].firstEdge;
		while (p) {  //将其所有相邻结点的入度减1,若减1后为0就入栈
			if (--G.vertices[p->adjvex].in == 0)
				S.push(p->adjvex);
			p = p->next;
		}
	}
	if (count == G.vertexNum)
		cout << "排序成功" << endl;
	else cout << "该图不能拓扑排序" << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值