存储顶点的所有邻居,快速找到顶点的邻居

Store the all the neighbors of the vertexes, find a vertex's neighbors quickly

本文关键字:邻居 顶点 存储      更新时间:2023-10-16

假设我有从0N-1标记的N顶点。每个顶点具有最多3个邻居和至少1个邻居。假设邻居信息最初存储在一个称为pairs的向量上,其中pairs[2*i]pairs[2*i+1]是一对相邻顶点。

现在我需要快速查找vertex[i]的邻居是什么,存储这些信息的最佳方式是什么?


我想出的方法是:

  • 声明一个称为neighbors[3*N]的向量,以便neighbors[3*i+0]neighbors[3*i+1]neighbors[3*i+2]存储三个可能的邻居。

  • 为什么说可能,因为每个顶点最多有三个邻居。

  • 所以我将向量neighbors的所有元素初始化为N,这意味着这不是有效的邻居,因为顶点被标记为从0N-1

代码实现如下:

void get_neighbors(const std::vector<int>& pairs, 
        std::vector<int>& neighbors)   {
    int N = neighbors.size()/3;
    int M = pairs.size()/2;
    //init all the vertexes' neighbours to nothing
    for (int i=0; i<3*N; ++i)   {
        neighbors[i] = N;
    }
    //loop through all the vertexes, and store their neighbors
    for(int i=0; i<N; ++i)  {
        int j = 0;
        //loop through pairs, and find out what neighbors vertex[i] has
        for (int k=0; k < M; ++k) {
            if(pairs[2*k]==i)    {
                neighbors[3*i+j]=pairs[2*k+1];
                ++j;
            }
            else if(pairs[2*k+1]==i)    {
                neighbors[3*i+j]=pairs[2*k];
                ++j;
            }
        }
    }
}

我对我的方法感到不舒服的事情:

  1. 声明向量neighbors(3*N)太多了,因为它的许多元素将是无用的N

  2. 如果我想查找vertex[i]的邻居,每次我都需要测试neighbors[3*i]==Nneighbors[3*i+1]==Nneighbors[3*i+2]==N

您正在使用的数据结构是Graph。但图是一个更通用的概念,其中一个节点可以有任何数量的邻居。

因为你的问题更严格,每个节点最多有3个邻居我在评论中建议的解决方案是一个通用的解决方案,可以用于所有的图,也可以很好地用于您的案例

声明图形:

vector<vector<int> > graph (N);

如果从ab存在边缘,请执行以下操作:

graph[a].push_back (b);

检索节点a的邻居:

for (int i=0;i<graph[a].size();i++)
    // Do whatever you want with the neighbour. The variable graph[a][i] holds the neighbour number

现在来谈谈你最多3个邻居的问题根据我的说法,最好的解决方案是:

struct node
{
    int data; // Some data related to the node. This is optional.
    int neigh_1;
    int neigh_2;
    int neigh_3;
}

并将图制作为节点阵列:

node[N] graph;

这也将占用3*N内存。但这是一个比你所做的更好的方法。让我解释一下:

当你说给我节点x:的邻居时

您的方法将访问内存位置x,3*x,3*x+4,3*x+8

我的方法将访问内存位置x,x+4,x+8,x+16

等等!为什么我们只想访问x的邻居?这是一个有效的问题,但如果您需要在节点上处理一些值或存储一些节点本地的信息,那么我们必须访问x

注意:我建议的方法不会比你的方法表现得更差。但在一般情况下,它肯定会表现得更好

为什么我的方法更好

我的方法将导致较少的缓存未命中,因为我具有更好的引用空间局部性。

如果你不关心缓存的工作,简单地说,我的方法将是访问彼此更近的位置,因此很有可能已经在更快的内存缓存中,这将导致更快的访问。

还有人可能会争辩说,只有在需要的时候,我们才应该为邻居声明内存。本质上,vector解决方案就是这样做的。但在这种情况下,它不会很好地执行,因为即使声明一个指针也需要8个字节。但有了这个空间,你可以存储2个整数的信息,即关于2个邻居的信息。

因此,动态分配内存不会节省任何空间,因为最大邻居为3。

您可以简单地定义一个结构,该结构包含节点值和邻居的索引,如

#include <vector>
#include <iostream>
struct vertex{
    int data;
    std::vector<int> neighbours;
};

下面给出了一个向每个节点添加2个邻居的示例程序。每个节点都有一个其索引平方的值。

int main(){
    vertex v[5];
    for (int i=0;i<5;i++){
        v[i].data = i*i;
    }
    for (int i=0;i<5;i++){
        v[i].neighbours.push_back((i+1) % 5);
        v[i].neighbours.push_back((i+2) % 5);
    }
    for (int i=0;i<5;i++){
         std::cout << "vector " << i << " has value " << v[i].data << std::endl;
        for (int j=0;j<v[i].neighbours.size();j++){
            int nodeNum = v[i].neighbours[j];
            std::cout << "vector " << i << " has neighbours " << v[i].neighbours[j] << " with data " << v[nodeNum].data << std::endl;
        }
        std::cout << std::endl ;
    }
    return 0;
}

这将在之后输出

vector 0 has value 0
vector 0 has neighbours 1 with data 1
vector 0 has neighbours 2 with data 4
vector 1 has value 1
vector 1 has neighbours 2 with data 4
vector 1 has neighbours 3 with data 9
vector 2 has value 4
vector 2 has neighbours 3 with data 9
vector 2 has neighbours 4 with data 16
vector 3 has value 9
vector 3 has neighbours 4 with data 16
vector 3 has neighbours 0 with data 0
vector 4 has value 16
vector 4 has neighbours 0 with data 0
vector 4 has neighbours 1 with data 1

因为我们用矢量表示邻居。我们可以为我们的节点分配任意数量的邻居。这是一般情况。也适用于您的要求。希望这能帮助