数据结构之六度空间
实验题目:
“六度空间”理论又称作“六度分隔(Six Degrees of Separation)”理论。这个理论可以通俗地阐述为:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过五个人你就能够认识任何一个陌生人。”
也就是在无向图中,以节点作为人,两个节点之间的路径小于等于6。
实验要求
求出与一个节点满足上述要求的其他节点的数目,初始值为1
如图 以A, B为例
A: 没有与A满足上述条件的点.因此。A节点的值为0
B: D C E H G F都满足条件 因此B节点的值就是7
实验思路:
即无向图的广度优先遍历
1.图的存储
书上讲过无向图的存储最好是邻接多重链表,但是看起来比较麻烦,我就偷懒用了邻接表来存储
创建方式,定义表头节点和表节点
typedef struct EdgeNode { int adjvex; struct EdgeNode * next; } EdgeNode; //顶点表节点结构,tag用来判定是否被遍历过,一个firstedge是用来指向边表的第一个节点 typedef struct { int tag; EdgeNode * firstedge; } AdjList;
定义表头节点的数组,表的初始化就完成了。
存储图,忽略节点名字,以数组下标代替节点名。tag初始为0,输入一条边的头尾节点,在头尾节点对应的表头节点后加入信息。
实现代码
void add(int x, int y) { if (adjList[x - 1].firstedge == NULL) { EdgeNode *p; p = new EdgeNode; p->adjvex = y - 1; p->next = NULL; adjList[x - 1].firstedge = p; } else { EdgeNode *p , *q; p = adjList[x - 1].firstedge; while (p->next) { p = p->next; } q = new EdgeNode; q->adjvex = y - 1; q->next = NULL; p->next = q; } // 无向图,一条边对应两个结点 if (adjList[y - 1].firstedge == NULL) { EdgeNode *p; p = new EdgeNode; p->adjvex = x - 1; p->next = NULL; adjList[y - 1].firstedge = p; } else { EdgeNode *p, *q; p = adjList[y - 1].firstedge; while (p->next) { p = p->next; } q = new EdgeNode; q->adjvex = x - 1; q->next = NULL; p->next = q; } }
2.图的广度优先遍历
在上述存储结构的前提下,以每个节点为初始节点,依次进行广度优先遍历
思路:定义两个队列,用来存储本次访问节点的下标和下次访问节点的下标,以便于计算路径长度。在本次访问节点队列不空的情况下,再次进行循环,直到空为止,循环第一次结束。依次类推
下面以图的形式具体说明
第一次以0下标开始,tag置为1,表示被访问过。本次队列和下次队列都为null
首先进行本次队列赋值,此时路径为1
之后再本次队列不为空并且路径小于6的前提下进行另一个循环
①以本次队列中的数据作为起始位置,若遍历的节点中,tag为0,则该节点进入下次队列,直到本次队列空为止。此时数据的状态为
此时将下次队列的值赋给本次队列,此时路径变为2
继续进行循环①
①循环结束之后 将所有tag置为0,队列清空进行以下一个节点为初始节点的计算,重复上述过程直到循环结束
实现代码
void BL(int x) { // 数组下标对应到存储改序号节点路径不超过6的节点个数 double num[10]; for (int i = 0; i < x; i++) { num[i] = 1; } queue<int>q; queue<int>r; for (int i = 0; i < num_vertex; i++) { // 从第一个节点开始,依次广度遍历图,k为当前节点到各节点的路径长度 int k = 0; EdgeNode *p; // 记录是否访问过该点 adjList[i].tag = 1; p = adjList[i].firstedge; // 记录路径长度为1的节点个数 while (p) { if (adjList[p->adjvex].tag == 0) { num[i]++; adjList[p->adjvex].tag = 1; q.push(p->adjvex); } if (!p->next) { break; } if (p->next) { p = p->next; } } k++; // 记录路径长度为2~6的节点个数,q队列中存入本次次广度遍历的节点,r队列存入下一次广度 // 遍历的节点。来区分不同的路径长度 while (!q.empty() && k < 6) { p = adjList[q.front()].firstedge; q.pop(); while (p) { if (adjList[p->adjvex].tag == 0) { num[i]++; adjList[p->adjvex].tag = 1; r.push(p->adjvex); } if (!p->next) { break; } else p = p->next; } if (q.empty() && !r.empty()) { while (!r.empty()) { q.push(r.front()); r.pop(); } k++; } } // 初始化数据,准备进行从下一个节点开始的广度遍历 for (int j = 0; j < num_vertex; j++) { adjList[j].tag = 0; } while (!q.empty()) { q.pop(); } while (!r.empty()) { r.pop(); } }