7-5 六度空间(C语言版详解)

“六度空间”理论又称作“六度分隔(Six Degrees of Separation)”理论。这个理论可以通俗地阐述为:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过五个人你就能够认识任何一个陌生人。”如图1所示。


图1 六度空间示意图

“六度空间”理论虽然得到广泛的认同,并且正在得到越来越多的应用。但是数十年来,试图验证这个理论始终是许多社会学家努力追求的目标。然而由于历史的原因,这样的研究具有太大的局限性和困难。随着当代人的联络主要依赖于电话、短信、微信以及因特网上即时通信等工具,能够体现社交网络关系的一手数据已经逐渐使得“六度空间”理论的验证成为可能。

假如给你一个社交网络图,请你对每个节点计算符合“六度空间”理论的结点占结点总数的百分比。

输入格式:

输入第1行给出两个正整数,分别表示社交网络图的结点数N(1<N≤103,表示人数)、边数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%

代码长度限制

16 KB

时间限制

2500 ms

内存限制

64 MB

解题思路:
        根据题意,需要用到队列的结构,图的存储方式有邻接矩阵邻接表两种,根据题意,

因为使用邻接矩阵所需要最大空间太大,所以邻接表更适合当前情况。

1.邻接表设置的简要解释:

        邻接表由表头结点和邻接表组成,data存放出发结点的名称,*first存放了一条链,链内存放各个能从出发结点有连接的其他结点。最后图n,m中存放结点数和边数,Linjie存放整个邻接表。

//定义邻接表的节点
typedef struct ArcNode
{
    //结点名
    int name;
    //下一个结点
    struct ArcNode *next;
    //该节点是否被选中过
    int info;
}ArcNode;

//定义邻接表结构
typedef struct Grape
{
    //顶点的信息
    int data;
    //顶点所能到达结点的集合
    ArcNode *first;
}VNode, List[MaxSize];//所有顶点都包含

//图
typedef struct
{
    List LinJie;
    int n, m;
}ALGraph;

2.邻接表创建:

         创建邻接表需要图G,结点数n和边数m。

        首先,将n和m输入到图中,因为题中结点的名称编号从1开始,我们便也从1开始为邻接表的头结点依次赋值。一开始还没输入边的信息时,将头结点对应的链表指向NULL方便操作。

        然后,输入可以连通的两个结点,通过两个结点的编号确定他们对应的头结点,将两个结点互相添加到各自链表中去(头插法即可)。

//创建邻接表
bool CreateUDG(ALGraph* G, int n, int m)
{
    int i, j, k;
    //先输入总顶点数和总边数
    G->n = n;
    G->m = m;
    //scanf("%d %d", &G->n, &G->m);
    //输入顶点值(从下表1开始存储的)
    for(i = 1; i <= G->n; i++)
    {
        //每个顶点
        G->LinJie[i].data = i;
        //初始化头结点指针为NULL
        G->LinJie[i].first = NULL;
    }
    //输入各边,构造邻接矩阵
    for(k = 0; k < G->m; k++)
    {
        //每条边依附的两个点
        int v1, v2;
        scanf("%d %d", &v1, &v2);
        //确定v1和v2所在顶点的位置
        i = v1; j = v2;
        //生成一个新的边节点
        ArcNode* p1 = (ArcNode*)malloc(sizeof(ArcNode));
        p1->name = j;

        //把新节点插入头部
        p1->next = G->LinJie[i].first;
        G->LinJie[i].first = p1;

        //生成一个新的边节点
        ArcNode* p2 = (ArcNode*)malloc(sizeof(ArcNode));
        p2->name = i;

        //把新节点插入头部
        p2->next = G->LinJie[j].first;
        G->LinJie[j].first = p2;
    }
    return true;
}

3.广度优先遍历 :

        广度优先遍历需要图G和出发点v。visited数组来判断是否被选择过。队列用来输出答案。

        首先,用结点st存放出发点的数据,cur代表结点名,step代表距离出发点的阶数。初始化visited数组都为0,代表都未被选择过,先将出发点v的visited值改为true,代表选择过了。把st压入队列。

        然后,为了输出全部路径,设置队长 == 0 为循环条件,但题目要求只需要连六个人,所以当出队的值的step == 6 时便跳出循环即可。p结点指针指向临界表链中的第一个元素,将邻接表中所有元素都入队

//广度优先遍历
int BFS(ALGraph* G, int v)
{
    Node st,pt,curq;
    st.cur = v;
    st.step = 0;

    //用c来记一共有多少六步能达到的结点
    int c = 1;

    //先让判断是否选中的数组都等于零
    bool visited[MaxSize] = {0};
    //临时遍历u用来放当前结点值,
    int u;
    //搞一个队列用来输出
    Queue Q;
    InitQueue(&Q);
    //出发点先弄上
    visited[v] = true;
    EnQueue(&Q, st);
    //一个临时存放邻接表结点
    ArcNode* p;

    //队列空就结束循环
    while(LengthQueue(Q))
    {
        DeQueue(&Q, &curq);
        

        //根据题意,到六跳出循环
        if(curq.step == 6) break;
        
        //指向邻接表的链表第一个元素
        p = G->LinJie[curq.cur].first;

        //当链表到尾部的NULL了结束循环
        while(p)
        {
            pt.step = curq.step;
            pt.cur = p->name;
            if(!visited[p->name])
            {
                c++;
                pt.step++;
                EnQueue(&Q, pt);
                visited[p->name] = true;
            }
            p = p->next;
        }
    }
    return c;
}

4.所有代码:

#include <stdio.h>
#include <stdlib.h>

#define bool char
#define true 1
#define false 0
#define MaxSize 10000


/****************队列结构*******************/
typedef struct Node
{
    int step;
    int cur;
}Node;

typedef struct Queue
{
    Node *data;
    int front;
    int rear;
}Queue;

//初始化
bool InitQueue(Queue* Q)
{
    Q->front = Q->rear = 0;
    Q->data = (Node*)malloc(sizeof(Node)*MaxSize);
    return true;
}

//入队
bool EnQueue(Queue* Q, Node e)
{
    if((Q->rear+1)%MaxSize == Q->front) return false;
    Q->data[Q->rear] = e;
    Q->rear = (Q->rear+1)%MaxSize;

    return true;
}

//出队
bool DeQueue(Queue* Q, Node* e)
{
    if(Q->rear == Q->front) return false;
    *e = Q->data[Q->front];
    Q->front = (Q->front+1)%MaxSize;
    return true;
}

//打印队列元素
bool PrintQueue(Queue Q)
{
    for(int i = 0; i < (Q.rear-Q.front+MaxSize)%MaxSize; i++)
    {
        if(i == 0)
            printf("%d", Q.data[i].cur);
        else
            printf(" %d", Q.data[i].cur);
    }
    return true;
}

//队列长度
int LengthQueue(Queue Q)
{
    return (Q.rear-Q.front+MaxSize)%MaxSize;
}



/****************邻接表结构*******************/


//定义邻接图的节点
typedef struct ArcNode
{
    //结点名
    int name;
    //下一个结点
    struct ArcNode *next;
    //该节点是否被选中过
    int info;
}ArcNode;

//定义邻接图结构
typedef struct Grape
{
    //顶点的信息
    int data;
    //顶点所能到达结点的集合
    ArcNode *first;
}VNode, List[MaxSize];//所有顶点都包含

//邻接表
typedef struct
{
    List LinJie;
    int n, m;
}ALGraph;

//创建邻接表
bool CreateUDG(ALGraph* G, int n, int m)
{
    int i, j, k;
    //先输入总顶点数和总边数
    G->n = n;
    G->m = m;
    //scanf("%d %d", &G->n, &G->m);
    //输入顶点值(从下表1开始存储的)
    for(i = 1; i <= G->n; i++)
    {
        //每个顶点
        G->LinJie[i].data = i;
        //初始化头结点指针为NULL
        G->LinJie[i].first = NULL;
    }
    //输入各边,构造邻接矩阵
    for(k = 0; k < G->m; k++)
    {
        //每条边依附的两个点
        int v1, v2;
        scanf("%d %d", &v1, &v2);
        //确定v1和v2所在顶点的位置
        i = v1; j = v2;
        //生成一个新的边节点
        ArcNode* p1 = (ArcNode*)malloc(sizeof(ArcNode));
        p1->name = j;

        //把新节点插入头部
        p1->next = G->LinJie[i].first;
        G->LinJie[i].first = p1;

        //生成一个新的边节点
        ArcNode* p2 = (ArcNode*)malloc(sizeof(ArcNode));
        p2->name = i;

        //把新节点插入头部
        p2->next = G->LinJie[j].first;
        G->LinJie[j].first = p2;
    }
    return true;
}

//遍历图
bool Print(ALGraph G, int n)
{
    int i = 0;
    for(i = 1; i <= n; i++)
    {
        //输出顶点名
        printf("%d:",G.LinJie[i].data);
        ArcNode* p = G.LinJie[i].first;
        while(p)
        {
            printf("%d->", p->name);
            p = p->next;
        }
        printf("\n");
    }
    return true;
}

//广度优先遍历
int BFS(ALGraph* G, int v)
{
    Node st,pt,curq;
    st.cur = v;
    st.step = 0;
    int c = 1;
    //先让判断是否选中的数组都等于零
    bool visited[MaxSize] = {0};
    //临时遍历u用来放当前结点值,
    int u;
    //搞一个队列用来输出
    Queue Q;
    InitQueue(&Q);
    //出发点先弄上
    visited[v] = true;
    EnQueue(&Q, st);
    //一个临时存放邻接表结点
    ArcNode* p;
    while(LengthQueue(Q))
    {
        DeQueue(&Q, &curq);

        //printf("%d->",curq.cur);
        if(curq.step == 6) break;

        p = G->LinJie[curq.cur].first;

        while(p)
        {
            pt.step = curq.step;
            pt.cur = p->name;
            if(!visited[p->name])
            {
                c++;
                pt.step++;
                EnQueue(&Q, pt);
                visited[p->name] = true;
            }
            p = p->next;
        }
    }
    return c;
}








int main()
{
    ALGraph G;
    int n, m;
    scanf("%d %d",&n,&m);
    CreateUDG(&G, n, m);

    //Print(G, 10); //测试用遍历
    //printf("\n");
    for(int i = 1; i <= n; i++)
    {
        float s;

        s = BFS(&G, i);

        printf("%d: %.2f%%\n", i, s/n*100);
    }




    return 0;
}

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百年bd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值