Пошук у ширинуПо́шук у ширину́ — алгоритм пошуку на графі[1]. Якщо задано граф G = (V, E) та початкову вершину s, алгоритм пошуку в ширину систематично обходить всі досяжні із s вершини. На першому кроці вершина s позначається, як пройдена, а в список додаються всі вершини, досяжні з s без відвідування проміжних вершин. На кожному наступному кроці всі поточні вершини списку відмічаються, як пройдені, а новий список формується із вершин, котрі є ще не пройденими сусідами поточних вершин списку. Для реалізації списку вершин найчастіше використовується черга та принцип FIFO. Виконання алгоритму продовжується до досягнення шуканої вершини або до того часу, коли на певному кроці в список не включається жодна вершина. Другий випадок означає, що всі вершини, доступні з початкової, уже відмічені, як пройдені, а шлях до цільової вершини не знайдений. Алгоритм має назву пошуку в ширину, оскільки «фронт» пошуку (між пройденими та непройденими вершинами) одноманітно розширюється вздовж всієї своєї ширини. Тобто, алгоритм проходить всі вершини на відстані k перед тим як пройти вершини на відстані k+1.[1] АлгоритмНаведемо кроки алгоритму
Формальний опис алгоритмуНижче наведено псевдокод алгоритму для випадку, коли необхідно лише знайти цільовий вузол. Залежно від конкретного застосування алгоритму, може знадобитися додатковий код, що забезпечує збереження потрібної інформації (відстань від початкового вузла, вузол-батько і т. ін.) Рекурсивна формулювання: BFS(start_node, goal_node) {
return BFS'({start_node}, ∅, goal_node);
}
BFS'(fringe, visited, goal_node) {
if(fringe == ∅) {
// цільовий вузол не знайдено
return false;
}
if (goal_node ∈ fringe) {
return true;
}
return BFS'({child | x ∈ fringe, child ∈ expand(x)} \ visited, visited ∪ fringe, goal_node);
} Ітеративна формулювання: BFS(start_node, goal_node) {
for(all nodes i) visited[i] = false; // спочатку список відвіданих вузлів порожній
queue.push(start_node); // починаючи з вузла-джерела
visited[start_node] = true;
while(! queue.empty() ) { // поки черга не порожня
node = queue.pop(); // витягти перший елемент в черзі
if(node == goal_node) {
return true; // перевірити, чи не є поточний вузол цільовим
}
foreach(child in expand(node)) { // всі наступники поточного вузла, ...
if(visited[child] == false) { // ... які ще не були відвідані ...
queue.push(child); // ... додати в кінець черги...
visited[child] = true; // ... і позначити як відвідані
}
}
}
return false; // цільовий вузол недосяжний
} ВластивостіПозначимо число вершин і ребер в графі як і відповідно. Просторова складністьТак як в пам'яті зберігаються всі розгорнуті вузли, просторова складність алгоритму становить . Алгоритм пошуку з ітеративним поглибленням схожий на пошук в ширину тим, що при кожній ітерації перед переходом на наступний рівень досліджується повний рівень нових вузлів, але вимагає значно менше пам'яті. Часова складністьТак як в гіршому випадку алгоритм відвідує всі вузли графу, при зберіганні графу у вигляді списків суміжності[en], тимчасова складність алгоритму становить . ПовнотаЯкщо у кожного вузла є кінцеве число наступників, алгоритм є повним: якщо рішення існує, алгоритм пошуку в ширину його знаходить, незалежно від того, чи є граф кінцевим. Однак якщо рішення не існує, на нескінченному графі пошук не закінчується. ОптимальністьЯкщо довжини ребер графу рівні між собою, пошук в ширину є оптимальним, тобто завжди знаходить найкоротший шлях. В разі зваженого графу пошук в ширину знаходить шлях, що містить мінімальну кількість ребер, але не обов'язково найкоротший. Пошук за критерієм вартості є узагальненням пошуку в ширину і оптимальний на зваженому графі з невід'ємними вагами ребер. Алгоритм відвідує вузли графу в порядку зростання вартості шляху з початкового вузла і зазвичай використовує чергу з пріоритетами. Джерела інформації
Див. також |