@@ -55,6 +55,33 @@ public class Graph
55
55
/// <returns></returns>
56
56
double Weight ( Vertex v1 , Vertex v2 )
57
57
{
58
+ #region Блок для тестирования
59
+
60
+ if ( IsWeightFromTo ( v1 , 0 , 0 , v2 , 0 , 1 ) )
61
+ return 1.0 ;
62
+ if ( IsWeightFromTo ( v1 , 0 , 1 , v2 , 0 , 2 ) )
63
+ return 3.0 ;
64
+ if ( IsWeightFromTo ( v1 , 0 , 0 , v2 , 1 , 0 ) )
65
+ return 1.0 ;
66
+ if ( IsWeightFromTo ( v1 , 1 , 0 , v2 , 1 , 1 ) )
67
+ return 100.0 ;
68
+ if ( IsWeightFromTo ( v1 , 1 , 1 , v2 , 1 , 2 ) )
69
+ return 10.0 ;
70
+ if ( IsWeightFromTo ( v1 , 0 , 2 , v2 , 1 , 2 ) )
71
+ return 2.0 ;
72
+ if ( IsWeightFromTo ( v1 , 0 , 0 , v2 , 1 , 1 ) )
73
+ return 40.0 ;
74
+ if ( IsWeightFromTo ( v1 , 1 , 0 , v2 , 0 , 1 ) )
75
+ return 50.0 ;
76
+ if ( IsWeightFromTo ( v1 , 0 , 1 , v2 , 1 , 2 ) )
77
+ return 70.0 ;
78
+ if ( IsWeightFromTo ( v1 , 1 , 1 , v2 , 0 , 2 ) )
79
+ return 30.0 ;
80
+ if ( IsWeightFromTo ( v1 , 0 , 1 , v2 , 1 , 1 ) )
81
+ return 50.0 ;
82
+
83
+ #endregion
84
+
58
85
( double , double ) x1y1 = GetRealXY ( v1 ) ;
59
86
( double , double ) x2y2 = GetRealXY ( v2 ) ;
60
87
@@ -67,6 +94,17 @@ double Weight(Vertex v1, Vertex v2)
67
94
return Math . Sqrt ( sumOfSquares ) ;
68
95
}
69
96
97
+ private bool IsWeightFromTo ( Vertex v1 , int x1 , int y1 , Vertex v2 , int x2 , int y2 )
98
+ {
99
+ bool case1 = x1 == v1 . Coordinate . i && y1 == v1 . Coordinate . j &&
100
+ x2 == v2 . Coordinate . i && y2 == v2 . Coordinate . j ;
101
+
102
+ bool case2 = x1 == v2 . Coordinate . i && y1 == v2 . Coordinate . j &&
103
+ x2 == v1 . Coordinate . i && y2 == v1 . Coordinate . j ;
104
+
105
+ return case1 || case2 ;
106
+ }
107
+
70
108
/// <summary>
71
109
/// Возвращает кратчайший путь между двумя заданными вершинами графа и его длину
72
110
/// </summary>
@@ -76,25 +114,31 @@ double Weight(Vertex v1, Vertex v2)
76
114
/// <returns></returns>
77
115
public List < Point2D > FindShortestPathAndLength ( Point2D startPoint , Point2D goalPoint , out double shortestPathLength )
78
116
{
79
- shortestPathLength = 0.0 ;
117
+ shortestPathLength = 0.0 ; // Здесь будет храниться длина искомого пути
80
118
81
- // Вершине из которой будем искать путь присваиваем нулевую метку
119
+ // Стартовой вершине присваиваем нулевую метку
82
120
Vertex start = Vertices [ startPoint . i , startPoint . j ] ;
83
121
start . Label = 0.0 ;
84
122
85
- // Целевую вершину пометим, что она целевая
123
+ // Сохраним отдельно целевую вершину
86
124
Vertex goal = Vertices [ goalPoint . i , goalPoint . j ] ;
87
- goal . IsGoal = true ;
88
125
89
- // Помечаем стартовую вершину как текущую
90
- Vertex current = start ;
126
+ // Очередь с приоритетами
127
+ List < Vertex > priorityQueue = new List < Vertex > ( ) ;
128
+ priorityQueue . Add ( start ) ;
91
129
92
- // В этом списке будем копить посещенные вершины
93
- List < Vertex > visitedVertices = new List < Vertex > ( ) ;
94
-
95
- while ( current != null )
130
+ // Цикл заканчивает свою работу, когда очередь с приоритетами пустая, либо когда целевая вершина оказалась посещена
131
+ while ( priorityQueue . Any ( ) && ! goal . IsVisited )
96
132
{
97
- // Находим подходящих (годных) соседей: которые еще не посещены, не являются препятствиями и т.п.
133
+ // Получаем из очереди с приоритетами вершину с минимальной меткой и одновременно удаляем эту вершину из очереди
134
+ Vertex current = RemoveAndReturnVertexWithMinimalLabel ( priorityQueue ) ;
135
+
136
+ if ( current . IsVisited )
137
+ continue ;
138
+
139
+ current . IsVisited = true ;
140
+
141
+ // Находим подходящих соседей: которые еще не посещены, не являются препятствиями и т.п.
98
142
List < Vertex > neighbors = GetValidNeighbors ( current ) ;
99
143
100
144
foreach ( Vertex neighbor in neighbors )
@@ -104,15 +148,9 @@ public List<Point2D> FindShortestPathAndLength(Point2D startPoint, Point2D goalP
104
148
{
105
149
neighbor . Label = currentWeight ;
106
150
neighbor . CameFrom = current . Coordinate ;
151
+ priorityQueue . Add ( neighbor ) ;
107
152
}
108
- }
109
-
110
- // После того как все подходящие соседи рассмотрены (им расставлены метки), помечаем текущую вершину как посещенную
111
- current . IsVisited = true ;
112
- // и добавляем ее в список посещенных вершин
113
- visitedVertices . Add ( current ) ;
114
- // Используем этот список посещенных вершин для поиска новой текущей вершины
115
- current = GetCurrent ( visitedVertices ) ;
153
+ }
116
154 }
117
155
118
156
// В конце работы алгоритма в целевой вершине в свойстве Label будет находиться длина искомого пути
@@ -121,6 +159,21 @@ public List<Point2D> FindShortestPathAndLength(Point2D startPoint, Point2D goalP
121
159
return GetShortestPath ( goal ) ;
122
160
}
123
161
162
+ /// <summary>
163
+ /// Находит и возвращает вершину с минимальной меткой из очереди с приоритетами, после этого удаляет найденную вершину из очереди
164
+ /// </summary>
165
+ /// <param name="priorityQueue">очередь с приоритетом</param>
166
+ /// <returns></returns>
167
+ private Vertex RemoveAndReturnVertexWithMinimalLabel ( List < Vertex > priorityQueue )
168
+ {
169
+ // Находим вершину с минимальной меткой
170
+ Vertex vertexWithMinimalLabel = priorityQueue . Aggregate ( ( currentMin , v ) => ( v . Label < currentMin . Label ? v : currentMin ) ) ;
171
+ // Удаляем эту вершину из очереди
172
+ priorityQueue . Remove ( vertexWithMinimalLabel ) ;
173
+ // и возвращаем эту вершину
174
+ return vertexWithMinimalLabel ;
175
+ }
176
+
124
177
/// <summary>
125
178
/// Формирует кратчайший путь, начиная с целевой вершины и заканчивая стартовой
126
179
/// </summary>
@@ -141,44 +194,7 @@ private List<Point2D> GetShortestPath(Vertex goal)
141
194
}
142
195
143
196
return path ;
144
- }
145
-
146
- /// <summary>
147
- /// Возвращает текущую вершину, используя список посещенных вершин и подходящих соседей (которые валидны и не являются целевой вершиной)
148
- /// </summary>
149
- /// <param name="visitedVertices">список посещенных вершин</param>
150
- /// <returns></returns>
151
- private Vertex GetCurrent ( List < Vertex > visitedVertices )
152
- {
153
- List < Vertex > validAndNotGoalNeighbors = new List < Vertex > ( ) ;
154
-
155
- foreach ( Vertex v in visitedVertices )
156
- if ( HasValidAndNotGoalNeighbors ( v , out validAndNotGoalNeighbors ) )
157
- break ;
158
-
159
- // Если не нашлось ни одного подходящего соседа, значит мы дошли до целевой вершины. Алгоритм завершен
160
- if ( ! validAndNotGoalNeighbors . Any ( ) )
161
- return null ;
162
-
163
- // Иначе находим и возвращаем соседа с минимальной меткой
164
- double minLabel = validAndNotGoalNeighbors . Min ( v => v . Label ) ;
165
- Vertex newCurrent = validAndNotGoalNeighbors . First ( v => v . Label == minLabel ) ;
166
-
167
- return newCurrent ;
168
- }
169
-
170
- /// <summary>
171
- /// Возвращает true, если у текущей вершины имеются валидные соседи, за исключением целевой вершины, иначе false,
172
- /// а также сам список этих соседей
173
- /// </summary>
174
- /// <param name="vertex">текущая вершина</param>
175
- /// <param name="validAndNotGoalNeighbors">список подходящих соседей</param>
176
- /// <returns></returns>
177
- private bool HasValidAndNotGoalNeighbors ( Vertex vertex , out List < Vertex > validAndNotGoalNeighbors )
178
- {
179
- validAndNotGoalNeighbors = GetValidNeighbors ( vertex ) . Where ( v => ! v . IsGoal ) . ToList ( ) ;
180
- return validAndNotGoalNeighbors . Any ( ) ;
181
- }
197
+ }
182
198
183
199
/// <summary>
184
200
/// Возвращает величину уклона между двумя вершинами в градусах
0 commit comments