免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4093 | 回复: 9
打印 上一主题 下一主题

[C] 请教各位大神一个关于C语言中有向图的问题~~~ [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-01-25 21:03 |只看该作者 |倒序浏览
最近碰到一个比较复杂的数据结构问题,感觉和C里面的有向图的结构差不多的,自己写了一下,感觉很多地方都写得不是很合理,请教高人写一个创建此图的方法。

结构图大致如下:


// 节点struct定义如下:
struct Spoint
{
    int id;
    char name[10];
    Spoint *next;// 指向下一个节点
   Spoint *father;// 指向父节点
   Special *special;// 特殊节点,当一个节点有两个以上的父节点的时候则使用此链描述
};

结合上图以及上面的struct说明如下:
黑色的线代表的是next指针指向;蓝色的线代表的是father指针指向;红色的线是当一个节点有两个以上的前驱节点时使用的特殊节点special指针;

//特殊节点struct
struct Special
{
    Spoint *point;// point指针指向的是某个Spoint节点,如上图,类似g节点才会有Special的结构,而point指向e

    Spoint *next;// next指针指向的是下一个特殊节点,如上图,next指向的下一个特殊Special结构的point指针指向的是f的地址
};

数据源如下:
struct SourceData
{
    int id;
    char name[10];
    long pointId[100];// 这里记录的是每个节点的前驱节点的id号,最少有一个,最多100个,当只有一个的时候那么就表示该节点存在一个father节点,如果大于1,则表示该节点不存在 father节点了,而是特殊节点;
};

int main()
{
    vector<SourceData> vecData;// 假设数据源已经都放入到此vector中了

    for(int i = 0; i < vecData.size(); i++)
     {
          Create(vecData);
     }
}

// 那么我这里需要根据数据源生成一个类似上图中那样的结构,然后将链表头指针返回
Spoint * Create(vecData &data)
{
   
}

请大牛帮忙实现以下create函数,谢谢了~

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
2 [报告]
发表于 2013-01-26 10:38 |只看该作者
本帖最后由 yulihua49 于 2013-01-26 10:46 编辑
七夜未央 发表于 2013-01-25 21:03
最近碰到一个比较复杂的数据结构问题,感觉和C里面的有向图的结构差不多的,自己写了一下,感觉很多地方都写 ...

father可以不要,问题更简单一些。
以vecData.size();尺寸分配一个spoint数组。
对于spoint数组的每个成员,用strtok分解priontId,填入special链表。
最后遍历数组,填写next。如果多重后继,next也应是链表。

论坛徽章:
59
2015年亚洲杯之约旦
日期:2015-01-27 21:27:392015年亚洲杯之日本
日期:2015-02-06 22:09:41拜羊年徽章
日期:2015-03-03 16:15:432015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:50:282015元宵节徽章
日期:2015-03-06 15:50:392015年亚洲杯之阿联酋
日期:2015-03-19 17:39:302015年亚洲杯之中国
日期:2015-03-23 18:52:23巳蛇
日期:2014-12-14 22:44:03双子座
日期:2014-12-10 21:39:16处女座
日期:2014-12-02 08:03:17天蝎座
日期:2014-07-21 19:08:47
3 [报告]
发表于 2013-01-26 10:45 |只看该作者
  1. struct gnote{
  2. int id;
  3. // void *pdata;
  4. struct forwardlist{struct gnote *pgnext;struct forwardlist *next;} *next;
  5. };
复制代码

论坛徽章:
0
4 [报告]
发表于 2013-01-26 11:16 |只看该作者
回复 2# yulihua49
感谢回复,但是father链必须得要呢,如果没这条链的话那么就无法得知前驱节点的状态,需求就这么恶心{:3_188:}

   

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
5 [报告]
发表于 2013-01-27 10:08 |只看该作者
七夜未央 发表于 2013-01-26 11:16
回复 2# yulihua49
感谢回复,但是father链必须得要呢,如果没这条链的话那么就无法得知前驱节点的状态, ...

father放在special里即可。
也可以用map,以id为key。
这样不必预先分配数组,随时加入新节点。
最后遍历next时,检索id也很方便。

论坛徽章:
3
15-16赛季CBA联赛之山东
日期:2016-10-30 08:47:3015-16赛季CBA联赛之佛山
日期:2016-12-17 00:06:31CU十四周年纪念徽章
日期:2017-12-03 01:04:02
6 [报告]
发表于 2013-01-27 13:02 |只看该作者
本帖最后由 captivated 于 2013-01-27 15:50 编辑

要表示图,需要表示顶点和边。要么用矩阵,要么用邻接表,要么直接用内核链表那种节点做十字交叉链。
LZ用表示树的想法来表示图,感觉怪怪。。。

两个表示图的数据结构示例,首先是矩阵表示法。。。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>

  4. #define VERTEX_NUM        8

  5. // 矩阵记录每个顶点的连通状况.
  6. // edge_cnt记录有多少条边.
  7. typedef struct _graph graph_t;

  8. struct _graph {
  9.         int edge[VERTEX_NUM][VERTEX_NUM];  // 边
  10.         int edge_cnt;                      // 边计数
  11.         int visited[VERTEX_NUM];           // 顶点访问信息
  12. };

  13. // 创建图.
  14. graph_t *graph_create(void)
  15. {
  16.         graph_t *thiz = malloc(sizeof(graph_t));
  17.         if (NULL == thiz)
  18.                 return NULL;

  19.         int i, j;
  20.         for (i = 0; i < VERTEX_NUM; i++)
  21.                 for (j = 0; j < VERTEX_NUM; j++)
  22.                 {
  23.                         thiz->edge[i][j] = 0;       
  24.                 }
  25.         thiz->edge_cnt = 0;

  26.         return thiz;
  27. }

  28. // 增加一条边, 也即将矩阵中沿反对角线对称的两个点位置1.
  29. // 这个图是无向的.
  30. void graph_add_edge(graph_t *thiz, int from, int to)
  31. {
  32.         thiz->edge[from][to] = 1;
  33.         thiz->edge[to][from] = 1;
  34.         thiz->edge_cnt++;
  35. }

  36. // 深度优先(depth first search/traverse)搜索/遍历
  37. void __graph_dfs(graph_t *thiz, int start)
  38. {
  39.         // 访问的动作就是将该顶点值(其索引)打印.
  40.         printf("%d ", start);

  41.         // 已访问顶点要置位标记
  42.         thiz->visited[start] = 1;

  43.         // edge[start][i]为1则表示边存在(可访问)
  44.         int i;
  45.         for (i = 0; i < VERTEX_NUM; i++)
  46.         {
  47.                 if (thiz->edge[start][i] == 1 && thiz->visited[i] == 0)       
  48.                         __graph_dfs(thiz, i);
  49.         }
  50. }

  51. // dfs的外覆器
  52. void graph_dfs(graph_t *thiz, int start)
  53. {
  54.         memset(thiz->visited, 0, sizeof(int) * VERTEX_NUM);
  55.         printf("start => ");
  56.         __graph_dfs(thiz, start);
  57.         printf("end.\n");
  58. }
复制代码
用邻接表表示的:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>

  4. #define MAX 0xffffff

  5. // edge infomation, 比如, 用一条边表示两顶点之间的交通状况,
  6. // 则速度, 交通运输类型, 两顶点之间的距离, 票价如何,etc...
  7. typedef struct _einfo einfo_t;
  8. struct _einfo {
  9.         int speed;
  10.         char traffic_style[32];
  11.         int distance;
  12.         int price_per_km;
  13. };

  14. // edge,表示一条边
  15. // w: weight, 权值, 该权值如何可以根据einfo来计算
  16. // adj: adjacent / adjacency 邻接信息, 与什么顶点相连 -- 对应于vertex中的顶点标记.
  17. // next: 链表不用说了
  18. typedef struct _edge edge_t;
  19. struct _edge {
  20.         einfo_t einfo;
  21.         int w;
  22.         int adj;
  23.         edge_t *next;
  24. };

  25. // vertex infomation
  26. // data: 顶点的标记,标示该顶点,也就是顶点的id, 就像矩阵中用数组索引下标标示顶点一样,
  27. // 只不过这种结构更灵活一些, id是可选的一个int数值.
  28. typedef struct _vinfo vinfo_t;
  29. struct _vinfo {
  30.         char name[32];
  31.         int data;
  32. };

  33. // vertex.
  34. // nei_cnt: how many neighours this vertex hold.
  35. // visited: has it been visited?
  36. // head: head pointer of edge list.
  37. // next: next vertex
  38. typedef struct _vertex vertex_t;
  39. struct _vertex {
  40.         vinfo_t vinfo;
  41.         int nei_cnt;
  42.         int visited;
  43.         edge_t *head;
  44.         vertex_t *next;
  45. };

  46. // 图结构
  47. // 总体结构是这样的, 就是链表链链表:
  48. // holder  -> vertex1 -> vertex2 -> vertex3 -> vertex4 -> vertex5...
  49. //                |
  50. //             edge1[edge结构体对象中的adj表示该vertex1和哪一个顶点相连, ex, vertex3, 那么adj就是vertex3的information的data]
  51. //                |
  52. //             edge2[ex, 假如这个边节点表示vertex1和vertex5相连, 那么adj就是vertex5的data]
  53. //                |
  54. //            edge3...

  55. typedef struct _graph graph_t;
  56. struct _graph {
  57.         vertex_t *holder;
  58.         int v_cnt;
  59.         int e_cnt;
  60. };

  61. // 初始化图, 只是创建一个容器的手柄,
  62. // 让用户自行添加顶点和边, 图的最终形状通过添加确定
  63. graph_t *graph_create(void)
  64. {
  65.         graph_t *thiz = NULL;
  66.         thiz = malloc(sizeof(graph_t));
  67.         if (!thiz)
  68.                 return NULL;

  69.         thiz->holder = NULL;
  70.         thiz->v_cnt = 0;
  71.         thiz->e_cnt = 0;

  72.         return thiz;
  73. }

  74. void __graph_init_vertex(vertex_t *v, vinfo_t *vinfo)
  75. {
  76.         memcpy(&v->vinfo, vinfo, sizeof(vinfo_t));
  77.         v->nei_cnt = 0;
  78.         v->head = NULL;
  79.         v->next = NULL;
  80.         v->visited = 0;
  81. }

  82. void graph_add_vertex(graph_t *thiz, vinfo_t *vinfo)
  83. {
  84.         vertex_t **cur = &thiz->holder;

  85.         vertex_t *v = malloc(sizeof(vertex_t));
  86.         if (!v)
  87.                 return;
  88.        
  89.         __graph_init_vertex(v, vinfo);

  90.         while (*cur)
  91.                 cur = &(*cur)->next;
  92.        
  93.         *cur = v;

  94.         thiz->v_cnt++;
  95. }

  96. // key是顶点标记,根据顶点标记从图中[就是顶点链表]搜索到该顶点
  97. vertex_t *graph_search_vertex(graph_t *thiz, int key)
  98. {
  99.         vertex_t *t = thiz->holder;

  100.         while (t) {
  101.                 if (t->vinfo.data == key)       
  102.                         return t;
  103.                 t = t->next;
  104.         }

  105.         return NULL;
  106. }

  107. // 无向图, 添加边, from, to 是顶点标记
  108. // 如果是有向图就更简单, 只要给from顶点的邻接链表中增加一个边节点即可
  109. void graph_add_edge(graph_t *thiz, int from, int to, einfo_t *einfo)
  110. {
  111.         // 首先得到需要增加边的两个顶点
  112.         vertex_t *from_v = graph_search_vertex(thiz, from);
  113.         vertex_t *to_v = graph_search_vertex(thiz, to);

  114.         // 构建edge, 加到from_v的邻接链表中
  115.         edge_t *ef = malloc(sizeof(edge_t));
  116.         if (!ef)
  117.                 return;
  118.         ef->einfo = *einfo;
  119.         ef->adj = to;
  120.         ef->next = NULL;

  121.         edge_t **cur = &from_v->head;
  122.         while (*cur)
  123.                 cur = &(*cur)->next;
  124.         *cur = ef;

  125.         // 加到to_v的邻接链表中
  126.         edge_t *et = malloc(sizeof(edge_t));
  127.         if (!et)
  128.         {
  129.                 free(ef);       
  130.                 return;
  131.         }
  132.         et->einfo = *einfo;
  133.         et->adj = from;
  134.         et->next = NULL;

  135.         cur = &to_v->head;
  136.         while (*cur)
  137.                 cur = &(*cur)->next;
  138.         *cur = et;

  139.         from_v->nei_cnt++;
  140.         to_v->nei_cnt++;
  141.         thiz->e_cnt++;
  142. }

  143. void __graph_dfs(graph_t *thiz, int start)
  144. {
  145.         vertex_t *v = graph_search_vertex(thiz, start);

  146.         if (!v)
  147.                 return;
  148.        
  149.         printf("%s ", v->vinfo.name);
  150.         v->visited = 1;

  151.         edge_t *e = v->head;
  152.         while (e)
  153.         {
  154.                 v = graph_search_vertex(thiz, e->adj);
  155.                 while (v->visited == 0)
  156.                         __graph_dfs(thiz, v->vinfo.data);
  157.                 e = e->next;
  158.         }
  159. }

  160. void graph_dfs(graph_t *thiz, int start)
  161. {
  162.         vertex_t *v = thiz->holder;

  163.         while (v)
  164.         {
  165.                 v->visited = 0;       
  166.                 v = v->next;
  167.         }

  168.         __graph_dfs(thiz, start);
  169.         printf("\n");
  170. }
复制代码
PS:代码看上去有点长。耐心点读罢。。。

论坛徽章:
3
15-16赛季CBA联赛之山东
日期:2016-10-30 08:47:3015-16赛季CBA联赛之佛山
日期:2016-12-17 00:06:31CU十四周年纪念徽章
日期:2017-12-03 01:04:02
7 [报告]
发表于 2013-01-27 13:16 |只看该作者
本帖最后由 captivated 于 2013-01-27 13:20 编辑

。。。见楼下。

论坛徽章:
3
15-16赛季CBA联赛之山东
日期:2016-10-30 08:47:3015-16赛季CBA联赛之佛山
日期:2016-12-17 00:06:31CU十四周年纪念徽章
日期:2017-12-03 01:04:02
8 [报告]
发表于 2013-01-27 13:19 |只看该作者
本帖最后由 captivated 于 2013-01-27 15:37 编辑

另外:

图创建并不是生成图,因为每个图都不一样。
创建图只是创建一个容器的手柄。
然后给出添加/删除顶点和边的接口。

用户拿到这个容器的手柄,然后自行添加顶点和边,他想要什么形状,就是什么形状。

如果用户想要随机生成图,那么如何随机生成是用户自己的事情,他自己得想算法。当然也可以给一个随机生成图的接口。。。

论坛徽章:
3
15-16赛季CBA联赛之山东
日期:2016-10-30 08:47:3015-16赛季CBA联赛之佛山
日期:2016-12-17 00:06:31CU十四周年纪念徽章
日期:2017-12-03 01:04:02
9 [报告]
发表于 2013-01-27 15:33 |只看该作者
本帖最后由 captivated 于 2013-01-27 15:33 编辑
  1. // 例如:
  2. int main(void)
  3. {
  4.         graph_t *g = NULL;

  5.         g = graph_create();

  6.         int i,j;

  7.         vinfo_t vinfo_ary[] = {
  8.                 { "深圳", 1 },
  9.                 { "重庆", 2 },
  10.                 { "成都", 3 },
  11.                 { "西安", 4 },
  12.                 { "上海", 5 },
  13.                  // ...
  14.         };

  15.         // 用户加入顶点...
  16.         for(i = 0; i < sizeof(vinfo_ary) / sizeof(vinfo_t); i++)
  17.         {
  18.                 graph_add_vertex(g, vinfo_ary + i);
  19.         }

  20.         einfo_t einfo_ary[] = {
  21.                 { 120, "train", 2000, 2 },
  22.                 { 150, "bus", 600, 5 },
  23.                 { 130, "train", 1200, 3 },
  24.                 // ...
  25.         };

  26.         // 用户自行增加边, 构建图的形状
  27.         graph_add_edge(g, 1, 2, einfo_ary);
  28.         graph_add_edge(g, 1, 3, einfo_ary + 1);
  29.         graph_add_edge(g, 1, 4, einfo_ary + 2);
  30.         // ...

  31.         printf("\n从各个顶点开始的深度优先遍历:\n");
  32.         int data[] = { 1, 2, 3, 4, 5 };
  33.         for(i = 0; i < sizeof(data) / sizeof(int); i++)
  34.                 graph_dfs(g, data[i]);

  35.         graph_destroy(g);

  36.         return 0;
  37. }
复制代码

论坛徽章:
0
10 [报告]
发表于 2013-01-28 10:17 |只看该作者
回复 9# captivated

多谢高人指教,可能是我之前对图的理解不正确,以至于和需求有点偏差,不过我的问题已经解决了,谢谢各位了,呵呵~


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP