其他分享
首页 > 其他分享> > 在C中为有向图制作邻接列表

在C中为有向图制作邻接列表

作者:互联网

各位大家好:)今天我在图论和数据结构方面提炼技巧.我决定在C做一个小项目因为我在C工作已经有一段时间了.

我想为有向图制作一个邻接列表.换句话说,看起来像:

0-->1-->3
1-->2
2-->4
3-->
4-->

这将是有向图,其中V0(顶点0)具有到V1和V3的边缘,V1具有到V2的边缘,V2具有到V4的边缘,如下所示:

V0----->V1---->V2---->V4
 |
 |
 v
 V3 

我知道为了做到这一点,我需要在C中创建一个邻接列表.邻接列表基本上是链表的数组.好的,让我们看一些伪C代码:

#include <stdio>
#include <iostream>
using namespace std;

struct graph{
//The graph is essentially an array of the adjList struct.  
node* List[];

};

struct adjList{
//A simple linked list which can contain an int at each node in the list.

};

struct node {
int vertex;
node* next;
};

int main() {
//insert cool graph theory sorting algorithm here
}

如你所知,这个伪代码目前还远远不够.这就是我想要的一些帮助 – C中的指针和结构从来都不是我的强项.首先,这会处理顶点指向的顶点 – 但顶点本身呢?如何跟踪该顶点?当我遍历数组时,只知道指向哪个顶点,而不是知道它们指向哪个顶点,这对我没有好处.每个列表中的第一个元素应该是该顶点,然后是之后的元素是它指向的顶点.但是,如何在主程序中访问列表的第一个元素? (对不起,如果这是令人费解或混乱,我很乐意改写).

我希望能够遍历这个邻接列表,用图表做一些很酷的事情.例如,使用邻接列表表示来实现一些图论理论算法(排序,最短路径等).

(另外,我有一个关于邻接列表的问题.与使用数组列表有什么不同?为什么我不能在列表中的每个元素上都有一个带有数组的列表?)

解决方法:

您可以在节点中使用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); //or &nodes[j]

当然,你可以做无指针邻接列表并在表格“上方”工作.比你有vector< int>在节点中,你推动邻居的数量.通过图表的两种表示,您可以实现使用邻接列表的所有算法.

最后,我可以补充一下.有些使用list而不是向量,因为删除时间为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.它是有向图.如果您想要无向,则应该向两个节点添加邻居的地址.您可以使用数字而不是指针. vector< unsigned int>在类节点和推回数字,没有地址.

我们知道,您不需要使用指针.这也是一个例子.

当顶点数量可能发生变化时,您可以使用节点向量(vector< node>)代替数组,而只使用resizing.其余的保持不变.例如:

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(list< node *>).否则,您必须保留不存在的顶点.在这里,订单很重要!

关于行nodes.emplace_back(); //添加节点,没有指针的图表是安全的.如果要使用指针,则主要不应更改图形的大小.
当向量将被复制到新位置(空间外)时,您可能会意外地更改某些节点的地址,同时添加顶点.

处理它的一种方法是使用reserve,尽管你必须知道图形的最大尺寸!但事实上,我鼓励你在使用指针时不要使用向量来保持顶点.如果您不了解实现,更安全的可能是自我内存管理(智能指针,例如shared_ptr或仅new).

node* const graph = new node[size]; //<-- It is your graph.
//Here no address change accidentally.

使用vector作为邻接列表总是很好!没有机会改变节点的地址.

标签:adjacency-list,c,graph
来源: https://codeday.me/bug/20191006/1860632.html