图的初始化和使用

时间:2013-03-30 23:23:27

标签: c data-structures graph

我正在尝试实现图表来存储文本文件中的数据列表,如下所示:

0,1 (node 0 links to 1)
0,2 (node 0 links to 2)
1,2 (node 1 links to 2)
2,1 (node 2 links to 1)

无论如何,在定义结构时我遇到了麻烦。我在使用矩阵或相邻列表之间徘徊,但我想我会选择列表,我只是不确定如何定义结构。我应该使用可变大小的数组,链接列表还是其他什么?哪种方式最简单?

struct grph{

};

struct node{

    //ID of the node
    int id;

};

其次,如何将数据存储到此图表中,这是我遇到的最麻烦的地方。从本质上讲,我认为像链接列表一样简单,你只需要在最后添加一个节点。这里的不同之处在于每个节点可以指向许多不同的节点或根本不指向任何节点。如何将图结构与所有链接的节点结构相链接?

例如,在使用链接列表时,如何在上面的示例中存储0连接的节点?我理解你使用矩阵或列表/数组,但由于C中没有这样的实现的例子,我真的很困惑。我发现的任何例子都比以前更糟糕。

4 个答案:

答案 0 :(得分:1)

这只是一个例子:

struct node{
        int id; 
        struct node **out;
        int num_out;
        /* optional: if you want doubly links */
        struct node **in;
        int num_in;
};

/* quick access to a node given an id */
struct node *node_list;

/* connect 'from' to 'to' */
void link(struct node *graph, int from, int to) {
        struct node *nfrom = &node_list[from], 
                    *nto   = &node_list[to];
        nfrom->num_out++;
        nfrom->out = realloc(nfrom->out, 
             sizeof(struct node*) * nfrom->num_out);
        nfrom->out[num_out-1] = nto;
        /* also do similar to nto->in if you want doubly links */
}

答案 1 :(得分:1)

回答你的第一个问题:邻接矩阵与邻接列表?如果您希望图形密集,即大多数节点与大多数其他节点相邻,那么请选择矩阵,因为大多数操作在矩阵上更容易。如果你真的需要传递闭包,那么矩阵也可能更好,因为它们往往是密集的。否则邻接列表会越来越快。

图表如下:

typedef struct node * node_p;
typedef struct edge * edge_p;

typedef struct edge
{       node_p  source, target;
        /* Add any data in the edges */
} edge;

typedef struct node
{       edge_p  * pred, * succ;
        node_p  next;
        /* Add any data in the nodes */
} node;

typedef struct graph
{       node_p  N;
} graph;

N的{​​{1}}字段会使用graph的{​​{1}}字段来启动图表节点的链接列表,以链接列表。 nextnode可以是使用predsucc分配的数组,用于图中的后继边和前导边(指针数组到边缘并malloc终止)。尽管保留后继者和前任者似乎都是多余的,但你会发现大多数图形算法都喜欢能够双向行走。边缘的reallocNULL字段指向节点。如果您不希望在边缘存储数据,那么您可以让sourcetarget数组直接指向节点并忘记pred类型。

请勿尝试在succ edge realloc上使用N,因为节点的所有地址都可能会发生变化,并且会在图表的其余部分大量使用。

P.S:就个人而言,我更喜欢graph个结束链表的循环链表,因为大多数(如果不是全部)操作的代码都要简单得多。在这种情况下,NULL将包含(虚拟)graph而不是指针。

答案 2 :(得分:1)

你可以这样做:

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

typedef struct
{
  void* pElements;
  size_t ElementSize;
  size_t Count; // how many elements exist
  size_t TotalCount; // for how many elements space allocated
} tArray;

void ArrayInit(tArray* pArray, size_t ElementSize)
{
  pArray->pElements = NULL;
  pArray->ElementSize = ElementSize;
  pArray->TotalCount = pArray->Count = 0;
}

void ArrayDestroy(tArray* pArray)
{
  free(pArray->pElements);
  ArrayInit(pArray, 0);
}

int ArrayGrowByOne(tArray* pArray)
{
  if (pArray->Count == pArray->TotalCount) // used up all allocated space
  {
    size_t newTotalCount, newTotalSize;
    void* p;

    if (pArray->TotalCount == 0)
    {
      newTotalCount = 1;
    }
    else
    {
      newTotalCount = 2 * pArray->TotalCount; // double the allocated count
      if (newTotalCount / 2 != pArray->TotalCount) // count overflow
        return 0;
    }

    newTotalSize = newTotalCount * pArray->ElementSize;
    if (newTotalSize / pArray->ElementSize != newTotalCount) // size overflow
      return 0;

    p = realloc(pArray->pElements, newTotalSize);
    if (p == NULL) // out of memory
      return 0;

    pArray->pElements = p;
    pArray->TotalCount = newTotalCount;
  }

  pArray->Count++;
  return 1;
}

int ArrayInsertElement(tArray* pArray, size_t pos, void* pElement)
{
  if (pos > pArray->Count) // bad position
    return 0;

  if (!ArrayGrowByOne(pArray)) // couldn't grow
    return 0;

  if (pos < pArray->Count - 1)
    memmove((char*)pArray->pElements + (pos + 1) * pArray->ElementSize,
           (char*)pArray->pElements + pos * pArray->ElementSize,
           (pArray->Count - 1 - pos) * pArray->ElementSize);

  memcpy((char*)pArray->pElements + pos * pArray->ElementSize,
         pElement,
         pArray->ElementSize);

  return 1;
}

typedef struct
{
  int Id;

  int Data;

  tArray LinksTo; // links from this node to other nodes (array of Id's)
  tArray LinksFrom; // back links from other nodes to this node (array of Id's)
} tNode;

typedef struct
{
  tArray Nodes;
} tGraph;

void GraphInit(tGraph* pGraph)
{
  ArrayInit(&pGraph->Nodes, sizeof(tNode));
}

void GraphPrintNodes(tGraph* pGraph)
{
  size_t i, j;

  if (pGraph->Nodes.Count == 0)
  {
    printf("Empty graph.\n");
  }

  for (i = 0; i < pGraph->Nodes.Count; i++)
  {
    tNode* pNode = (tNode*)pGraph->Nodes.pElements + i;

    printf("Node %d:\n  Data: %d\n", pNode->Id, pNode->Data);

    if (pNode->LinksTo.Count)
    {
      printf("  Links to:\n");

      for (j = 0; j < pNode->LinksTo.Count; j++)
      {
        int* p = (int*)pNode->LinksTo.pElements + j;
        printf("    Node %d\n", *p);
      }
    }
  }
}

void GraphDestroy(tGraph* pGraph)
{
  size_t i;

  for (i = 0; i < pGraph->Nodes.Count; i++)
  {
    tNode* pNode = (tNode*)pGraph->Nodes.pElements + i;
    ArrayDestroy(&pNode->LinksTo);
    ArrayDestroy(&pNode->LinksFrom);
  }

  ArrayDestroy(&pGraph->Nodes);
}

int NodeIdComparator(const void* p1, const void* p2)
{
  const tNode* pa = p1;
  const tNode* pb = p2;

  if (pa->Id < pb->Id)
    return -1;
  if (pa->Id > pb->Id)
    return 1;
  return 0;
}

int IntComparator(const void* p1, const void* p2)
{
  const int* pa = p1;
  const int* pb = p2;

  if (*pa < *pb)
    return -1;
  if (*pa > *pb)
    return 1;
  return 0;
}

size_t GraphFindNodeIndexById(tGraph* pGraph, int Id)
{
  tNode* pNode = bsearch(&Id,
                         pGraph->Nodes.pElements,
                         pGraph->Nodes.Count,
                         pGraph->Nodes.ElementSize,
                         &NodeIdComparator);

  if (pNode == NULL)
    return (size_t)-1;

  return pNode - (tNode*)pGraph->Nodes.pElements;
}

int GraphInsertNode(tGraph* pGraph, int Id, int Data)
{
  size_t idx = GraphFindNodeIndexById(pGraph, Id);
  tNode node;

  if (idx != (size_t)-1) // node with this Id already exist
    return 0;

  node.Id = Id;
  node.Data = Data;
  ArrayInit(&node.LinksTo, sizeof(int));
  ArrayInit(&node.LinksFrom, sizeof(int));

  if (!ArrayInsertElement(&pGraph->Nodes, pGraph->Nodes.Count, &node))
    return 0;

  qsort(pGraph->Nodes.pElements,
        pGraph->Nodes.Count,
        pGraph->Nodes.ElementSize,
        &NodeIdComparator); // maintain order for binary search

  return 1;
}

int GraphLinkNodes(tGraph* pGraph, int IdFrom, int IdTo)
{
  size_t idxFrom = GraphFindNodeIndexById(pGraph, IdFrom);
  size_t idxTo = GraphFindNodeIndexById(pGraph, IdTo);
  tNode *pFrom, *pTo;

  if (idxFrom == (size_t)-1 || idxTo == (size_t)-1) // one or both nodes don't exist
    return 0;

  pFrom = (tNode*)pGraph->Nodes.pElements + idxFrom;
  pTo = (tNode*)pGraph->Nodes.pElements + idxTo;

  // link IdFrom -> IdTo
  if (bsearch(&IdTo,
              pFrom->LinksTo.pElements,
              pFrom->LinksTo.Count,
              pFrom->LinksTo.ElementSize,
              &IntComparator) == NULL) // IdFrom doesn't link to IdTo yet
  {
    if (!ArrayInsertElement(&pFrom->LinksTo, pFrom->LinksTo.Count, &IdTo))
      return 0;

    qsort(pFrom->LinksTo.pElements,
          pFrom->LinksTo.Count,
          pFrom->LinksTo.ElementSize,
          &IntComparator); // maintain order for binary search
  }

  // back link IdFrom <- IdTo
  if (bsearch(&IdFrom,
              pTo->LinksFrom.pElements,
              pTo->LinksFrom.Count,
              pTo->LinksFrom.ElementSize,
              &IntComparator) == NULL) // IdFrom doesn't link to IdTo yet
  {
    if (!ArrayInsertElement(&pTo->LinksFrom, pTo->LinksFrom.Count, &IdFrom))
      return 0;

    qsort(pTo->LinksFrom.pElements,
          pTo->LinksFrom.Count,
          pTo->LinksFrom.ElementSize,
          &IntComparator); // maintain order for binary search
  }

  return 1;
}

int main(void)
{
  tGraph g;

  printf("\nCreating empty graph...\n");
  GraphInit(&g);
  GraphPrintNodes(&g);

  printf("\nInserting nodes...\n");
  GraphInsertNode(&g, 0, 0);
  GraphInsertNode(&g, 1, 101);
  GraphInsertNode(&g, 2, 202);
  GraphPrintNodes(&g);

  printf("\nLinking nodes...\n");
  GraphLinkNodes(&g, 0, 1);
  GraphLinkNodes(&g, 0, 2);
  GraphLinkNodes(&g, 1, 2);
  GraphLinkNodes(&g, 2, 1);
  GraphPrintNodes(&g);

  printf("\nDestroying graph...\n");
  GraphDestroy(&g);
  GraphPrintNodes(&g);

  // repeat
  printf("\nLet's repeat...\n");

  printf("\nCreating empty graph...\n");
  GraphInit(&g);
  GraphPrintNodes(&g);

  printf("\nInserting nodes...\n");
  GraphInsertNode(&g, 1, 111);
  GraphInsertNode(&g, 2, 222);
  GraphInsertNode(&g, 3, 333);
  GraphPrintNodes(&g);

  printf("\nLinking nodes...\n");
  GraphLinkNodes(&g, 1, 2);
  GraphLinkNodes(&g, 2, 3);
  GraphLinkNodes(&g, 3, 1);
  GraphPrintNodes(&g);

  printf("\nDestroying graph...\n");
  GraphDestroy(&g);
  GraphPrintNodes(&g);

  return 0;
}

输出(ideone):

Creating empty graph...
Empty graph.

Inserting nodes...
Node 0:
  Data: 0
Node 1:
  Data: 101
Node 2:
  Data: 202

Linking nodes...
Node 0:
  Data: 0
  Links to:
    Node 1
    Node 2
Node 1:
  Data: 101
  Links to:
    Node 2
Node 2:
  Data: 202
  Links to:
    Node 1

Destroying graph...
Empty graph.

Let's repeat...

Creating empty graph...
Empty graph.

Inserting nodes...
Node 1:
  Data: 111
Node 2:
  Data: 222
Node 3:
  Data: 333

Linking nodes...
Node 1:
  Data: 111
  Links to:
    Node 2
Node 2:
  Data: 222
  Links to:
    Node 3
Node 3:
  Data: 333
  Links to:
    Node 1

Destroying graph...
Empty graph.

答案 3 :(得分:0)

看起来很像我的工作,社交网络...... 您可以单独定义节点和链接。在c语言中,您可以定义为:

struct graph_node{
    int id;
    struct node_following *following;
    struct graph_node *next_node;
}

struct node_following{
    int id;
    struct node_following *next_node;
}

对于您的示例,结果是: root - &gt; node0 - &gt; node1 - &gt;节点2

root的内容可能是:id = -1;下列= NULL; next_node = node0

node0的内容可能是:id = 0; next_node = node1;以下指向node_following的列表: 以下 - &gt; {1,下一个节点的地址} - &gt; {2,NULL}

node1的内容可能是:id = 1; next_node = node2;以下指向node_following的列表: 以下 - &gt; {2,NULL}

node2的内容可能是:id = 2; next_node = NULL;以下指向node_following的列表: 以下 - &gt; {1,NULL}

基本上,这是一个关于如何存储二维矩阵的问题。如果矩阵稀疏,请使用链接列表。否则,位图是更好的解决方案。