邻接链表构建图

图的表示:


邻接矩阵表示法:

对于上面一个有向图的一种简单的表示方法是使用二维数组,称为邻接矩阵表示法
如果是无向图,对于每条边(u, v),将二维数组元素arr[u][v]值设置为true;否则该数组元素为false;
如果是有向图,对于每条边(u, v),将二维数组元素arr[u][v]值设置为该边的权重;否则该数组元素设置为一个很大的数值或是一个很小的数组(根据具体情景设置);
虽然邻接矩阵表示法比较简单,但是需要的空间比较大,适合于稠密类型的图。



邻接表表示法:

另一种表示方法是使用邻接表表示法,该方法适合于稀疏类型的图。



其实左边的数组也可以不用,如果有需要的话可以加上用来保存各个顶点的信息。一般实现的话根据右边链表数组的顺序就可以知道每个顶点对应的链表。

图中节点的数据结构:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //图节点信息  
  2. typedef struct Node{   
  3.     int edge_num;//边号   
  4.     int src;//源点   
  5.     int vertex;//自身   
  6.     int weight;//边的权重   
  7. }Node;   

图的邻接表表示法的类接口:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /******************************************************* 
  2. *  类名称: 邻接表图 
  3. ********************************************************/   
  4. class Graph{  
  5.     private:  
  6.         int edge_num;//图边的个数  
  7.         int vertex_num;//图的顶点数目  
  8.         list<Node> * graph_list;//邻接表  
  9.     public:  
  10.         Graph(){}  
  11.         Graph(char* graph[], int edgenum);   
  12.         ~Graph();  
  13.         void print();//以邻接表的形式打印图信息  
  14.     private:  
  15.         vector<int> get_graph_value(char* graph[], int columns);//获得每条边的数据  
  16.         void addEdge(char* graph[], int columns);  
  17. };  

测试函数:

1、读取图文件中的数据,图中的数据格式为下面所示:

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 0,0,1,1  
  2. 1,0,2,2  
  3. 2,0,3,1  
第1列为边的标号,第2列为边的起点,第3列为边的终点,第4列为边的权重。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /**************************************************************** 
  2. *   函数名称:read_file 
  3. *   功能描述: 读取文件中的图的数据信息 
  4. *   参数列表: buff是将文件读取的图信息保存到buff指向的二维数组中  
  5. *             spec是文件中图最大允许的边的个数 
  6. *             filename是要打开的图文件 
  7. *   返回结果:无 
  8. *****************************************************************/  
  9. int read_file(char ** const buff, const unsigned int spec, const char * const filename)  
  10. {  
  11.     FILE *fp = fopen(filename, "r");  
  12.     if (fp == NULL)  
  13.     {  
  14.         printf("Fail to open file %s, %s.\n", filename, strerror(errno));  
  15.         return 0;  
  16.     }  
  17.     printf("Open file %s OK.\n", filename);  
  18.   
  19.     char line[MAX_LINE_LEN + 2];  
  20.     unsigned int cnt = 0;  
  21.     while ((cnt < spec) && !feof(fp))  
  22.     {  
  23.         line[0] = 0;  
  24.         fgets(line, MAX_LINE_LEN + 2, fp);  
  25.         if (line[0] == 0)   continue;  
  26.         buff[cnt] = (char *)malloc(MAX_LINE_LEN + 2);  
  27.         strncpy(buff[cnt], line, MAX_LINE_LEN + 2 - 1);  
  28.         buff[cnt][4001] = 0;  
  29.         cnt++;  
  30.     }  
  31.     fclose(fp);  
  32.     printf("There are %d lines in file %s.\n", cnt, filename);  
  33.   
  34.     return cnt;  
  35. }  

2、释放刚才读取的文件中的图的数据信息

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /**************************************************************** 
  2. *   函数名称:release_buff 
  3. *   功能描述: 释放刚才读取的文件中的图的数据信息 
  4. *   参数列表: buff是指向文件读取的图信息 
  5. *             valid_item_num是指图中边的个数 
  6. *   返回结果:void 
  7. *****************************************************************/  
  8. void release_buff(char ** const buff, const int valid_item_num)  
  9. {  
  10.     for (int i = 0; i < valid_item_num; i++)  
  11.         free(buff[i]);  
  12. }  

3、主测试函数

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int main(int argc, char *argv[])  
  2. {  
  3.     char *topo[5000];  
  4.     int edge_num;  
  5.     char *demand;  
  6.     int demand_num;  
  7.   
  8.     char *topo_file = argv[1];  
  9.     edge_num = read_file(topo, 5000, topo_file);  
  10.     if (edge_num == 0)  
  11.     {  
  12.         printf("Please input valid topo file.\n");  
  13.         return -1;  
  14.     }  
  15.   
  16.     Graph G(topo, edge_num);//创建一个图对象G  
  17.     G.print();//以邻接表的形式打印图信息  
  18.   
  19.     release_buff(topo, edge_num);  
  20.   
  21.     return 0;  
  22. }  

图的邻接表表示法类的源代码:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #ifndef GRAPH_H  
  2. #define GRAPH_H  
  3.   
  4. #include <list>  
  5. #include <iostream>  
  6. #include <vector>  
  7. #include <stdlib.h>  
  8. #include <string.h>  
  9. #include <algorithm>  
  10. #include <iterator>  
  11. #include <stdio.h>  
  12. #include <errno.h>  
  13. #include <unistd.h>  
  14. #include <signal.h>  
  15.   
  16. using namespace std;  
  17.   
  18. #define MAX_VERTEX_NUM 600  
  19.   
  20. //图节点信息  
  21. typedef struct Node{   
  22.     int edge_num;//边号   
  23.     int src;//源点   
  24.     int vertex;//自身   
  25.     int weight;//边的权重   
  26. }Node;   
  27.   
  28. /******************************************************* 
  29. *  类名称: 邻接表图 
  30. ********************************************************/   
  31. class Graph{  
  32.     private:  
  33.         int edge_num;//图边的个数  
  34.         int vertex_num;//图的顶点数目  
  35.         list<Node> * graph_list;//邻接表  
  36.     public:  
  37.         Graph(){}  
  38.         Graph(char* graph[], int edgenum);   
  39.         ~Graph();  
  40.         void print();//以邻接表的形式打印图信息  
  41.     private:  
  42.         vector<int> get_graph_value(char* graph[], int columns);//获得每条边的数据  
  43.         void addEdge(char* graph[], int columns);  
  44. };  
  45.   
  46.   
  47. /************************************************* 
  48. 函数名称:print 
  49. 功能描述:将图的信息以邻接表的形式输出到标准输出 
  50. 参数列表:无 
  51. 返回结果:无 
  52. *************************************************/  
  53. void Graph::print()  
  54. {  
  55.     cout << "******************************************************************" << endl;   
  56.     //for(int i = 0 ; i < MAX_VERTEX_NUM; ++i){  
  57.     for(int i = 0 ; i < vertex_num; ++i){  
  58.         if(graph_list[i].begin() != graph_list[i].end()){  
  59.             cout << i << "-->";  
  60.             for(list<Node>::iterator it = graph_list[i].begin(); it != graph_list[i].end(); ++it){  
  61.                 cout << (*it).vertex << "(边号:" << (*it).edge_num << ",权重:" << (*it).weight << ")-->";  
  62.             }  
  63.             cout << "NULL" << endl;  
  64.         }  
  65.     }  
  66.   
  67.     cout << "******************************************************************" << endl;   
  68. }  
  69.   
  70. /************************************************* 
  71. 函数名称:get_graph_value 
  72. 功能描述:将图的每一条边的信息保存到一个数组中 
  73. 参数列表: graph:指向图信息的二维数组 
  74.           columns:图的第几条边 
  75. 返回结果:无 
  76. *************************************************/  
  77. vector<int> Graph::get_graph_value(char* graph[], int columns)  
  78. {  
  79.     vector<int> v;  
  80.     char buff[20];  
  81.     int i = 0, j = 0, val;  
  82.     memset(buff, 0, 20);  
  83.   
  84.     while((graph[columns][i] != '\n') && (graph[columns][i] != '\0')){  
  85.         if(graph[columns][i] != ','){  
  86.             buff[j] = graph[columns][i];  
  87.             j++;  
  88.         }  
  89.         else{  
  90.             j = 0;  
  91.             val = atoi(buff);   
  92.             v.push_back(val);  
  93.             memset(buff, 0, 20);  
  94.         }  
  95.         i++;  
  96.     }  
  97.     val = atoi(buff);   
  98.     v.push_back(val);  
  99.   
  100.     return v;  
  101. }  
  102.   
  103.   
  104.   
  105. /************************************************* 
  106. 函数名称:addEdge 
  107. 功能描述:将图的每一条边的信息加入图的邻接表中 
  108. 参数列表:graph:指向图信息的二维数组 
  109.           columns:图的第几条边 
  110. 返回结果:无 
  111. *************************************************/  
  112. void Graph::addEdge(char* graph[], int columns)  
  113. {  
  114.     Node node;  
  115.     vector<int> v = get_graph_value(graph, columns);  
  116.   
  117.     node.edge_num = v[0];  
  118.     node.src = v[1];  
  119.     node.vertex = v[2];  
  120.     node.weight = v[3];  
  121.   
  122.     if(node.vertex > vertex_num)  
  123.         vertex_num = node.vertex;  
  124.   
  125.     //要考虑重复的边,但是边的权重不一样  
  126.     for(list<Node>::iterator it = graph_list[node.src].begin(); it != graph_list[node.src].end(); ++it){  
  127.         if((*it).vertex == node.vertex){  
  128.             if((*it).weight > node.weight){  
  129.                 (*it).weight = node.weight;     
  130.             }  
  131.             return;  
  132.         }  
  133.     }  
  134.     graph_list[node.src].push_back(node);  
  135. }  
  136.   
  137.   
  138. /************************************************* 
  139. 函数名称:构造函数 
  140. 功能描述:以邻接表的形式保存图的信息,并保存必须经过的顶点 
  141. 参数列表:graph:指向图信息的二维数组 
  142.           edgenum:图的边的个数 
  143. 返回结果:无 
  144. *************************************************/  
  145. Graph::Graph(char* graph[], int edgenum)  
  146. {  
  147.     edge_num =  edgenum;   
  148.     vertex_num = 0;  
  149.     graph_list = new list<Node>[MAX_VERTEX_NUM+1];  
  150.   
  151.     for(int i = 0; i < edgenum; ++i){  
  152.         addEdge(graph, i);     
  153.     }  
  154.   
  155.     vertex_num++;  
  156. }  
  157.   
  158.   
  159. /************************************************* 
  160. 函数名称:析构函数 
  161. 功能描述:释放动态分配的内存 
  162. 参数列表:无 
  163. 返回结果:无 
  164. *************************************************/  
  165. Graph::~Graph()  
  166. {  
  167.     delete[] graph_list;  
  168. }  
  169.   
  170. #endif  

测试函数的源代码:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <assert.h>  
  5. #include <time.h>  
  6. #include <sys/timeb.h>  
  7. #include <errno.h>  
  8. #include <unistd.h>  
  9. #include <signal.h>  
  10. #include <stdio.h>  
  11. #include "graph.h"  
  12.   
  13. #define MAX_LINE_LEN 4000  
  14.   
  15. int read_file(char ** const buff, const unsigned int spec, const char * const filename);  
  16. void release_buff(char ** const buff, const int valid_item_num);  
  17.   
  18. int main(int argc, char *argv[])  
  19. {  
  20.     char *topo[5000];  
  21.     int edge_num;  
  22.     char *demand;  
  23.     int demand_num;  
  24.   
  25.     char *topo_file = argv[1];  
  26.     edge_num = read_file(topo, 5000, topo_file);  
  27.     if (edge_num == 0)  
  28.     {  
  29.         printf("Please input valid topo file.\n");  
  30.         return -1;  
  31.     }  
  32.   
  33.     Graph G(topo, edge_num);//创建一个图对象G  
  34.     G.print();//以邻接表的形式打印图信息  
  35.   
  36.     release_buff(topo, edge_num);  
  37.   
  38.     return 0;  
  39. }  
  40.   
  41. /**************************************************************** 
  42. *   函数名称:read_file 
  43. *   功能描述: 读取文件中的图的数据信息 
  44. *   参数列表: buff是将文件读取的图信息保存到buff指向的二维数组中  
  45. *             spec是文件中图最大允许的边的个数 
  46. *             filename是要打开的图文件 
  47. *   返回结果:无 
  48. *****************************************************************/  
  49. int read_file(char ** const buff, const unsigned int spec, const char * const filename)  
  50. {  
  51.     FILE *fp = fopen(filename, "r");  
  52.     if (fp == NULL)  
  53.     {  
  54.         printf("Fail to open file %s, %s.\n", filename, strerror(errno));  
  55.         return 0;  
  56.     }  
  57.     printf("Open file %s OK.\n", filename);  
  58.   
  59.     char line[MAX_LINE_LEN + 2];  
  60.     unsigned int cnt = 0;  
  61.     while ((cnt < spec) && !feof(fp))  
  62.     {  
  63.         line[0] = 0;  
  64.         fgets(line, MAX_LINE_LEN + 2, fp);  
  65.         if (line[0] == 0)   continue;  
  66.         buff[cnt] = (char *)malloc(MAX_LINE_LEN + 2);  
  67.         strncpy(buff[cnt], line, MAX_LINE_LEN + 2 - 1);  
  68.         buff[cnt][4001] = 0;  
  69.         cnt++;  
  70.     }  
  71.     fclose(fp);  
  72.     printf("There are %d lines in file %s.\n", cnt, filename);  
  73.   
  74.     return cnt;  
  75. }  
  76.   
  77. /**************************************************************** 
  78. *   函数名称:release_buff 
  79. *   功能描述: 释放刚才读取的文件中的图的数据信息 
  80. *   参数列表: buff是指向文件读取的图信息 
  81. *             valid_item_num是指图中边的个数 
  82. *   返回结果:void 
  83. *****************************************************************/  
  84. void release_buff(char ** const buff, const int valid_item_num)  
  85. {  
  86.     for (int i = 0; i < valid_item_num; i++)  
  87.         free(buff[i]);  
  88. }  

测试用例0:

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 0,0,1,1  
  2. 1,0,2,2  
  3. 2,0,3,1  
  4. 3,2,1,3  
  5. 4,3,1,1  
  6. 5,2,3,1  
  7. 6,3,2,1  
  8. 7,3,0,1  

测试用例1:

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 0,0,13,15  
  2. 1,0,8,17  
  3. 2,0,19,1  
  4. 3,0,4,8  
  5. 4,1,0,4  
  6. 5,2,9,19  
  7. 6,2,15,8  
  8. 7,3,0,14  
  9. 8,3,11,12  
  10. 9,4,1,15  
  11. 10,4,5,17  
  12. 11,5,8,18  
  13. 12,5,9,14  
  14. 13,5,6,2  
  15. 14,6,17,4  
  16. 15,7,13,1  
  17. 16,7,16,19  
  18. 17,8,6,1  
  19. 18,8,12,17  
  20. 19,9,14,11  
  21. 20,10,12,1  
  22. 21,11,7,12  
  23. 22,11,4,7  
  24. 23,12,14,5  
  25. 24,13,17,12  
  26. 25,13,4,2  
  27. 26,14,19,9  
  28. 27,15,10,14  
  29. 28,15,18,2  
  30. 29,16,8,1  
  31. 30,17,9,14  
  32. 31,17,19,3  
  33. 32,17,18,10  
  34. 33,18,15,8  
  35. 34,18,3,8  
  36. 35,19,18,12  
  37. 36,2,3,20  
  38. 37,3,5,20  
  39. 38,5,7,20  
  40. 39,7,11,20  
  41. 40,11,13,20  
  42. 41,17,11,20  
  43. 42,11,19,20  
  44. 43,17,5,20  
  45. 44,5,19,20  

运行结果:

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. root@linux_ever:~/linux_ever/algorithm/graph_ch9# ls  
  2. case0  case1  graph.h  testGraph  testGraph.cpp  
  3. root@linux_ever:~/linux_ever/algorithm/graph_ch9# ./testGraph ./case0/topo.csv   
  4. Open file ./case0/topo.csv OK.  
  5. There are 8 lines in file ./case0/topo.csv.  
  6. ******************************************************************  
  7. 0-->1(边号:0,权重:1)-->2(边号:1,权重:2)-->3(边号:2,权重:1)-->NULL  
  8. 2-->1(边号:3,权重:3)-->3(边号:5,权重:1)-->NULL  
  9. 3-->1(边号:4,权重:1)-->2(边号:6,权重:1)-->0(边号:7,权重:1)-->NULL  
  10. ******************************************************************  
  11. root@linux_ever:~/linux_ever/algorithm/graph_ch9# ./testGraph ./case1/topo.csv    
  12. Open file ./case1/topo.csv OK.  
  13. There are 45 lines in file ./case1/topo.csv.  
  14. ******************************************************************  
  15. 0-->13(边号:0,权重:15)-->8(边号:1,权重:17)-->19(边号:2,权重:1)-->4(边号:3,权重:8)-->NULL  
  16. 1-->0(边号:4,权重:4)-->NULL  
  17. 2-->9(边号:5,权重:19)-->15(边号:6,权重:8)-->3(边号:36,权重:20)-->NULL  
  18. 3-->0(边号:7,权重:14)-->11(边号:8,权重:12)-->5(边号:37,权重:20)-->NULL  
  19. 4-->1(边号:9,权重:15)-->5(边号:10,权重:17)-->NULL  
  20. 5-->8(边号:11,权重:18)-->9(边号:12,权重:14)-->6(边号:13,权重:2)-->7(边号:38,权重:20)-->19(边号:44,权重:20)-->NULL  
  21. 6-->17(边号:14,权重:4)-->NULL  
  22. 7-->13(边号:15,权重:1)-->16(边号:16,权重:19)-->11(边号:39,权重:20)-->NULL  
  23. 8-->6(边号:17,权重:1)-->12(边号:18,权重:17)-->NULL  
  24. 9-->14(边号:19,权重:11)-->NULL  
  25. 10-->12(边号:20,权重:1)-->NULL  
  26. 11-->7(边号:21,权重:12)-->4(边号:22,权重:7)-->13(边号:40,权重:20)-->19(边号:42,权重:20)-->NULL  
  27. 12-->14(边号:23,权重:5)-->NULL  
  28. 13-->17(边号:24,权重:12)-->4(边号:25,权重:2)-->NULL  
  29. 14-->19(边号:26,权重:9)-->NULL  
  30. 15-->10(边号:27,权重:14)-->18(边号:28,权重:2)-->NULL  
  31. 16-->8(边号:29,权重:1)-->NULL  
  32. 17-->9(边号:30,权重:14)-->19(边号:31,权重:3)-->18(边号:32,权重:10)-->11(边号:41,权重:20)-->5(边号:43,权重:20)-->NULL  
  33. 18-->15(边号:33,权重:8)-->3(边号:34,权重:8)-->NULL  
  34. 19-->18(边号:35,权重:12)-->NULL  
  35. ******************************************************************  


输出结果的每一行的第一列表示各个顶点的标号。
比如:
0-->13(边号:0,权重:15)-->8(边号:1,权重:17)-->19(边号:2,权重:1)-->4(边号:3,权重:8)-->NULL

上面表示,顶点0到13的边的边号为0,权重为15。顶点0到顶点8的边的边号为1,权重为17。顶点0到顶点19的边的边号为2,权重为1。顶点0到顶点4的边的边号为3,权重为8。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值