“六度空间”理论又称作“六度分隔(Six Degrees of Separation)”理论。这个理论可以通俗地阐述为:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过五个人你就能够认识任何一个陌生人。”如图1所示。
![](https://i-blog.csdnimg.cn/blog_migrate/35f37373dc62dccfc445c5f63fa7acaf.png)
图1 六度空间示意图
“六度空间”理论虽然得到广泛的认同,并且正在得到越来越多的应用。但是数十年来,试图验证这个理论始终是许多社会学家努力追求的目标。然而由于历史的原因,这样的研究具有太大的局限性和困难。随着当代人的联络主要依赖于电话、短信、微信以及因特网上即时通信等工具,能够体现社交网络关系的一手数据已经逐渐使得“六度空间”理论的验证成为可能。
假如给你一个社交网络图,请你对每个节点计算符合“六度空间”理论的结点占结点总数的百分比。
输入格式:
输入第1行给出两个正整数,分别表示社交网络图的结点数N(1<N≤104,表示人数)、边数M(≤33×N,表示社交关系数)。随后的M行对应M条边,每行给出一对正整数,分别是该条边直接连通的两个结点的编号(节点从1到N编号)。
输出格式:
对每个结点输出与该结点距离不超过6的结点数占结点总数的百分比,精确到小数点后2位。每个结节点输出一行,格式为“结点编号:(空格)百分比%”。
输入样例:
10 9
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
输出样例:
1: 70.00%
2: 80.00%
3: 90.00%
4: 100.00%
5: 100.00%
6: 100.00%
7: 100.00%
8: 90.00%
9: 80.00%
10: 70.00%
个人分析:六度空间,要求输出关系网中每个人满足“六度空间”的百分比(每个结点输出与该结点距离不超过6的结点数占结点总数的百分比)。每个点都必须遍历,每个点都要作为起点进行一次搜索,搜索最近的6层中有多少个结点。这么一看,有清晰的“6层”要求,那应该是用BFS了。有一个棘手的问题:BFS过程中如何知道已经到哪一层了?因为我们只需要知道6层共有多少结点,在Level到达6时,直接退出BFS即可。
解决层数的问题之后,只需要在每入队一个点后使计数变量count++即可。
代码如下:
#include<stdio.h>
#include<stdlib.h>
#define MaxVertex 10000
int Visit[MaxVertex]={0};
typedef int Vertex;
typedef int WeightType;
//边定义
typedef struct ENode *Edge;
struct ENode{
Vertex V1,V2;
WeightType Weight;
};
//邻接点定义
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode{
Vertex AdjV;
WeightType Weight;
PtrToAdjVNode Next;
};
//图结点定义
typedef struct GNode *LGraph;
struct GNode{
int Nv;
int Ne;
PtrToAdjVNode G[MaxVertex];
};
//队列定义
typedef struct QNode *Queue;
struct QNode{
int *Data;
int Front,Rear;
int Maxsize;
};
void Add(Queue Q,int X)
{
if((Q->Rear+1)%Q->Maxsize!= Q->Front)
{
Q->Rear = (Q->Rear+1)%Q->Maxsize;
Q->Data[Q->Rear] = X;
}
}
bool IsEmpty(Queue Q)
{
return (Q->Front==Q->Rear);
}
int Delete(Queue Q)
{
if(!IsEmpty(Q))
{
Q->Front =(Q->Front+1)%Q->Maxsize;
return Q->Data[Q->Front];
}
}
//邻接表图初始化
LGraph CreateGraph(int VertexNum)
{
Vertex V;
LGraph Graph;
Graph=(LGraph)malloc(sizeof(struct GNode));
Graph->Nv = VertexNum;
Graph->Ne = 0;
for(V=0;V<Graph->Nv;V++)
Graph->G[V]=NULL;
return Graph;
}
//插入边
void InsertEdge(LGraph Graph,Edge E)
{
PtrToAdjVNode NewNode;
NewNode = (PtrToAdjVNode)malloc(sizeof(AdjVNode));
NewNode->AdjV=E->V2;
NewNode->Weight=E->Weight;
NewNode->Next=Graph->G[E->V1];
Graph->G[E->V1]=NewNode;
NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
NewNode->AdjV = E->V1;
NewNode->Weight = E->Weight;
/* 将V1插入V2的表头 */
NewNode->Next = Graph->G[E->V2];
Graph->G[E->V2] = NewNode;
}
//根据输入建图
LGraph BuildGraph()
{
LGraph Graph;
Edge E;
Vertex V;
int Nv,i;
scanf("%d ",&Nv);
Graph=CreateGraph(Nv);
scanf("%d",&Graph->Ne);
getchar();
if(Graph->Ne!=0)
{
E=(Edge)malloc(sizeof(struct ENode));
for(i=0;i<Graph->Ne;i++)
{
scanf("%d %d",&E->V1,&E->V2);
E->V1--;
E->V2--;
E->Weight=1;
InsertEdge(Graph,E);
}
}
return Graph;
}
//BFS广度优先搜索
double BFS(LGraph Graph,Vertex V)
{
Visit[V]=1;
int level=0;
int last=V;int tail;
double count=1;
Queue q=(Queue)malloc(sizeof(struct QNode));
q->Data=(int *)malloc(MaxVertex*sizeof(int));
q->Front=q->Rear=0;
q->Maxsize=MaxVertex;
PtrToAdjVNode W;
Add(q,V);
while(!IsEmpty(q))
{
V=Delete(q);
for(W=Graph->G[V];W!=NULL;W=W->Next)
{
if(!Visit[W->AdjV])
{
Visit[W->AdjV]=1;
Add(q,W->AdjV);
count++;
tail=W->AdjV;
}
}
if(V==last)
{
level++;
last=tail;
}
if(level==6) break;
}
return count;
}
//六度空间函数
void SDS(LGraph Graph)
{
int i,j;
double count;
int test=Graph->Nv;
for(i=0;i<Graph->Nv;i++)
{
count=BFS(Graph,i);
printf("%d: %.2lf%%\n",i+1,100*count/Graph->Nv);
for(j=0;j<Graph->Nv;j++)
Visit[j]=0;
}
}
int main()
{
LGraph Graph=BuildGraph();
SDS(Graph);
return 0;
}
测试结果:
总结:就像之前所说的,在BFS搜索的过程中,添加一些判断条件,就能实现很多功能。在这道题里,通过记录入队层最后结点/出队层最后结点,实现了实时记录搜索的层数;通过添加count++,很简单的记录了遍历的结点数目。短短十几行的BFS,也能有很多的变数,菜鸡还不够熟练不敏感,对于功能实现还要一步一步跟着遍历搜索过程推演,才能发现如何设置判断条件。一句话,还是要多练!!