你可以在Node.js中使用vector作为邻接表。
class node {
int value;
vector<node*> neighbors;
};
如果在编译时已知图表,可以使用array,但这会更加困难一些。如果您只知道图表的大小(在编译时),则可以采取类似以下的操作。
template<unsigned int N>
class graph {
array<node, N> nodes;
}
要添加一个邻居,你需要像这样做(不要忘记从零开始编号):
nodes[i].neighbors.push_back(nodes+j)
当然,你可以使用无指针邻接表并在表的上层工作。那么,你将在节点中使用 vector<int>
,并且推入相邻节点的数量。通过这两种图的表示方式,你可以实现所有使用邻接表的算法。
最后,我想补充一点。有些人使用list而不是vector,因为删除节点的时间复杂度为O(1)。但这是错误的。对于大多数算法,邻接表中的顺序并不重要。所以你可以在O(1)时间内从向量中删除任何元素。只需将其与最后一个元素交换,pop_back的时间复杂度为O(1)。就像这样:
if(i != number_of_last_element_in_list) //neighbors.size() - 1
swap(neighbors[i], neighbor.back());
neighbors.pop_back();
具体示例(节点中有向量,使用C++11):
//creation of nodes, as previously
constexpr unsigned int N = 3;
array<node,N> nodes; //or array<node, 3> nodes;
//creating edge (adding neighbors), in the constructor, or somewhere
nodes[0].neighbors = {&nodes[1]};
nodes[1].neighbors = {&nodes[0], &nodes[1]};
//adding runtime, i,j from user, eg. i = 2, j = 0
nodes[i].neighbors.push_back(&nodes[j]); //nodes[2].neighbors = {&nodes[0]};
我认为很清楚。从0
可以到达1
,从1
可以到达0
和自身,例如从2
可以到达0
。这是有向图。如果想得到无向图,你需要将每个节点的地址加入到其相邻节点中。你可以使用数字代替指针,在class node
中使用vector<unsigned int>
,并推入数字,而不是地址。
正如我们所知,您不需要使用指针。这里也提供了一个示例。
当顶点数量可能会改变时,您可以使用节点向量 (vector<node>
) 代替数组,并进行 调整大小。其他部分保持不变。例如:
vector<node> nodes(n); //you have n nodes
nodes.emplace_back(); //you added new node, or .resize(n+1)
//here is place to runtime graph generate
//as previously, i,j from user, but now you have 'vector<unsigned int>' in node
nodes[i].neighbors.push_back(j);
但是你不能删除一个节点,这会违反编号!如果想要删除某个节点,应该使用指针的列表(list<node*>
)。否则,你必须保留不存在的顶点。在这里,顺序很重要!
关于代码行nodes.emplace_back(); //adding node
,对于没有指针的图形结构来说是安全的。如果您想使用指针,请不要改变图的大小。当添加顶点时,您可能会意外地更改某些节点的地址,因为vector
将被复制到新位置(超出空间)。
解决这个问题的一种方法是使用reserve,不过您必须知道图的最大大小!但实际上,我鼓励您在使用指针时不要使用vector
来保存顶点。如果您不知道具体实现,更安全的做法可能是自我内存管理(例如,智能指针如shared_ptr或只是new)。
node* const graph = new node[size]; //<
//Here no address change accidentally.
使用vector
作为邻接表总是可以的!没有改变节点地址的机会。