图的存储与遍历

图的存储结构主要有邻接矩阵和邻接表存储两种存储方式

在遍历方式上有广度遍历和深度遍历

广度遍历就是按层次遍历,可以借助队列实现

深度遍历可以理解为一种递归遍历

在练习程序中选择用邻接表结构存储图,图的结构如图所示

如果邻接矩阵结构存储图的话,当图的节点较多,但边数较时浪费了存储空间。

邻接表结构的话可以先用头节点链表存储所有的图的节点,每一个头节点又是以该头节点为起始节点的边的链表的起始点。

每个链接表的节点结构为

该节点在头节点链表中的位置,和其下一节点。

我觉得做图的相关存储时的关键点:

 图的头节点链表的结构和边链表的结构

头节点中要包含该节点的值和边链表的链表指针,在C#中用类的示例来表示

示例代码为:

 

 

ContractedBlock.gif ExpandedBlockStart.gif Code
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Text;
  4 
  5 namespace LinkGraph
  6 {
  7     /// <summary>
  8     /// 采用邻接表方式存储图
  9     /// </summary>
 10     /// <typeparam name="T">图的节点的数据类型</typeparam>
 11  public class GraphLink<T>
 12     {
 13          /*图的节点*/
 14 
 15      public GraphLink() 
 16      {
 17          NodeList = new List<GraphLink<T>.HeadNode>();
 18          nodeCount = 0;
 19          borderCount = 0;
 20      }
 21      public class HeadNode
 22      {
 23        public  T data;
 24        public   bool isVisited;
 25        public   SubNode<HeadNode> Next;
 26      }
 27 
 28      public class SubNode<vType>
 29      {
 30          /// <summary>
 31          /// 该子节点在节点链表中的位置
 32          /// </summary>
 33        public   vType adjex;//指向头节点链表中的前一个节点
 34          /// <summary>
 35          /// 以该节点为目的节点的边的权值
 36          /// </summary>
 37        public   T weight;  //以该节点为目的节点的边的权值
 38         /// <summary>
 39         /// 子节点的下一个子节点
 40         /// </summary>
 41  
 42        public   SubNode<vType> next;
 43 
 44        public SubNode(vType value) 
 45        {
 46            adjex = value;
 47        }  
 48      }
 49      /// <summary>
 50      /// 存放图的所有节点的链表
 51      /// </summary>
 52      public List<HeadNode> NodeList;
 53      /// <summary>
 54      /// 图中节点数
 55      /// </summary>
 56      public int nodeCount;
 57      /// <summary>
 58      /// 图中边的数目
 59      /// </summary>
 60      public int borderCount;
 61      /// <summary>
 62      /// 在图中增加一个节点
 63      /// </summary>
 64      /// <param name="NewNodeValue">节点的值</param>
 65      public void AddNode(T NewNodeValue) 
 66      {
 67          if (NodeList == null) NodeList = new List<GraphLink<T>.HeadNode>();
 68          foreach (HeadNode h in NodeList) 
 69          {
 70              /*
 71               * 判断图中时有已存在的节点
 72               */
 73              if (h.data.Equals(NewNodeValue) == true
 74              {
 75                  Console.WriteLine("图中已经存在该节点");
 76                  return;
 77              }
 78 
 79          }
 80          HeadNode t = new GraphLink<T>.HeadNode();
 81          t.data = NewNodeValue;
 82          t.isVisited = false;
 83          t.Next = null;
 84          /*
 85             图的节点链表加入该子节点
 86           */
 87          NodeList.Add(t);
 88          nodeCount = NodeList.Count;
 89      }
 90 
 91      /// <summary>
 92      /// 在图中添加一条边
 93      /// </summary>
 94      /// <param name="FromValue">起始节点的值</param>
 95      /// <param name="ToValue">目的节点的值</param>
 96      /// <param name="BorderWeight"></param>
 97      public void AddBorder(T FromValue, T ToValue, T BorderWeight) 
 98      {
 99 
100          DirectAddBorder(FromValue,ToValue,BorderWeight);
101          DirectAddBorder(ToValue, FromValue, BorderWeight);        
102          borderCount++;
103         
104      }
105 
106      void DirectAddBorder(T FromValue, T ToValue, T w)
107      {
108          HeadNode FromNode;
109          HeadNode ToNode;
110          FromNode = Find(FromValue);
111          if (FromNode == null)
112          {
113              ErrorShow("图中找不到起始节点");
114              return;
115          }
116          ToNode = Find(ToValue);
117          if (ToNode == null)
118          {
119              ErrorShow("图中找不到目的节点");
120              return;
121          }
122 
123 
124 
125          SubNode<HeadNode> t = new GraphLink<T>.SubNode<GraphLink<T>.HeadNode>(ToNode);
126          t.weight = w;
127 
128          if (FromNode.Next == null)   //头节点没有子节点的情况
129          {
130              FromNode.Next = t;
131          }
132          else
133          {
134 
135              SubNode<HeadNode> p = FromNode.Next;
136              SubNode<HeadNode> pr = p;
137 
138              while (p != null)
139              {
140                  /*
141                    判断时有已有存在的边
142                   * 如果有的话则提示并且返回
143                   */
144                  if (p.adjex.data.Equals(ToValue) == true)
145                  {
146                      ErrorShow("这条边已经存在");
147                      return;
148                  }
149                  pr = p;
150                  p = p.next;
151 
152              }
153              pr.next = t;
154          }
155          
156 
157      }
158      /// <summary>
159      /// 递归方式 深度优先遍历图  
160      /// </summary>
161      /// <param name="StartNodeValue">起始节点的值</param>
162      public void DFSTraverse(T StartNodeValue) 
163      {
164          HeadNode h = Find(StartNodeValue);
165          if (h == null
166          {
167              ErrorShow("图中不存在该起始节点");
168          }
169          DFS(h);
170 
171      }
172 
173      void DFS(HeadNode n) 
174      {
175          VistNode(n);
176 
177          if (n.Next != null
178          {
179              /*
180                访问已访问节点的子节点 
181               */
182              SubNode<HeadNode> p = n.Next;
183          
184              while (p != null
185              {
186                  /*如果已访问节点n的子节点中有未访问的节点
187                   * 则递归访问该节点
188                   */
189                  if (p.adjex.isVisited == false
190                  {
191                      DFS(p.adjex); //递归遍历未访问过的头节点
192                  }  
193 
194                  p = p.next;
195              }
196          }
197      }
198      /// <summary>
199      /// 广度优先遍历图
200      /// </summary>
201      /// <param name="StartNodeValue">起始节点的值</param>
202      public void BFSTraverse(T StartNodeValue) 
203      {
204          HeadNode h = Find(StartNodeValue);
205          if (h == null)
206          {
207              ErrorShow("图中不存在该起始节点");
208          }
209          BFS(h);
210      }
211 
212      void BFS(HeadNode n) 
213      {
214          /*借助先入先出的队列来按层次访问图*/
215          Queue<HeadNode> NodeQueue = new Queue<GraphLink<T>.HeadNode>();
216 
217          NodeQueue.Enqueue(n);
218          while (NodeQueue.Count>0
219          {
220              HeadNode h= NodeQueue.Dequeue();
221              /*如果该节点还未被访问则访问该节点*/
222              if (h.isVisited == false)
223              {
224                  VistNode(h);
225              }
226 
227              if (h.Next != null
228              {
229                  SubNode<HeadNode> p = h.Next;
230                  while (p != null
231                  {
232                      /*如果该节点还未被访问则访问该节点*/
233                      if (p.adjex.isVisited == false)
234                      {
235                          VistNode(p.adjex);
236                          NodeQueue.Enqueue(p.adjex);
237                      }
238                      p = p.next;
239 
240                  }
241              }
242          }
243      }
244 
245      void VistNode(HeadNode n) 
246      {
247          Console.WriteLine(n.data.ToString());
248          /*设置已访问标志*/
249          n.isVisited = true;  
250          
251      }
252 
253      void ErrorShow(string txt) 
254      {
255          Console.WriteLine(txt);
256      }
257      /// <summary>
258      /// 在节点链表中查找节点
259      /// </summary>
260      /// <param name="keyValue">待查找节点的数据域的值</param>
261      /// <returns></returns>
262       HeadNode Find(T keyValue) 
263      {
264          foreach (HeadNode t in NodeList) 
265          {
266          
267            if(t.data.Equals(keyValue)==true)
268            {
269              return t;
270            }
271          }
272          return null;
273      }
274      /// <summary>
275      /// 显示所有节点的值
276      /// </summary>
277       public void ShowAllNodes() 
278       {
279           foreach (HeadNode t in NodeList) 
280           {
281               Console.WriteLine(t.data);
282           }
283       }
284 
285 
286     }
287 
288  
289 }
290 

 

 

 

 

转载于:https://www.cnblogs.com/smile2you/archive/2009/03/06/1404932.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值