图的存储与遍历
-
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.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
图的存储结构主要有邻接矩阵和邻接表存储两种存储方式
在遍历方式上有广度遍历和深度遍历
广度遍历就是按层次遍历,可以借助队列实现
深度遍历可以理解为一种递归遍历
在练习程序中选择用邻接表结构存储图,图的结构如图所示
如果邻接矩阵结构存储图的话,当图的节点较多,但边数较时浪费了存储空间。
邻接表结构的话可以先用头节点链表存储所有的图的节点,每一个头节点又是以该头节点为起始节点的边的链表的起始点。
每个链接表的节点结构为
该节点在头节点链表中的位置,和其下一节点。
我觉得做图的相关存储时的关键点:
图的头节点链表的结构和边链表的结构
头节点中要包含该节点的值和边链表的链表指针,在C#中用类的示例来表示
示例代码为:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
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