【数据结构】图的邻接表表示(GNU C++实现)

前端之家收集整理的这篇文章主要介绍了【数据结构】图的邻接表表示(GNU C++实现)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

图G=(V,E)有两种标准的方法,即邻接表和邻接矩阵,它们即可以表示有向图,又可以表示无向图;其中邻接表,通常表示的是稀疏图;稠密图通常用邻接矩阵表示;


复杂度分析

(1)邻接矩阵的空间复杂度为O(V*V),针对有向图;

(2)邻接表的空间复杂度为O(E),针对有向图;


本实验的图为稀疏的有环有向带权图;随机产生;

方案:

(1)一般我们用自己的C语言的指针和动态内存来构建邻接表,但是还是有所弊端,动态内存,如果用户一直不显示释放,将会一直驻留内存,无法释放;

(2)如果使用C++语言中的智能指针,对本场景也会有问题;

(2.1)动态内存使用智能指针来管理,节点的内部指向仍旧使用普通指针来管理,那么当使用的动态内存没有计数指向(use_count为0)时,就会自动释放,造,普通指针指向错误

(2.2)动态内存使用智能指针来管理,节点的内部指向也使用智能指针来管理;由于内部的智能指针代表的是一个新的指向计数,失去了普通指针操作内存的作用;

(3)虽然(1)可以实现,但是是自己造轮子,效率和可靠性不够,因此采用STL标准库的vector来管理智能指针;


程序实现:

Directed_Graph.h

  1. #include <vector>
  2. #include <random>
  3. #include <memory>
  4. #include <iostream>
  5. #include <ctime>
  6. using namespace std;
  7.  
  8. struct V_Nodes{ //循环链表表示
  9. unsigned id; //顶点的编号,从0开始到vertex_nums-1;
  10. unsigned weight; //权值
  11. };
  12.  
  13. class Directed_Graph
  14. {
  15. private:
  16. unsigned vertex_nums; //顶点的个数
  17. vector<vector<shared_ptr<V_Nodes>>> vnodes_arrays; //存放邻接表的顶点表向量,vnodes_arrays中存放每一个顶点的双向循环链表,使用list速度较慢;
  18.  
  19. /*
  20. 函数主要是构造邻接表的边的过程,成功返回true,失败返回false
  21. */
  22. bool insert_edge(unsigned source,unsigned dest,unsigned weight)
  23. {
  24. vector<shared_ptr<V_Nodes>>& vs = vnodes_arrays[source];
  25. auto iter = vs.cbegin();
  26. for(; iter != vs.cend(); iter++){
  27. if((*iter)->id == dest){ //看是否已经构造过该边了
  28. return false;
  29. }else if((*iter)->id > dest){ //找到插入节点的后节点
  30. break;
  31. }
  32. }
  33. shared_ptr<V_Nodes> p = make_shared<V_Nodes>(); //构造该边
  34. p->id = dest;
  35. p->weight = weight;
  36. vs.insert(iter,p); //在iter之前插入新的节点,即边
  37. return true;
  38. }
  39. public:
  40. Directed_Graph() //构造函数初始化时将会随机建立有向有环带权有向图
  41. {
  42. default_random_engine e(time(0)); //设置种子
  43. uniform_int_distribution<unsigned> u(5,9); //均匀分布,u为随机数源
  44. vertex_nums = u(e); //产生顶点数目
  45. for(size_t i = 0; i < vertex_nums; i++){
  46. vector<shared_ptr<V_Nodes>> vectors;
  47. vnodes_arrays.push_back(vectors); //放入循环链表
  48. }
  49.  
  50. uniform_int_distribution<unsigned> u2(0,vertex_nums-1); //顶点的随机
  51. uniform_int_distribution<unsigned> u3(3,15); //边值的随机
  52. unsigned source,dest,weight; //源顶点,目的顶点,权值
  53. unsigned max_edges_nums = vertex_nums * vertex_nums; //由于有环,故打印的总边数并不是无环有向图的vertex_nums * (vertex_nums - 1);
  54. unsigned count = 0;
  55. unsigned total_count = 0;
  56.  
  57. while(count < max_edges_nums / 3){ //当边数小于一半时就继续产生
  58. source = u2(e); //产生随机
  59. dest = u2(e);
  60. weight = u3(e);
  61. if(insert_edge(source,weight)){
  62. count++;
  63. }
  64. total_count++;
  65. }
  66.  
  67. //cout<<vertex_nums<<" "<<max_edges_nums<<" "<<count<<" "<<total_count<<endl;
  68. }
  69.  
  70. void printf_graph() //打印图
  71. {
  72. int count = 0;
  73. int counts = 0;
  74. for(auto vs : vnodes_arrays){
  75. for(auto r : vs){
  76. cout<<"<"<<count<<","<<r->id<<">("<<r->weight<<") ";
  77. counts++;
  78. }
  79. cout<<endl;
  80. count++;
  81. }
  82. }
  83. };

main.cpp

  1. #include "Directed_Graph.h"
  2.  
  3. int main(void)
  4. {
  5. Directed_Graph directed_graph;
  6.  
  7. cout<<"---------------------------------------------------------\n";
  8. cout<<"随机产生的有向有环带权图如下:\n";
  9. directed_graph.printf_graph();
  10. cout<<"---------------------------------------------------------\n";
  11. return 0;
  12. }

程序输出



注,方案1的实现;由于内存释放得自己实现,后续图的操作将不会使用此方案,因为我们要实现动态内存的自动管理;

Directed_Graph.h

  1. #include <vector>
  2. #include <random>
  3. #include <memory>
  4. #include <iostream>
  5. #include <ctime>
  6. using namespace std;
  7.  
  8. struct V_Nodes{ //循环链表表示
  9. unsigned id; //顶点的编号,从0开始到vertex_nums-1;
  10. unsigned weight; //权值
  11. V_Nodes *p_next; //邻接表指向下一个顶点
  12. V_Nodes *p_pre; //邻接表指向上一个顶点
  13. };
  14.  
  15.  
  16. class Directed_Graph
  17. {
  18. private:
  19. unsigned vertex_nums; //顶点的个数
  20. vector<V_Nodes *> vnodes_lists; //存放邻接表的顶点表向量,只是存放指针
  21.  
  22. /*
  23. 函数主要是构造邻接表的边的过程,成功返回true,失败返回false
  24. */
  25. bool insert_edge(unsigned source,unsigned weight)
  26. {
  27. V_Nodes* temp = vnodes_lists[source]; //如果不是引用,也就是拷贝,获得顶点向量
  28. if(dest == source) //源顶点等于目的顶点时
  29. {
  30. if(temp->weight == 0){
  31. temp->weight = weight; //自身到自身也有全值,有环
  32. return true;
  33. }else{
  34. return false;
  35. }
  36. }
  37.  
  38.  
  39. V_Nodes *p_next = temp->p_next; //指向下一个节点
  40.  
  41. while(p_next != temp->p_next->p_pre){ //当到了队列头时
  42. if(p_next->id == dest){ //已经生成过该条边了,返回flase
  43. return false;
  44. }else if(p_next->id > dest){ //邻接表中,邻接的边数递增
  45. break; //没有找到
  46. }
  47. p_next = p_next->p_next;
  48. };
  49. //在一个顶点的后面插入一个节点,只能有两种情况,初始化的时候会行不通
  50. V_Nodes* current = new V_Nodes(); //创建新节点,使前节点指向新节点
  51. current->id = dest; //改变新节点的值
  52. current->weight = weight;
  53.  
  54. current->p_next = p_next; //改变新节点的next
  55. current->p_pre = p_next->p_pre; //改变新节点的pre
  56. p_next->p_pre->p_next = current; //改变前节点的p_next
  57. p_next->p_pre = current; //改变后节点的pre
  58.  
  59.  
  60. return true;
  61. }
  62. public:
  63. Directed_Graph() //构造函数初始化时将会随机建立有向有环带权有向图
  64. {
  65. default_random_engine e(time(0)); //设置种子
  66. uniform_int_distribution<unsigned> u(7,12); //均匀分布,u为随机数源
  67. vertex_nums = u(e); //产生顶点数目
  68. for(size_t i = 0; i < vertex_nums; i++){
  69. V_Nodes* v_nodes = new V_Nodes();
  70. v_nodes->id = i;
  71. v_nodes->weight = 0; //初始化,权重为0
  72. v_nodes->p_next = v_nodes;
  73. v_nodes->p_pre = v_nodes;
  74. //cout<<i<<" "<<v_nodes<<endl;
  75. vnodes_lists.push_back(v_nodes); //copy,p_next内部指针指向仍是以前的内存,存放这几个顶点的邻接表向量
  76. }
  77.  
  78.  
  79. uniform_int_distribution<unsigned> u2(0,vertex_nums-1); //顶点的随机
  80. uniform_int_distribution<unsigned> u3(3,15); //边值的随机
  81. unsigned source,weight; //源顶点,目的顶点,权值
  82. unsigned max_edges_nums = vertex_nums * vertex_nums; //由于有环,故打印的总边数并不是无环有向图的vertex_nums * (vertex_nums - 1);
  83. unsigned count = 0;
  84. unsigned total_count = 0;
  85. while(count < max_edges_nums / 3){ //当边数小于一半时就继续产生
  86. source = u2(e); //产生随机
  87. dest = u2(e);
  88. weight = u3(e);
  89. if(insert_edge(source,weight)){
  90. count++;
  91. }
  92. total_count++;
  93. }
  94. }
  95.  
  96.  
  97. void printf_graph()
  98. {
  99. for(auto iter = vnodes_lists.cbegin(); iter != vnodes_lists.cend(); iter++){
  100. V_Nodes *p_next = (*iter)->p_next;
  101. if((*iter)->weight > 0){ //打印有环的顶点
  102. cout<<"<"<<(*iter)->id<<","<<(*iter)->id<<">("<<(*iter)->weight<<") ";
  103. }
  104. while(p_next != (*iter)->p_next->p_pre){ //打印邻接的顶点和边
  105. cout<<"<"<<(*iter)->id<<","<<p_next->id<<">("<<p_next->weight<<") "; //打印
  106. p_next = p_next->p_next;
  107. }
  108. cout<<endl;
  109. }
  110. }
  111. };

猜你在找的数据结构相关文章