1

我正在尝试实现一个图形来存储来自文本文件的数据列表,例如:

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

4 回答 4

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字段将使用字段的链接graph列表来启动图形节点的链接列表。and可以是使用 and 分配给图中的后继和前驱边的数组(指向边和终止的指针数组)。尽管同时保留后继和前任似乎是多余的,但您会发现大多数图算法都喜欢能够双向行走。边缘点的and字段返回到节点。如果您不希望将数据存储在边缘中,那么您可以让和数组直接指向节点并忘记类型。nextnodepredsuccmallocreallocNULLsourcetargetpredsuccedge

不要尝试在中使用reallocon Ngraph因为节点的所有地址都可能发生变化,并且这些地址在图表的其余部分中被大量使用。

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

于 2013-03-30T23:48:31.943 回答
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 */
}
于 2013-03-30T23:35:51.640 回答
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.
于 2013-03-31T02:31:45.460 回答
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 -> node0 -> node1 -> node2

root 的内容可能是:id = -1; 以下=空;下一个节点=节点0

node0 的内容可能是:id = 0; 下一个节点=节点1;following 指向 node_following 的列表:following -> {1, address of next node} -> {2, NULL}

node1 的内容可能是:id = 1; 下一个节点=节点2;following 指向 node_following 的列表为:following -> {2, NULL}

node2 的内容可能是:id = 2; 下一个节点=空;以下指向 node_following 的列表为:following -> {1, NULL}

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

于 2013-03-30T23:37:54.770 回答