Я розумію відмінності між DFS та BFS, але мені цікаво знати, коли практичніше використовувати один над іншим?
Чи може хтось навести приклади того, як DFS козирував BFS і навпаки?
Я розумію відмінності між DFS та BFS, але мені цікаво знати, коли практичніше використовувати один над іншим?
Чи може хтось навести приклади того, як DFS козирував BFS і навпаки?
Відповіді:
Це значною мірою залежить від структури дерева пошуку та кількості та місця розташування рішень (він же шукає елементи).
Якщо дерево дуже глибоке і рішення рідкісні, то перший пошук глибини (DFS) може зайняти надзвичайно багато часу, але BFS може бути швидшим.
Якщо дерево дуже широке, BFS може потребувати занадто багато пам'яті, тому це може бути абсолютно непрактично.
Якщо розчини часті, але розташовані глибоко в дереві, BFS може бути недоцільним.
Але це лише великі правила; вам, мабуть, доведеться експериментувати.
Швидкісні пошуки часто використовуються при моделюванні ігор (і ігрових ситуаціях у реальному світі). У типовій грі ви можете вибрати одну з кількох можливих дій. Кожен вибір призводить до подальших виборів, кожен з яких призводить до подальших виборів тощо у графік можливостей дерева, що постійно розширюється.
Наприклад, у таких іграх, як «Шахи», тик-так-носок, коли ви вирішуєте, який хід зробити, ви можете подумки уявити собі рух, то можливі відповіді опонента, потім ваші відповіді тощо. Ви можете вирішити, що робити, побачивши, який хід призводить до найкращого результату.
Лише деякі шляхи в ігровому дереві ведуть до вашої виграші. Деякі призводять до виграшу опонента, коли ви досягнете такого кінця, ви повинні створити резервну копію або відхилитись до попереднього вузла і спробувати інший шлях. Таким чином ви досліджуєте дерево, поки не знайдете шлях з успішним висновком. Тоді ви робите перший рух по цьому шляху.
Перший широтний пошук має цікаву властивість: він спочатку знаходить усі вершини, які знаходяться в одному краю від початкової точки, потім всі вершини, які знаходяться на двох ребрах, і так далі. Це корисно, якщо ви намагаєтесь знайти найкоротший шлях від початкової вершини до заданої вершини. Ви запускаєте BFS, і коли ви знайдете вказану вершину, ви знаєте, що шлях, який ви простежили до цього часу, - це найкоротший шлях до вузла. Якби був коротший шлях, BFS знайшов би його вже.
Пошук за шириною може використовуватися для пошуку сусідніх вузлів в однорангових мережах, таких як BitTorrent, GPS-системи для пошуку сусідніх локацій, сайтів соціальних мереж, щоб знайти людей на вказаній відстані та подібні речі.
Приємне пояснення з http://www.programmerinterview.com/index.php/data-structures/dfs-vs-bfs/
Приклад BFS
Ось приклад того, як виглядатиме BFS. Це щось на кшталт обходу дерева порядку рівня, де ми будемо використовувати QUEUE з ІТЕРАТИВНИМ підходом (здебільшого РЕКУРСІЯ закінчується DFS). Цифри представляють порядок доступу до вузлів у BFS:
Перший пошук на глибині ви починаєте з кореня та слідуєте за якоюсь можливою гілкою дерева до тих пір, поки не знайдеться той вузол, який ви шукаєте, або не потрапите на листовий вузол (вузол без дітей). Якщо ви потрапили на вузол листя, то продовжуйте пошук найближчого предка з недослідженими дітьми.
Приклад ДФС
Ось приклад того, як виглядатиме DFS. Я думаю, що обхід після замовлення у двійковому дереві спочатку почне працювати з рівня Leaf. Цифри представляють порядок доступу до вузлів у DFS:
Відмінності між DFS та BFS
Порівнюючи BFS та DFS, велика перевага DFS полягає в тому, що він має значно менші вимоги до пам'яті, ніж BFS, тому що не потрібно зберігати всі дочірні вказівники на кожному рівні. Залежно від даних і того, що ви шукаєте, DFS або BFS можуть бути вигідними.
Наприклад, зважаючи на генеалогічне дерево, якщо хтось шукав когось на дереві, хто ще живий, то можна було б припустити, що людина опиниться на дні дерева. Це означає, що BFS знадобиться дуже багато часу, щоб досягти цього останнього рівня. Однак DFS знайде мету швидше. Але, якби хтось шукав члена родини, який помер дуже давно, то ця людина була б ближче до верхівки дерева. Тоді BFS, як правило, буде швидшим, ніж DFS. Отже, переваги або варіюються залежно від даних та того, що ви шукаєте.
Ще один приклад - Facebook; Пропозиція про друзів друзів. Нам потрібні негайні друзі для пропозиції, де ми можемо використовувати BFS. Можемо знайти найкоротший шлях або виявити цикл (за допомогою рекурсії), який ми можемо використовувати DFS.
Перший пошук ширини - це найкращий підхід, коли глибина дерева може змінюватися, і вам потрібно лише шукати частину дерева для пошуку рішення. Наприклад, пошук найкоротшого шляху від початкового значення до кінцевого значення - хороше місце для використання BFS.
Перший пошук по глибині зазвичай використовується, коли потрібно шукати все дерево. Це легше реалізувати (використовуючи рекурсію), ніж BFS, і вимагає меншого стану: Хоча BFS вимагає зберігати весь 'кордон', DFS вимагає лише ви зберігати список батьківських вузлів поточного елемента.
DFS є більш економічним для простору, ніж BFS, але може зайняти зайві глибини.
Їх назви показові: якщо є велика ширина (тобто великий коефіцієнт розгалуження), але дуже обмежена глибина (наприклад, обмежена кількість "ходів"), то DFS може бути більш переважним перед BFS.
Слід зазначити, що існує маловідомий варіант, який поєднує в собі космічну ефективність DFS, але (кумулятивно) відвідування BFS за рівнем порядку - це ітеративний поглиблений пошук в глибині перших . Цей алгоритм переглядає деякі вузли, але він лише вносить постійний коефіцієнт асимптотичної різниці.
Коли ви підходите до цього питання як програміст, виділяється один фактор: якщо ви використовуєте рекурсію, то пошук на глибині першого простіший здійснити, тому що вам не потрібно підтримувати додаткову структуру даних, що містить вузли, які ще слід вивчити.
Ось перший поглиблений пошук неорієнтованого графіка, якщо ви зберігаєте "вже відвідану" інформацію у вузлах:
def dfs(origin): # DFS from origin:
origin.visited = True # Mark the origin as visited
for neighbor in origin.neighbors: # Loop over the neighbors
if not neighbor.visited: dfs(next) # Visit each neighbor if not already visited
Якщо зберігати "вже відвідану" інформацію в окремій структурі даних:
def dfs(node, visited): # DFS from origin, with already-visited set:
visited.add(node) # Mark the origin as visited
for neighbor in node.neighbors: # Loop over the neighbors
if not neighbor in visited: # If the neighbor hasn't been visited yet,
dfs(node, visited) # then visit the neighbor
dfs(origin, set())
Порівнюйте це з першим широким пошуком, де вам потрібно підтримувати окрему структуру даних для списку вузлів, які ще слід відвідати, незалежно від того.
Однією з важливих переваг BFS було б те, що він може бути використаний для пошуку найкоротшого шляху між будь-якими двома вузлами в невагомому графіку. Тоді як ми не можемо використовувати DFS для того ж .
Для BFS ми можемо розглянути приклад Facebook. Ми отримуємо пропозицію додати друзів із профілю FB з інших профілів друзів. Припустимо, A-> B, тоді як B-> E і B-> F, тому A отримає пропозицію для E і F. Вони повинні використовувати BFS для читання до другого рівня. DFS більше базується на сценаріях, де ми хочемо прогнозувати щось на основі даних, які ми маємо від джерела до місця призначення. Як уже говорилося про шахи чи судоку. Коли я щось інше тут, я вважаю, що DFS слід використовувати для найкоротшого шляху, оскільки DFS спочатку покриє весь шлях, тоді ми можемо визначити найкращий. Але оскільки BFS буде використовувати жадібний підхід, так, можливо, це виглядає як найкоротший шлях, але кінцевий результат може відрізнятися. Дайте мені знати, чи моє розуміння неправильне.
Деякі алгоритми залежать від конкретних властивостей DFS (або BFS) для роботи. Наприклад, алгоритм Hopcroft і Tarjan для пошуку 2-з'єднаних компонентів використовує той факт, що кожен вже відвіданий вузол, з яким стикається DFS, перебуває на шляху від кореня до досліджуваного на даний момент вузла.
Простими словами:
Алгоритм першого пошуку ширини (BFS) від назви "Ширина" виявляє всіх сусідів вузла через виворітні краї вузла, потім він виявляє невідомих сусідів згаданих раніше сусідів через їхні краї і так далі, поки все відвідуються вузли, доступні від оригінального джерела (ми можемо продовжувати та брати інше оригінальне джерело, якщо залишилися непроглянуті вузли тощо). Ось чому з його допомогою можна знайти найкоротший шлях (якщо такий є) від вузла (оригінального джерела) до іншого вузла, якщо вага країв рівномірний.
Алгоритм "Перший глибинний пошук" (DFS) від своєї назви "Глибина" виявляє невідомих сусідів останнього виявленого вузла x через його край. Якщо від вузла x немає невідомого сусіда, алгоритм здійснює відстеження для виявлення невідомих сусідів вузла (через його вихідні краї), з якого було виявлено вузол x тощо, до тих пір, поки не будуть відвідані всі вузли, доступні від початкового джерела (ми можемо продовжувати та брати інше оригінальне джерело, якщо залишилися непроглянуті вузли тощо).
І BFS, і DFS можуть бути неповними. Наприклад, якщо коефіцієнт розгалуження вузла нескінченний або дуже великий для підтримки ресурсів (пам'яті) (наприклад, при зберіганні вузлів, які будуть виявлені далі), BFS не є повним, навіть якщо шуканий ключ може знаходитись на відстані з кількох країв від початкового джерела. Цей нескінченний розгалужуючий фактор може бути зумовлений нескінченним вибором (сусідні вузли) з даного вузла для виявлення. Якщо глибина нескінченна або дуже велика, щоб ресурси (пам'ять) підтримували (наприклад, при зберіганні вузлів, які будуть виявлені далі), то DFS не є повним, навіть якщо шуканий ключ може бути третім сусідом початкового джерела. Ця нескінченна глибина може бути через ситуацію, коли для кожного вузла алгоритм виявляє, принаймні, новий вибір (сусідній вузол), який раніше не відвідували.
Тому ми можемо зробити висновок, коли використовувати BFS та DFS. Припустимо, ми маємо справу з керованим обмеженим фактором розгалуження та керованою обмеженою глибиною. Якщо шуканий вузол неглибокий, тобто доступний після деяких ребер від початкового джерела, тоді краще використовувати BFS. З іншого боку, якщо шуканий вузол є глибоким, тобто доступним після великої кількості ребер від початкового джерела, тоді краще використовувати DFS.
Наприклад, у соціальній мережі, якщо ми хочемо шукати людей, які мають подібні інтереси конкретної людини, ми можемо застосувати BFS від цієї людини як джерельне джерело, тому що в основному ці люди будуть його прямими друзями або друзями друзів, тобто одним або два ребра далеко. З іншого боку, якщо ми хочемо шукати людей, які мають абсолютно різні інтереси конкретної людини, ми можемо застосувати DFS від цієї людини як джерельне джерело, тому що в основному ці люди будуть дуже далекі від нього, тобто товариш друга друга .... тобто занадто багато ребер далеко.
Застосування BFS та DFS можуть відрізнятися також через механізм пошуку в кожному з них. Наприклад, ми можемо використовувати або BFS (припускаючи, що коефіцієнт розгалуження є керованим), або DFS (припускаючи, що глибина керована), коли ми просто хочемо перевірити доступність від одного вузла до іншого, не маючи інформації, де цей вузол може бути. Також обидва вони можуть вирішувати такі самі завдання, як топологічне сортування графіка (якщо він є). BFS можна використовувати для пошуку найкоротшого шляху з одиничними ваговими краями від вузла (оригінального джерела) до іншого. В той час, як DFS може використовуватися для вичерпання всіх варіантів через його характер заглиблення, як виявлення найдовшого шляху між двома вузлами в ациклічному графіку. Також DFS можна використовувати для виявлення циклу в графіку.
Зрештою, якщо ми маємо нескінченну глибину і нескінченний розгалужуючий фактор, ми можемо використовувати Ітеративний поглиблений пошук (IDS).
За властивостями DFS та BFS. Наприклад, коли ми хочемо знайти найкоротший шлях. ми зазвичай використовуємо bfs, це може гарантувати "найкоротший". але лише dfs може гарантувати, що ми можемо прийти з цієї точки, можемо досягти цієї точки, не може гарантувати "найкоротшого".
Я думаю, це залежить від того, з якими проблемами ви стикаєтесь.
Оскільки пошуки заглиблень-перших використовують стек під час обробки вузлів, зворотний трекінг надається за допомогою DFS. Оскільки Breadth-First Searches використовує чергу, а не стек, щоб відслідковувати, які вузли обробляються, зворотний трекінг не надається з BFS.
Це хороший приклад для того, щоб продемонструвати, що BFS є кращим, ніж DFS в певному випадку. https://leetcode.com/problems/01-matrix/
При правильній реалізації обидва рішення повинні відвідувати комірки, що мають більшу відстань, ніж поточна комірка +1. Але DFS є неефективним і неодноразово відвідував одну і ту ж клітинку, що призводить до складності O (n * n).
Наприклад,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,
Це залежить від ситуації, в якій він використовується. Щоразу, коли у нас виникає проблема проходження графа, ми робимо це з певною метою. Коли виникає проблема знайти найкоротший шлях у невагомому графіку або знайти, чи графік двосторонній, ми можемо використовувати BFS. Для проблем виявлення циклу або будь-якої логіки, що вимагає зворотного відстеження, ми можемо використовувати DFS.