数据结构之六度空间

实验题目:

“六度空间”理论又称作“六度分隔(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();
        }
    }