数据结构——图(邻接链表)

邻接链表

邻接矩阵是不错的⼀种图存储结构,但是我们也发现,对于边数相对顶点较少的图,这种结构是存在对存储空间的极大浪费的。比如说,如果我们要处理下图这样的稀疏有向图,邻接矩阵中除了arc[1][0]有权值外,没有其他弧,其实这些存储空间都浪费掉了。
数据结构——图(邻接链表)_第1张图片
因此选择一种新的数据结构来存储这种稀疏图则尤为重要了。此时则使用链表结构来存储原来的连接信息。
数据结构——图(邻接链表)_第2张图片

定义如下数据结构

//图的邻接链表存储结构

//边表节点结构,一个adjvex用来存储邻接点的位置,一个next指针用来指向下一个节点
typedef struct EdgeNode
{
	int adjvex;  //存储顶点下标信息
	struct EdgeNode *next;
} EdgeNode;

//顶点表节点结构
typedef struct
{
	string Vexs;  //用来存储顶点信息
	EdgeNode *firstedge;  //用来存储当前顶点的下一个顶点
} VexList;

//这里用动态数组存储顶点表,然后numVertex,numEdge是一个图的顶点数和边数
typedef struct
{
	vector<VexList> VexList;
	int Vertexs, Edges;
} GraphList;

创建图

此处创建边表使用的方法是尾插法,《大话数据结构》中使用的是头插法,个人感觉尾插法比较容易理解。其他的方面和邻接矩阵创建图时的思路基本一样。

void CreateGraph(GraphList * G)
{
	string v1, v2;
	EdgeNode * e, *p, *q;
	cout << "请输入顶点数和边数:" << endl;
	cin >> G->Vertexs >> G->Edges;
	cout << "请输入顶点的信息:" << endl;
	for (int i = 0; i < G->Vertexs; ++i){
		VexList tmp;
		cin >> tmp.Vexs;
		tmp.firstedge = NULL;
		G->VexList.push_back(tmp);
	}
	for (int k = 0; k < G->Edges; ++k){
		int i, j;//(Vi,Vj)
		cout << "请输入边(Vi,Vj):" << endl;
		cin >> i >> j;
		if (G->VexList[i].firstedge == NULL){//当前顶点i后面没有顶点
			e = new EdgeNode;
			e->adjvex = j;
			e->next = NULL;
			G->VexList[i].firstedge = e;
		}
		else{  //当前i后面有顶点
			EdgeNode *p = G->VexList[i].firstedge;
			while (p->next){
				p = p->next;
			}
			e = new EdgeNode;
			e->adjvex = j;
			e->next = NULL;
			p->next = e;
		}
		//因为是无向图,所以(Vi,Vj)与(Vj,Vi)都要连接起来
		if (G->VexList[j].firstedge == NULL){ //当前顶点j后面没有顶点
			e = new EdgeNode;
			e->adjvex = i;
			e->next = NULL;
			G->VexList[j].firstedge = e;
		}
		else{  //当前j后面有顶点
			EdgeNode *p = G->VexList[j].firstedge;
			while (p->next){
				p = p->next;
			}
			e = new EdgeNode;
			e->adjvex = i;
			e->next = NULL;
			p->next = e;
		}
	}
}

同样也写了一个打印图的函数。

//打印连接链表
void PrintGraph(GraphList *G)
{
	cout << "所建立的邻接表如以下所示:" << endl;
	for (int i = 0; i<G->Vertexs; i++){
		cout << G->VexList[i].Vexs;             //先输出顶点信息
		EdgeNode * e = G->VexList[i].firstedge;
		while (e){                           //然后就开始遍历输出每个边表所存储的邻接点的下标
			cout << "-->" << e->adjvex;
			e = e->next;
		}
		cout << endl;
	}
}

此时也需要一个全局访问数组

bool Visited_List[100];

深度优先遍历(DFS)

与基于邻接矩阵的DFS基本无差别,废话不都说,直接上代码。

//DFS
void DFS(GraphList *G, int i)
{

	EdgeNode * p;
	Visited_List[i] = true;
	cout << G->VexList[i].Vexs << "  ";
	p = G->VexList[i].firstedge;
	while (p){
		if (!Visited_List[p->adjvex])
			DFS(G, p->adjvex);
		p = p->next;
	}	
}

void DFSTraver(GraphList *G)
{
	cout << "深度优先遍历顺序:" << endl;
	for (int i = 0; i<G->Vertexs; ++i)
		Visited_List[i] = false;
	for (int i = 0; i<G->Vertexs; ++i){
		if (!Visited_List[i])
			DFS(G, i);
	}
	cout << endl;
}

广度优先遍历(BFS)

此处的思路和邻接矩阵的基本一样,就是边表的遍历此时是用链表的方式遍历。

//BFS  特点要使用队列,很像树的层序遍历
void BFSTraver(GraphList *G)
{
	cout << "广度优先遍历顺序:" << endl;
	EdgeNode * p;
	queue<int> Q;
	for (int i = 0; i<G->Vertexs; ++i)
		Visited_List[i] = false;
	for (int i = 0; i<G->Vertexs; ++i){
		if (!Visited_List[i]){
			Visited_List[i] = true;
			cout << G->VexList[i].Vexs << "  ";
			Q.push(i);
			while (!Q.empty()){
				i = Q.front();
				Q.pop();
				p = G->VexList[i].firstedge;
				while (p){//把当前顶点下相连接的点找完
					if (!Visited_List[p->adjvex]){
						Visited_List[p->adjvex] = true;
						cout << G->VexList[p->adjvex].Vexs << "  ";
						Q.push(p->adjvex);
					}
					p = p->next;
				}
			}
		}
	}
}

测试一下

emmmm ,又到了测试的时候了。。。采取同样的测试数据,输入如下图的数据。
数据结构——图(邻接链表)_第3张图片
结果:
数据结构——图(邻接链表)_第4张图片
over!!!
参考了一下前人的工作

你可能感兴趣的:(数据结构)