Skip to content
Home » Dfs Bfs 알고리즘 | [자료구조 알고리즘] Graph 검색 Dfs, Bfs 구현 In Java 30 개의 정답

Dfs Bfs 알고리즘 | [자료구조 알고리즘] Graph 검색 Dfs, Bfs 구현 In Java 30 개의 정답

당신은 주제를 찾고 있습니까 “dfs bfs 알고리즘 – [자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java“? 다음 카테고리의 웹사이트 https://ro.taphoamini.com 에서 귀하의 모든 질문에 답변해 드립니다: https://ro.taphoamini.com/wiki. 바로 아래에서 답을 찾을 수 있습니다. 작성자 엔지니어대한민국 이(가) 작성한 기사에는 조회수 90,188회 및 좋아요 1,805개 개의 좋아요가 있습니다.

dfs bfs 알고리즘 주제에 대한 동영상 보기

여기에서 이 주제에 대한 비디오를 시청하십시오. 주의 깊게 살펴보고 읽고 있는 내용에 대한 피드백을 제공하세요!

d여기에서 [자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java – dfs bfs 알고리즘 주제에 대한 세부정보를 참조하세요

[선행학습자료]Linked List 개념 https://youtu.be/DzGnME1jIwY
단방향/양방향 Linked List 개념 https://youtu.be/G4IIDyfoHeY
단방향 Linked List 구현 in Java https://youtu.be/C1SDkdPvQPA
Stack 구현하기 in Java https://youtu.be/whVUYv0Leg0
Queue 구현하기 in Java https://youtu.be/W3jNbNGyjMs
Tree의 종류 https://youtu.be/LnxEBW29DOw
Binary Tree의 3가지 순회방법 구현하기 https://youtu.be/QN1rZYX6QaA
그래프(Graph)에 대해서 https://youtu.be/fVcKN42YXXI

dfs bfs 알고리즘 주제에 대한 자세한 내용은 여기를 참조하세요.

[알고리즘] 깊이 우선 탐색(DFS) 과 너비 우선 탐색(BFS)

[알고리즘] 깊이 우선 탐색(DFS) 과 너비 우선 탐색(BFS). ※ 그래프의 개념. – 정점과 간선으로 이루어진 자료구조의 일종. G = (V, E).

+ 여기에 자세히 보기

Source: yunyoung1819.tistory.com

Date Published: 2/22/2021

View: 6745

(Algorithm) 그래프 탐색 – DFS(깊이 우선)와 BFS(너비 우선)

BFS도 위의 사진과 같이 동작하죠? DFS가 스택의 특징(LIFO)에 의해 자동으로 깊이 우선 탐색이 되는 반면, BFS는 큐의 특징(FIFO) 때문에 자동으로 …

+ 자세한 내용은 여기를 클릭하십시오

Source: www.zerocho.com

Date Published: 7/24/2021

View: 9003

[알고리즘] 깊이 우선 탐색(DFS) 과 너비 우선 … – 튜나 개발일기

해당 분기를 완벽하게 탐색하는 방식을 말합니다. … 그 갈림길부터 다시 다른 방향으로 탐색을 진행하는 것이 깊이 우선 탐색 방식이라고 할 수 있습니다.

+ 여기에 자세히 보기

Source: devuna.tistory.com

Date Published: 12/13/2021

View: 8449

[Algorithm] DFS, BFS 구현 (Python) – 도각도각 Dev Day

그놈의 DFS, BFS. 방학때만 문제를 푸니 매번 까먹는다. 개념은 알고 있는데 오랜만에 구현하려 하면 매번 멈칫한다. 이번이 마지막이라고 생각하고 …

+ 자세한 내용은 여기를 클릭하십시오

Source: devyuseon.github.io

Date Published: 8/10/2022

View: 3724

[알고리즘] 깊이 우선 탐색(DFS)과 너비 우선 탐색(BFS)

[알고리즘] 깊이 우선 탐색(DFS)과 너비 우선 탐색(BFS). 2021. 5. 30. 19:42. 그래프 탐색 알고리즘. 이번에는 그래프를 탐색하는 알고리즘에 대하여 다루어 볼 …

+ 여기에 보기

Source: currygamedev.tistory.com

Date Published: 8/7/2021

View: 205

[자료구조] 탐색 : BFS / DFS – 하나몬

자료구조와 알고리즘 … 그중에서 가장 대표적인 두 가지 방법, DFS 와 BFS 를 알아보자. … ❗️BFS (Breadth-First Search) – 너비 우선 탐색 …

+ 여기에 보기

Source: hanamon.kr

Date Published: 6/23/2022

View: 6182

깊이 우선 탐색(DFS)과 너비 우선 검색(BFS) – Techie Delight

이 게시물은 트리 또는 그래프 데이터 구조를 탐색/검색하는 데 사용되는 깊이 우선 검색(DFS)과 너비 우선 검색(BFS) 알고리즘의 차이점을 다룹니다.

+ 여기에 보기

Source: www.techiedelight.com

Date Published: 7/22/2022

View: 5516

DFS & BFS 이해하기 및 구현(C++) – 정리

BFS의 애니메이션 [출처 : 위키피디아(너비 우선 탐색)]. – 그래프에서 가장 가까운 노드부터 우선적으로 탐색하는 알고리즘. 큐 자료구조를 이용한다.

+ 더 읽기

Source: better-tomorrow.tistory.com

Date Published: 11/22/2022

View: 6712

주제와 관련된 이미지 dfs bfs 알고리즘

주제와 관련된 더 많은 사진을 참조하십시오 [자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java. 댓글에서 더 많은 관련 이미지를 보거나 필요한 경우 더 많은 관련 기사를 볼 수 있습니다.

[자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java
[자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java

주제에 대한 기사 평가 dfs bfs 알고리즘

  • Author: 엔지니어대한민국
  • Views: 조회수 90,188회
  • Likes: 좋아요 1,805개
  • Date Published: 2017. 12. 29.
  • Video Url link: https://www.youtube.com/watch?v=_hxFgg7TLZQ

[알고리즘] 깊이 우선 탐색(DFS) 과 너비 우선 탐색(BFS)

반응형

[알고리즘] 깊이 우선 탐색(DFS) 과 너비 우선 탐색(BFS)

※ 그래프의 개념

– 정점과 간선으로 이루어진 자료구조의 일종. G = (V, E)

※ 그래프 탐색

– 하나의 정점으로부터 시작하여 차례대로 모든 정점들을 한 번씩 방문하는 것

Ex) 특정 도시에서 다른 도시로 갈 수 있는지 없는지, 전자 회로에서 특정 단자와 단자가 서로 연결되어 있는지

※ 깊이 우선 탐색 (DFS, Depth-First Search)의 개념

– 루트 노드(혹은 다른 임의의 노드)에서 시작해서 다음 분기(branch)로 넘어가기 전에 해당 분기를 완벽하게 탐색하는 방법

1. 미로를 탐색할 때 한 방향으로 갈 수 있을 때까지 계속 가다가 더 이상 갈 수 없게 되면 다시 가장 가까운 갈림길로 돌아와서 이곳으로부터 다른 방향으로 다시 탐색을 진행하는 방법과 유사함

2. 즉 넓게(wide) 탐색하기 전에 깊게(deep) 탐색함

3. 모든 노드를 방문하고자 하는 경우에 이 방법을 선택함

4. 깊이 우선 탐색(DFS)이 너비 우선 탐색(BFS)보다 좀 더 간단함

5. 검색 속도 자체는 너비 우선 탐색(BFS)에 비해서 느림

※ 깊이 우선 탐색(DFS)의 특징

– 자기 자신을 호출하는 순환 알고리즘의 형태를 지님

– 이 알고리즘을 구현할 때 가장 큰 차이점은 그래프 탐색의 경우 어떤 노드를 방문했었는지 여부를 반드시 검사해야한다는 것 (이를 검사하지 않을 경우 무한루프에 빠질 위험이 있음)

※ 깊이 우선 탐색(DFS)의 과정

※ 깊이 우선 탐색(DFS)의 시간 복잡도

– DFS는 그래프(정점의 수 : N, 간선의 수: E)의 모든 간선을 조회함

* 인접 리스트로 표현된 그래프 : O(N+E)

* 인접 행렬로 표현된 그래프 : O(N^2)

※ 너비 우선 탐색 (BFS, Breadth-First Search)

– 루트 노드(혹은 다른 임의의 노드)에서 시작해서 인접한 노드를 먼저 탐색하는 방법

1. 시작 정점으로부터 가까운 정점을 먼저 방문하고 멀리 떨어져 있는 정점을 나중에 방문하는 순회 방법

2. 즉 깊게(deep) 탐색하기 전에 넓게(wide) 탐색하는 것

3. 두 노드 사이의 최단 경로 혹은 임의의 경로를 찾고 싶을 때 이 방법을 선택함

ex) 지구 상에 존재하는 모든 친구 관계를 그래프로 표현한 후 Ash 와 Vanessa 사이에 존재하는 경로를 찾는 경우

* 깊이 우선 탐색의 경우 – 모든 친구 관계를 다 살펴봐야할지도 모름

* 너비 우선 탐색의 경우 – Ash와 가까운 관계부터 탐색

※ 너비 우선 탐색(BFS)의 특징

– BFS 는 재귀적으로 동작하지 않는다.

– 이 알고리즘을 구현할 때 가장 큰 차이점은 그래프 탐색의 경우 어떤 노드를 방문했었는지 여부를 반드시 검사해야한다는 것이다 이를 검사하지 않을 경우 무한 루프에 빠질 위험이 있다.

– BFS 는 방문한 노드들을 차례로 저장한 후 꺼낼 수 있는 자료 구조인 큐(Queue)를 사용함

– 즉 선입선출(FIFO) 원칙으로 탐색

※ 너비 우선 탐색(BFS)이 과정

– 깊이가 1인 모든 노드를 방문하고 나서 그 다음에는 깊이가 2인 모든 노드를, 그 다음에는 깊이가 3인 모든 노드를 방문하는 식으로 계속 방문하다가 더 이상 방문할 곳이 없으면 탐색을 마친다.

※ DFS 와 BFS 의 차이

[그림 출처 : https://namu.wiki/w/BFS] [출처]

https://gmlwjd9405.github.io/2018/08/14/algorithm-dfs.html

https://namu.wiki/w/BFS

반응형

[알고리즘] 깊이 우선 탐색(DFS) 과 너비 우선 탐색(BFS)

728×90

[알고리즘] 깊이 우선 탐색(DFS)과 너비 우선 탐색(BFS)

그래프를 탐색하는 방법에는 크게 깊이 우선 탐색(DFS)과 너비 우선 탐색(BFS)이 있습니다.

📌여기서 그래프란, 정점(node)과 그 정점을 연결하는 간선(edge)으로 이루어진 자료구조의 일종을 말하며,

그래프를 탐색한다는 것은 하나의 정점으로부터 시작하여 차례대로 모든 정점들을 한 번씩 방문하는 것을 말합니다.

그래프와 트리의 차이가 궁금하다면? 👇🏻

더보기 그래프와 트리의 차이 큰 특징만 말하자면, 그래프 중에서 방향성이 있는 비순환 그래프를 트리라고 말합니다.

1. 깊이 우선 탐색 (DFS, Depth-First Search)

: 최대한 깊이 내려간 뒤, 더이상 깊이 갈 곳이 없을 경우 옆으로 이동

출처 https://developer-mac.tistory.com/64

💡 깊이 우선 탐색의 개념

루트 노드(혹은 다른 임의의 노드)에서 시작해서 다음 분기(branch)로 넘어가기 전에

해당 분기를 완벽하게 탐색하는 방식을 말합니다.

예를 들어, 미로찾기를 할 때 최대한 한 방향으로 갈 수 있을 때까지 쭉 가다가

더 이상 갈 수 없게 되면 다시 가장 가까운 갈림길로 돌아와서

그 갈림길부터 다시 다른 방향으로 탐색을 진행하는 것이 깊이 우선 탐색 방식이라고 할 수 있습니다.

1. 모든 노드를 방문하고자 하는 경우에 이 방법을 선택함

2. 깊이 우선 탐색(DFS)이 너비 우선 탐색(BFS)보다 좀 더 간단함

3. 검색 속도 자체는 너비 우선 탐색(BFS)에 비해서 느림

2. 너비 우선 탐색 (BFS, Breadth-First Search)

: 최대한 넓게 이동한 다음, 더 이상 갈 수 없을 때 아래로 이동

출처 https://developer-mac.tistory.com/64

💡 너비 우선 탐색의 개념

루트 노드(혹은 다른 임의의 노드)에서 시작해서 인접한 노드를 먼저 탐색하는 방법으로,

시작 정점으로부터 가까운 정점을 먼저 방문하고 멀리 떨어져 있는 정점을 나중에 방문하는 순회 방법입니다.

주로 두 노드 사이의 최단 경로를 찾고 싶을 때 이 방법을 선택합니다.

ex) 지구 상에 존재하는 모든 친구 관계를 그래프로 표현한 후 Sam과 Eddie사이에 존재하는 경로를 찾는 경우

* 깊이 우선 탐색의 경우 – 모든 친구 관계를 다 살펴봐야 할지도 모름

* 너비 우선 탐색의 경우 – Sam과 가까운 관계부터 탐색

3. 깊이 우선 탐색(DFS) 과 너비 우선 탐색(BFS) 비교

출처 https://namu.wiki/w/BFS

DFS(깊이우선탐색) BFS(너비우선탐색) 현재 정점에서 갈 수 있는 점들까지 들어가면서 탐색 현재 정점에 연결된 가까운 점들부터 탐색 스택 또는 재귀함수로 구현 큐를 이용해서 구현

💡DFS와 BFS의 시간복잡도

두 방식 모두 조건 내의 모든 노드를 검색한다는 점에서 시간 복잡도는 동일합니다.

DFS와 BFS 둘 다 다음 노드가 방문하였는지를 확인하는 시간과 각 노드를 방문하는 시간을 합하면 됩니다.

N은 노드, E는 간선일 때

인접 리스트 : O(N+E)

인접 행렬 : O(N²)

일반적으로 E(간선)의 크기가 N²에 비해 상대적으로 적기 때문에

인접 리스트 방식이 효율적임

더보기 인접 행렬의 경우, 정점의 개수 N만큼 도는 2중 for문을 돌려 두 정점 간에 간선이 존재하는지를 확인해야 합니다. 이때 N의 제곱만큼 돌게 되므로 O(N²)의 시간 복잡도가 됩니다. 인접 리스트로 구현된 경우, 존재하는 간선의 정보만 저장되어 있으므로 인접 행렬에서 사용한 2중 for문이 필요하지 않습니다. 다음 노드가 방문하였는지 확인할 때 간선의 개수인 E의 두 배만큼의 시간이 걸리고(1번에서 2, 6번이 방문하였는지를 확인하고 2번에서 1,3,6번을 방문하였는지 확인합니다. 이때 1번과 2번의 간선 하나에 두 번의 확인을 하기 때문에 두배만큼 시간이 걸립니다.) 각 노드를 방문할 때 정점의 개수인 N만큼 걸립니다. 따라서 O(N+2*E) = O(N+E)가 됩니다. (시간 복잡도에서 계수는 삭제합니다.)

3. 깊이 우선 탐색(DFS)과 너비 우선 탐색(BFS) 활용한 문제 유형/응용

DFS, BFS은 특징에 따라 사용에 더 적합한 문제 유형들이 있습니다.

1) 그래프의 모든 정점을 방문하는 것이 주요한 문제

단순히 모든 정점을 방문하는 것이 중요한 문제의 경우 DFS, BFS 두 가지 방법 중 어느 것을 사용하셔도 상관없습니다.

둘 중 편한 것을 사용하시면 됩니다.

2) 경로의 특징을 저장해둬야 하는 문제

예를 들면 각 정점에 숫자가 적혀있고 a부터 b까지 가는 경로를 구하는데 경로에 같은 숫자가 있으면 안 된다는 문제 등, 각각의 경로마다 특징을 저장해둬야 할 때는 DFS를 사용합니다. (BFS는 경로의 특징을 가지지 못합니다)

3) 최단거리 구해야 하는 문제

미로 찾기 등 최단거리를 구해야 할 경우, BFS가 유리합니다.

왜냐하면 깊이 우선 탐색으로 경로를 검색할 경우 처음으로 발견되는 해답이 최단거리가 아닐 수 있지만,

너비 우선 탐색으로 현재 노드에서 가까운 곳부터 찾기 때문에경로를 탐색 시 먼저 찾아지는 해답이 곧 최단거리기 때문입니다.

이밖에도

– 검색 대상 그래프가 정말 크다면 DFS를 고려

– 검색대상의 규모가 크지 않고, 검색 시작 지점으로부터 원하는 대상이 별로 멀지 않다면 BFS

4. DFS와 BFS을 사용한 JAVA코드

DFS 알고리즘에 경우 두 가지 방법으로 풀 수 있는데,

첫 번째로 스택을 이용하는 것

두 번째로 재귀함수를 이용하는 것인데, 재귀 함수를 이용하는 것이 가장 보편적이고 짧은 코드를 작성할 수 있다.

재귀 함수란 함수 내부에서 함수가 자기 자신을 또다시 호출하는 함수를 의미합니다.

이러한 재귀 호출은 자기가 자신을 계속해서 호출하므로, 끝없이 반복되기 때문에

함수 내에 재귀 호출을 중단하도록 조건이 변경될 명령문을 반드시 포함해야 합니다.

/* 인접 리스트 이용 */ class Graph { private int V; private LinkedList adj[]; Graph(int v) { V = v; adj = new LinkedList[v]; // 인접 리스트 초기화 for (int i=0; i it = adj[v].listIterator(); while (it.hasNext()) { int n = it.next(); // 방문하지 않은 노드면 해당 노드를 시작 노드로 다시 DFSUtil 호출 if (!visited[n]) DFSUtil(n, visited); } } }

BFS 알고리즘은 큐(Queue)를 사용해서 문제를 해결하면 됩니다.

class Graph { private int V; private LinkedList adj[]; Graph(int v) { V = v; adj = new LinkedList[v]; for (int i=0; i queue = new LinkedList(); //연결리스트 생성 visited[s] = true; queue.add(s); while (queue.size() != 0) { // 방문한 노드를 큐에서 추출(dequeue)하고 값을 출력 s = queue.poll(); System.out.print(s + ” “); // 방문한 노드와 인접한 모든 노드를 가져온다. Iterator i = adj[s].listIterator(); while (i.hasNext()) { int n = i.next(); // 방문하지 않은 노드면 방문한 것으로 표시하고 큐에 삽입(enqueue) if (!visited[n]) { visited[n] = true; queue.add(n); } } } } }

참고

https://gmlwjd9405.github.io/2018/08/14/algorithm-dfs.html

https://developer-mac.tistory.com/64

728×90

[Algorithm] DFS, BFS 구현 (Python)

들어가기 전에

그놈의 DFS, BFS. 방학때만 문제를 푸니 매번 까먹는다. 개념은 알고 있는데 오랜만에 구현하려 하면 매번 멈칫한다. 이번이 마지막이라고 생각하고 다시 공부했다. 설명보단 구현에 더 집중했다.

📢 이 포스팅은 DFS, BFS의 개념은 알고 있다는 전제 하에 작성되었습니다.

오류가 있다면 댓글로 알려주시면 감사하겠습니다 🙇‍♀️

📍 DFS , 깊이 우선 탐색

간단 개념

출처 https://developer-mac.tistory.com/64 트리나 그래프에서 한 루트로 탐색하다가 특정 상황에서 최대한 깊숙히 들어가서 확인한 뒤 다시 돌아가 다른 루트로 탐색하는 방식. 재귀호출, 스택 두가지 방법이 모두 가능하다.

구현하기 – 스택

스택을 하나 만든다. 빈 스택에 시작할 노드를 넣는다. 스택에서 노드를 하나 꺼내고(pop), 출력한다. 그리고 꺼낸 노드의 자식 노드들을 다 넣는다.

(❗이때 한 번 스택에 담은 노드는 다시 넣지 않음) 반복한다.

이때 기억해야 할 점은 스택이기 때문에 마지막으로 넣은 노드부터 탐색된다.

구현하기 – 재귀

일반적으로 코드가 깔끔하기 때문에 선호하는 방법이다. 노드에 방문하면 그 노드를 출력하고 그것의 자식들을 재귀 호출한다.

노드의 자식의 자식의 자식..을 계속 호출하고 들어간 다음 더이상 자식이 없으면 하나 올라와서 올라온 지점의 다른 경로로부터 탐색을 다시 시작한다.

1 2 3 4 5 6 7 1 / | \ 2 5 9 | /\ | 3 6 8 10 / | 4 7

이런 그래프가 있으면 재귀 호출은

1 2 3 4 5 6 7 8 9 10 11 dfs(1) dfs(2) dfs(5) dfs(3) dfs(4) dfs(5) dfs(6) dfs(7) dfs(8) dfs(9) dfs(10)

이렇게 된다.

📃 소스코드

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 “”” 1 / | \ 2 5 9 | /\ | 3 6 8 10 / | 4 7 “”” graph = { 1 : [ 2 , 5 , 9 ], 2 : [ 3 ], 3 : [ 4 ], 4 : [], 5 : [ 6 , 8 ], 6 : [ 7 ], 7 : [], 8 : [], 9 : [ 10 ], 10 : [] } def recursive_dfs ( v , visited = []): visited . append ( v ) # 시작 정점 방문 for w in graph [ v ]: if not w in visited : # 방문 하지 않았으면 visited = recursive_dfs ( w , visited ) return visited def iterative_dfs ( start_v ): visited = [] stack = [ start_v ] while stack : v = stack . pop () if v not in visited : visited . append ( v ) for w in graph [ v ]: stack . append ( w ) return visited print ( “recursive_dfs: ” , recursive_dfs ( 1 )) print ( “iterative_dfs: ” , iterative_dfs ( 1 )) # 스택은 마지막에 스택에 담은 정점부터 꺼내져 방문되기 때문에 # 재귀 방식과 결과가 다름. “”” recursive_dfs: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] iterative_dfs: [1, 9, 10, 5, 8, 6, 7, 2, 3, 4] “””

📍 BFS , 너비 우선 탐색

간단 개념

출처 https://developer-mac.tistory.com/64

시작 노드로부터 가까운 지점을 먼저 방문하고 먼 지점은 나중에 방문한다.

무한한 길이를 가지는 경로가 존재할 때 탐색 목표가 다른 경로에 있을 경우 DFS는 영원히 종료하지 못하지만 BFS는 모든 경로를 거의 동시에 진행하기 때문에 탐색이 가능하다 큐로 구현이 가능하며 재귀는 불가능하다.

구현하기 – 큐

큐는 선입 선출(FIFO) 이다.

빈 큐를 만들고 시작 노드를 넣는다. 큐에서 노드를 꺼내고(pop), 출력한다. 꺼낸 노드의 자식들을 큐에 추가한다

(❗이때 한 번 큐에 담은 노드는 다시 넣지 않음) 반복한다.

📃 소스코드

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 “”” 1 / | \ 2 3 4 | /\ | 5 6 7 8 / | 9 10 “”” graph = { 1 : [ 2 , 3 , 4 ], 2 : [ 5 ], 3 : [ 6 , 7 ], 4 : [ 8 ], 5 : [ 9 ], 6 : [ 10 ], 7 : [], 8 : [], 9 : [], 10 : [] } def bfs ( start_v ): visited = [ start_v ] queue = [ start_v ] while queue : v = queue . pop ( 0 ) for w in graph [ v ]: if w not in visited : visited . append ( w ) queue . append ( w ) return visited print ( “bfs: ” , bfs ( 1 )) “”” bfs: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] “””

리스트를 이용했는데, 사실 pop(0) 은 시간복잡도가 O(n)으로, 좋지 않다. 리스트보다는 Deque을 사용하는것이 좋다.

👍 Deque를 사용한 소스코드

1 2 3 4 5 6 7 8 9 10 11 12 13 from collections import deque def bfs ( start_v ): visited = [ start_v ] deq = deque () deq . append ( start_v ) while deq : v = deq . popleft () for w in graph [ v ]: if w not in visited : visited . append ( w ) deq . append ( w ) return visited

🌈 추천하는 설명 영상

[알고리즘] 깊이 우선 탐색(DFS)과 너비 우선 탐색(BFS)

🔻그래프 탐색 알고리즘

이번에는 그래프를 탐색하는 알고리즘에 대하여 다루어 볼 것이다. 그래프 탐색 알고리즘이란 한 정점에서 시작하여 차례대로 그래프에 있는 모든 정점들은 한번씩 방문하는 알고리즘을 뜻한다. 많은 그래프에 대한 문제를 해결하기 위해서 그래프 탐색 알고리즘을 알아야하는데 예를들어 다음과 같은 상황에서 탐색이 필요하다.

한 정점과 다른 정점의 경로를 구할 때

그래프가 연결되어 있는지 확인할 때

신장 트리(Spanning Tree)를 찾을 때

그래프를 탐색하는 알고리즘은 굉장히 중요한 알고리즘이라고 들었다. 알고리즘 사이트인 프로그래머스에서 보았을 때도 그래프 탐색이 출제 빈도수도 높고 정답률이 낮게 나온다.

대표적으로 깊이 우선 탐색(Depth-First Search)과 너비 우선 탐색(Breadth-First Search)이 존재한다. 줄여서 각각 DFS와 BFS라고 많이 부른다.

그래프(Graph)라는 자료구조에 대한 자세한 설명은 아래의 포스팅에 잘 정리해 두었다.

🔻 깊이 우선 탐색(DFS)과 너비 우선 탐색(BFS )

그래프를 탐색하는 알고리즘은 깊이 우선 탐색과 너비 우선 탐색으로 나누어 진다. 두 알고리즘을 알아보자.

💡 깊이 우선 탐색(DFS – Depth-First Search)

오른쪽 그림을 보면 이해가 쉬울 것이다. DFS의 기본 원리는 갈 수 있는 만큼 최대한 깊이가고, 더이상 갈곳이 없으면 이전 정점으로 돌아간다는 것이다. 쉽게 말해서 그냥 세로로 한줄씩 읽어 내려간다고 생각하면 된다. stack과 재귀함수를 사용하여 구현한다.

DFS의 장/단점

장점 현 경로상의 노드들만 기억하면 되므로 저장공간 수요가 비교적 적다. 목표 노드가 깊은 단계에 있을 경우 해를 빨리 구할 수 있다.

단점 해가 없는 경로가 깊을 경우 탐색시간이 오래 걸릴 수 있다. 얻어진 해가 최단 경로가 된다는 보장이 없다. 깊이가 무한히 깊어지면 스택오버플로우가 날 위험이 있다. (깊이 제한을 두는 방법으로 해결가능)

DFS의 구현

그래프를 인접 행렬(adjacency matrix)로 구현했는지, 인접 리스트(adjacency list)로 구현했는 지에 따라 구현방법이 달라진다.

인접 행렬로 구현했을 경우

vector> adjacent; //인접 행렬로 구현된 그래프 들어감 vector visited; // 그래프의 정점만큼의 크기로 생성 void DFS(int now) { visited[now] = true; int N = adjacent.size(); for (int i = 0; i < N; i++) { if (adjacent[now][i] == 0) continue; if (!visited[i]) DFS(i); } } [시간복잡도] DFS 하나당 N번의 loop를 돌게 되므로 O(n)의 시간복잡도를 가진다. 그런데 N개의 정점을 모두 방문 해야하므로 n*O(n)이므로 O(n^2)의 시간복잡도를 가지게 된다. 인접 리스트로 구현했을 경우 vector> adjacent; //인접 리스트로 구현된 그래프 들어감 vector visited; // 그래프의 정점만큼의 크기로 생성 void DFS(int now) { visited[now] = true; for (int i = 0; i < adjacent[now].size(); i++) { int next = adjacent[now][i]; if (!visited[next]) DFS(next); } } [시간복잡도] DFS가 총 N번 호출되긴 하지만 인접행렬과 달리 인접 리스트로 구현하게 되면 DFS하나당 각 정점에 연결되어 있는 간선의 개수만큼 탐색을 하게 되므로 예측이 불가능 하다. 하지만 DFS가 다 끝난 후를 생각하면, 모든 정점을 한번씩 다 방문하고, 모든 간선을 한번씩 모두 검사했다고 할 수 있으므로 O(n+e)의 시간이 걸렸다고 할 수 있다. 따라서 시간복잡도는 O(n+e)이다. 💡 너비 우선 탐색(BFS - Breadth-First Search) 마찬가지로 오른쪽 그림을 보면 이해가 쉬울 것이다. BFS의 기본 원리는 queue를 이용하여 지금 위치에 갈 수 있는 정점을 모두 queue에 넣는 것이다. 쉽게 말하면 DFS와는 정반대로 가로로 한줄씩 읽는다고 생각하면 된다. queue를 사용하여 구현한다. BFS의 장/단점 장점 너비를 우선으로 탐색하므로 답이 되는 경로가 여러 개인 경우에도 최단경로를 얻을 수 있다. 경로가 무한히 깊어져도 최단경로를 반드시 찾을 수 잇다. 노드 수가 적고 깊이가 얕은 해가 존재할 때 유리하다. 단점 DFS와 달리 큐를 이용하여 다음에 탐색할 정점들을 저장하므로 더 큰 저장공간이 필요하다. BFS의 구현 그래프를 인접 행렬(adjacency matrix)로 구현했는지, 인접 리스트(adjacency list)로 구현했는 지에 따라 구현방법이 달라진다. 인접 행렬로 구현했을 경우 vector> adjacent; //인접 행렬로 구현된 그래프 들어감 vector discovered; // 그래프의 정점만큼의 크기로 생성 void BFS(int now) { queue q; int N = adjacent.size(); q.push(now); discovered[now] == true; while(!q.empty()) { now = q.front(); q.pop(); for (int i = 0; i < N; i++) { if (adjacent[now][i] == 0) continue; if (!discovered[i]) { q.push(i); discovered[i] = true; } } } } [시간복잡도] 정점 한개당 N번의 for loop를 돌기 때문에 O(n)의 시간이 걸리는데 이 for loop는 큐에 아무것도 없을 때까지 즉, 모든 정점을 방문할 때까지 실행되므로 n번 반복 실행된다. 따라서 시간복잡도는 O(n^2)이다. 인접 리스트로 구현했을 경우 vector> adjacent; //인접 리스트로 구현된 그래프 들어감 vector discovered; // 그래프의 정점만큼의 크기로 생성 void BFS(int now) { queue q; q.push(now); discovered[now] == true; while(!q.empty()) { now = q.front(); q.pop(); for (int i = 0; i < adjacent[now].size(); i++) { int next = adjacent[now][i]; if (!discovered[next]) { q.push(next); discovered[next] = true; } } } } [시간복잡도] 아까 리스트로 구현된 DFS와 비슷한 논리로 시간복잡도를 구할 수 있다. BFS가 다 끝난 후를 생각해보면, 모든 간선에 대해서 한번씩 검사를 할 것이고, 각 정점을 한번씩 모두 방문하기 때문에 O(n+e)만큼의 시간복잡도를 가질 것이다. 🔻마치며 이렇게 그래프를 탐색하는 알고리즘인 DFS와 BFS에 대해서 알아보았다. 두 알고리즘 모두 인접행렬을 사용하여 만든 그래프의 경우에는 O(n^2)의 시간복잡도를 가지고 인접리스트를 사용하여 만든 그래프의 경우에는 O(n+e)의 시간복잡도를 가진다. 두 알고리즘의 시간복잡도는 같으므로 각각의 장단점을 알고 상황에 따라 적절한 알고리즘을 선택하는 것이 중요해 보인다. DFS, BFS알고리즘은 굉장히 중요한 알고리즘이란 것을 인지하고 계속해서 반복학습 해나가야 한다고 생각한다.

DFS & BFS 이해하기 및 구현(C++)

DFS : Depth First Search(깊이 우선 탐색)

– 그래프 전체를 탐색하는 방법 중 하나. (완벽히 탐색)

– 시작점부터 다음 branch로 넘어가기 전에 해당 branch를 완벽하게 탐색하고 넘어가는 방법.

– [재귀함수]나 [스택]으로 구현.

1. 탐색 시작 노드를 스택에 삽입하고 방문처리

2. 스택의 최상단 노드에 방문하지 않은 인접한 노드가 하나라도 있으면 그 노드를 스택에 넣고 방문처리

방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼낸다.

3. 2번의 과정을 수행할 수 없을 때까지 반복

– 노드 방문 시 방문(visited) 여부를 반드시 검사해야 한다. 아니면 무한 루프에 빠질 수 있음.

– 탐색 과정이 시작 노드에서 한없이 깊이 진행되는 것을 막기 위해 깊이 제한(depth bound)를 사용.

– 깊이 제한에 도달할 때까지 목표노드가 발견되지 않으면 최근에 첨가된 노두의 부모노드로 돌아와서(백트래킹-backtracking), 부모노드에 이전과는 다른 동작자를 적용하여 새로운 자식노드를 생성.

장점

– 단지 현 경로상의 노드만을 기억하면 되므로 저장공간의 수요가 비교적 적다.

– 목표노드가 깊은 단계에 있을 경우 해를 빨리 구할 수 있다.

단점

– 해가 없는 경로에 깊이 빠질 가능성이 있다. 따라서 실제의 경우 미리 지정한 임의의 깊이까지만 탐색하고 목표노드를 발경하지 못하면 다음 경로를 따라 탐색하는 방법이 유용할 수도 있다.

– 얻어진 해가 최단 경로가 된다는 보장이 없다. 이는 목표에 이르는 경로가 다수인 문제에 대해 DFS는 해에 다다르면 탐색을 끝내버리므로, 이때 얻어진 해는 최적이 아닐 수도 있다.

DFS의 애니메이션 [출처 : 위키피디아(깊이 우선 탐색)]

예시 -> 1부터 시작해서 작은 수부터 탐색

무방향 그래프 예시

시작노드를 스택에 삽입 및 방문 처리

1과 인접한 노드가 2, 3, 8이 있는데 ‘작은 수 부터 탐색’ 규칙에 따라 2부터 탐색을 진행한다

2를 방문처리하고 2와 인접한 노드가 1, 7 이렇게 있는데 1은 이미 방문했으니 7에 대해 탐색을 진행한다.

7을 방문처리하고 7과 인접한 노드가 2, 8, 6이 있는데 2는 이미 방문했고 ‘작은 수 부터 탐색’ 규칙에 따라 6을 탐색한다.

6을 방문처리 한다.

가장 깊게 들어왔으니 다른 방향으로 가야한다.

현재 스택의 최상단 노드인 6에 방문하지 않은 인접 노드가 없다.

따라서 스택에서 6 번 노드를 꺼낸다.

6번을 꺼낸뒤 7번에서 아직 방문하지 않은 8번을 방문한다.

이런식으로 반복을 진행하여 모든 노드를 탐색한다.

코드

// 코드 참고 : https://github.com/ndb796/python-for-coding-test #include #include using namespace std; // index 0은 사용하지 않음으로 배열을 하나 더 추가 bool visited[9]; vector graph[9]; void dfs(int x) { visited[x] = true; cout << x << " "; for (int i = 0; i < graph[x].size(); i++) // 인접한 노드 사이즈만큼 탐색 { int y = graph[x][i]; if (!visited[y]) // 방문하지 않았으면 즉 visited가 False일 때 not을 해주면 True가 되므로 아래 dfs 실행 dfs(y); // 재귀적으로 방문 } } int main(void) { /* 위 그래프와 동일하게 정의 */ graph[1].push_back(2); graph[1].push_back(3); graph[1].push_back(8); graph[2].push_back(1); graph[2].push_back(7); graph[3].push_back(1); graph[3].push_back(4); graph[3].push_back(5); graph[4].push_back(3); graph[4].push_back(5); graph[5].push_back(3); graph[5].push_back(4); graph[6].push_back(7); graph[7].push_back(2); graph[7].push_back(6); graph[7].push_back(8); graph[8].push_back(1); graph[8].push_back(7); dfs(1); } 위 코드의 핵심은 당연히도 이 부분이다. for (int i = 0; i < graph[x].size(); i++) // 인접한 노드 사이즈만큼 탐색 { int y = graph[x][i]; if (!visited[y]) // 방문하지 않았으면 즉 visited가 False일 때 not을 해주면 True가 되므로 아래 dfs 실행 dfs(y); // 재귀적으로 방문 } 아래와 같이 정의된 배열을 1번부터 시작하여 가지치기 하듯이 들어가 방문여부를 확인한다. 보기 어렵지만... 1번부터 탐색 과정을 천천히 따라가면서 보면 될 것 같다. BFS : Breadth First Search(너비 우선 탐색) - 시작 노드를 방문한 후 시작 노드에 있는 인접한 모든 노드들을 우선 방법. (최단 경로 탐색 or 임의의 경로 탐색) - 더 이상 방문하지 않은 노드가 없을 때까지 방문하지 않은 모든 노드에 대해서도 BFS를 적용한다. - 큐(Queue)를 이용하여 구현. BFS의 애니메이션 [출처 : 위키피디아(너비 우선 탐색)] - 그래프에서 가장 가까운 노드부터 우선적으로 탐색하는 알고리즘 큐 자료구조를 이용한다 1. 탐색 시작 노드를 큐에 삽입하고 방문 처리 2. 큐에서 노드를 꺼낸 뒤에 해당 노드의 인접 노드 중 방문하지 않은 노드를 모두 큐에 삽입 후 방문 처리 3. 2번 반복 특정 조건의 최단 경로 알고리즘을 계산할 때 사용 그래프 예시 (위 DFS 그래프와 동일) 예시 -> 1부터 시작해서 작은 수부터 탐색

1. 시작노드 1을 큐에 삽입후 방문 처리

2. 큐에서 노드 1을 꺼내 방문하지 않은 인접노드 2, 3, 8을 큐에 삽입후 방문 처리

3. 큐에서 노드 2를 꺼내 방문하지 않은 인접노드 7을 큐에 삽입하고 방문처리 (1은 이미 방문처리 되어 있음)

4. 큐에서 노드 3을 꺼내 방문하지 않은 인접노드 4, 5를 큐에 삽입하고 방문처리(1은 이미 방문처리)

5. 큐에서 노드 8을 꺼내지만 방문하지 않은 인접노드가 없기 때문에 무시

이런식으로 진행하면 최종 방문 순서가

1 -> 2 -> 3 -> 8 -> 7 -> 4 -> 5 -> 6 인걸 알 수 있음

장점

– 출발노드에서 목표노드까지의 최단 길이 경로를 보장

단점

– 경로가 매우 길 경우에는 탐색 가지가 급격히 증가함에 따라 보다 많은 기억 공간을 필요

– 해가 존재하지 않는다면 유한 그래프의 경우, 모든 그래프를 탐색한 후에 실패로 끝난다.

– 무한 그래프의 경우에는 결코 해를 찾지도 못하고, 끝내지도 못한다.

C++ 코드

// 코드 참고 : https://github.com/ndb796/python-for-coding-test #include #include #include using namespace std; bool visited[9]; vector graph[9]; // BFS 함수 정의 void bfs(int start) { queue q; q.push(start); // 첫 노드를 queue에 삽입 visited[start] = true; // 첫 노드를 방문 처리 // 큐가 빌 때까지 반복 while (!q.empty()) { // 큐에서 하나의 원소를 뽑아 출력 int x = q.front(); q.pop(); cout << x << ' '; // 해당 원소와 연결된, 아직 방문하지 않은 원소들을 큐에 삽입 for (int i = 0; i < graph[x].size(); i++) { int y = graph[x][i]; if (!visited[y]) { q.push(y); visited[y] = true; } } } } int main(void) { // 노드 1에 연결된 노드 정보 저장 graph[1].push_back(2); graph[1].push_back(3); graph[1].push_back(8); // 노드 2에 연결된 노드 정보 저장 graph[2].push_back(1); graph[2].push_back(7); // 노드 3에 연결된 노드 정보 저장 graph[3].push_back(1); graph[3].push_back(4); graph[3].push_back(5); // 노드 4에 연결된 노드 정보 저장 graph[4].push_back(3); graph[4].push_back(5); // 노드 5에 연결된 노드 정보 저장 graph[5].push_back(3); graph[5].push_back(4); // 노드 6에 연결된 노드 정보 저장 graph[6].push_back(7); // 노드 7에 연결된 노드 정보 저장 graph[7].push_back(2); graph[7].push_back(6); graph[7].push_back(8); // 노드 8에 연결된 노드 정보 저장 graph[8].push_back(1); graph[8].push_back(7); bfs(1); } 그래프 표현 방식 1. 인접 행렬 : 2차원 배열로 그래프 표현 그래프가 위와 같이 정의 되어 있다면 행렬은 아래와 같다. [i]에서 [j] 방향으로 연결이 되어 있으면 1, 아니면 0 처리를 한다. 위는 방향이 있는 유향 그래프이며 무향 그래프라면 행렬 값은 달라진다. 장점 - 구현이 쉽다. - 연결 여부를 확인하기 용이하다. array[i][j]가 1인지 0인지만 알면 되기 때문에 단점 - 노드 [i]와 연결된 모든 노드들에 방문해보고 싶을 때 array[i][1]부터 array[i][전체노드 개수]를 모두 확인해보아야 되기 때문에 시간 복잡도가 크다. 2. 인접 리스트 : 리스트로 그래프 표현 - 인접 리스트는 그래프의 연결 리스트의 자료 구조를 이용하여 구현 (파이썬에서는 리스트, c++에서는 vector를 사용하여 구현) - array[i] : 노드 i에 연결된 노드들을 원소로 같는 리스트 위 또한 방향이 있는 유향 그래프이기 때문에, 무향 그래프라면 값은 달라진다. 참고 - ko.wikipedia.org/wiki/%EA%B9%8A%EC%9D%B4_%EC%9A%B0%EC%84%A0_%ED%83%90%EC%83%89 - ko.wikipedia.org/wiki/%EB%84%88%EB%B9%84_%EC%9A%B0%EC%84%A0_%ED%83%90%EC%83%89 - sarah950716.tistory.com/12 https://www.youtube.com/watch?v=7C9RgOcvkvo

키워드에 대한 정보 dfs bfs 알고리즘

다음은 Bing에서 dfs bfs 알고리즘 주제에 대한 검색 결과입니다. 필요한 경우 더 읽을 수 있습니다.

이 기사는 인터넷의 다양한 출처에서 편집되었습니다. 이 기사가 유용했기를 바랍니다. 이 기사가 유용하다고 생각되면 공유하십시오. 매우 감사합니다!

사람들이 주제에 대해 자주 검색하는 키워드 [자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java

  • Coding Interview
  • 코딩 인터뷰
  • Java
  • 자바
  • Algorithm
  • 자료구조
  • Data structure
  • 알고리즘
  • Graph Search
  • 그래프 검색
  • Graph
  • 그래프
  • DFS
  • BFS
  • 깊이우선검색
  • 넓이우선검색
[자료구조 #알고리즘] #Graph #검색 #DFS, #BFS #구현 #in #Java


YouTube에서 dfs bfs 알고리즘 주제의 다른 동영상 보기

주제에 대한 기사를 시청해 주셔서 감사합니다 [자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java | dfs bfs 알고리즘, 이 기사가 유용하다고 생각되면 공유하십시오, 매우 감사합니다.

See also  Uhm Meaning From A Girl | 10 Texts All Girls Have Sent (And What They Mean) 190 개의 새로운 답변이 업데이트되었습니다.