Анотація: Пошук в ширину. Процедура пошуку в ширину. BFS-дерево і обчислення відстаней.
Пошук в ширину
З цієї лекції ми починаємо розглядати алгоритми для вирішення різних завдань на графах. Спочатку мова піде про завдання аналізу графів з метою виявлення їх структури і обчислення метричних характеристик. Багато задач такого роду можуть бути вирішені шляхом систематичного обходу графа з відвідуванням всіх його вершин і дослідженням всіх його ребер. Такий обхід можна виконати багатьма способами, в дійсності ж широке поширення завдяки своїй простоті, а в більшій мірі своєї корисності, отримали дві стратегії - пошук в глибину і пошук в ширину. Розгляд цих алгоритмів і прикладів їх застосування до задач аналізу графів становить зміст цієї та двох наступних лекцій.
Процедура пошуку в ширину
Робота будь-якого алгоритму обходу полягає в послідовному відвідуванні вершин і дослідженні ребер. Які саме дії виконуються при відвідуванні вершини і дослідженні ребра - залежить від конкретного завдання, для вирішення якої проводиться обхід. У будь-якому випадку, однак, факт відвідування вершини запам'ятовується, так що з моменту відвідування і до кінця роботи алгоритму вона вважається відвідується. Вершину, яка ще не відвідали, будемо називати новою. В результаті відвідування вершина стає відкритою і залишається такою, поки не будуть досліджені всі інцидентні їй ребра. Після цього вона перетворюється в закриту.
Ідея пошуку в ширину полягає в тому, щоб відвідувати вершини в порядку їх віддаленості від деякої заздалегідь обраної або зазначеної стартовою вершини . Інакше кажучи, спочатку відвідується сама вершина , Потім все вершини, суміжні з , Тобто знаходяться від неї на відстані , Потім вершини, що знаходяться від на відстані , і т.д.
Розглянемо алгоритм пошуку в ширину із заданою стартовою вершиною . Спочатку все вершини позначаються як нові. Першою відвідується вершина , Вона стає єдиною відкритою вершиною. Надалі кожен черговий крок починається з вибору деякої відкритої вершини . Ця вершина стає активною. Далі досліджуються ребра, інцидентні активної вершині. Якщо таке ребро з'єднує вершину з новою вершиною , То вершина відвідується і перетворюється у відкриту. Коли все ребра, інцидентні активної вершині, досліджені, вона перестає бути активною і стає закритою. Після цього вибирається нова активна вершина, і описані дії повторюються. Процес закінчується, коли безліч відкритих вершин стає порожнім.
Основна особливість пошуку в ширину, що відрізняє його від інших способів обходу графів, полягає в тому, що в якості активної вершини вибирається та з відкритих, яка була відвідана раніше інших. Саме цим забезпечується головна властивість пошуку в ширину: чим ближче вершина до старту, тим раніше вона буде переглянуло. Для реалізації такого правила вибору активної вершини зручно використовувати для зберігання безлічі відкритих вершин чергу - коли нова вершина стає відкритою, вона додається в кінець черги, а активна вибирається в її початку. Схематично процес зміни статусу вершин зображений на Мал. 4.1 . Чорним гуртком позначена активна вершина.
Опишемо процедуру пошуку в ширину (BFS - від англійської назви цього алгоритму - Breadth First Search) з заданою стартовою вершини . У цьому описі позначає безліч всіх вершин, суміжних з вершиною , - черга відкритих вершин. Передбачається, що при відвідуванні вершини вона позначається як відвідана і ця позначка означає, що вершина вже не є новою.
Procedure BFS (a)
- відвідати вершину
- while do
- for do
- досліджувати ребро
- if вершина нова
- then відвідати вершину
Відзначимо деякі властивості процедури BFS.
- Процедура BFS закінчує роботу після кінцевого числа кроків.
Дійсно, при кожному повторенні циклу while з черги видаляється одна вершина. Вершина додається до черги тільки тоді, коли вона відвідується. Кожна вершина може бути переглянуло не більше одного разу, так як відвідуються тільки нові вершини, а в результаті відвідування вершина перестає бути новою. Таким чином, число повторень циклу while не перевищує числа вершин.
- В результаті виконання процедури BFS будуть відвідані всі вершини з компоненти зв'язності, що містить вершину a, і тільки вони.
Очевидно, що вершина може бути переглянуло тільки в тому випадку, коли існує шлях, що з'єднує її з вершиною (Так як відвідується завжди вершина, суміжна з уже відвідується). Те, що кожна така вершина буде переглянуло, легко доводиться індукцією по відстані від даної вершини до вершини .
- Час роботи процедури BFS є , де - число ребер в компоненті зв'язності, що містить вершину .
З попередніх міркувань видно, що кожна вершина з цієї компоненти стає активною точно один раз. Внутрішній цикл for для активної вершини виконується раз. Отже, загальне число повторень внутрішнього циклу дорівнюватиме .
Отже, процедура BFS ( ) Виробляє обхід компоненти зв'язності, що містить вершину . Щоб перейти до іншої компоненті, досить вибрати якусь нову вершину (якщо такі вершини ще є), в якості стартової. нехай - безліч вершин графа. Наступний алгоритм здійснює повний обхід графа методом пошуку в ширину.
Алгоритм 1. Пошук в ширину.
- позначити всі вершини як нові
- створити порожню чергу
- for do if нова then BFS ( )
З огляду на, що цикл for в рядку повторюється раз, де - число вершин графа, отримуємо загальну оцінку трудомісткості . Необхідно відзначити, що ця оцінка справедлива в припущенні, що час, необхідний для перегляду околі вершини, пропорційно ступеня цієї вершини. Це має місце, наприклад, якщо граф заданий списками суміжності. Якщо ж граф заданий матрицею суміжності, то для перегляду околиці будь-якої вершини буде витрачатися час, пропорційне . В цьому випадку загальний час роботи алгоритму буде оцінюватися як . Найбільше значення величини при даному одно , Тобто має порядок . Таким чином, трудомісткість алгоритму пошуку в ширину при завданні графа списками суміжності не вище, ніж при завданні матрицею суміжності. В цілому ж перший спосіб завдання краще, так як дає виграш для графів з невеликим числом ребер.
В якості найпростішого прикладу застосування пошуку в ширину для графа розглянемо задачу виявлення компонент зв'язності. Припустимо, ми хочемо отримати відповідь у вигляді таблиці, в якій для кожної вершини вказано номер компоненти, якій належить ця вершина. Компоненти отримуватимуть номера в процесі обходу. Для вирішення цього завдання досить ввести змінну зі значенням, рівним поточному номеру компоненти, і кожен раз при відвідуванні нової вершини думати . значення спочатку встановлюється рівним і модифікується при кожному виклику процедури BFS.