너비 우선 탐색
루트노드(또는 다른 노드)에서 시작해서 인접한 노드를 먼저 탐색하는 방법
- 시작 정점(노드)으로부터 가까운 정점을 방문하고 멀리 떨어져 있는 정점을 나중에 방문하는 순회 방법
맹목적 탐색방법의 하나로 시작 정점을 방문한 후 시작 정점에 인접한 모든 정점들을 우선 방문하는 방법
- 맹목적 탐색방법이란 이미 정해진 순서에 따라 상태 공간 그래프를 점차 형성해 가면서 해를 탐색하는 방법
더 이상 방문하지 않은 정점이 없을 때까지 방문하지 않은 모든 정점들에 대해서도 너비 우선 검색을 적용
너비 우선 탐색 알고리즘의 장점
- 너비 우선 탐색은 출발노드에서 목표노드까지의 최단 길이 경로를 보장
너비 우선 탐색 알고리즘의 단점
- 경로가 매우 길 경우에는 탐색 가지가 급격히 증가함에 따라 보다 많은 기억 공간을 필요로 하게 된다.
- 해가 존재하지 않는다면 유한 그래프(finite graph)의 경우에는 모든 그래프를 탐색한 후에 실패로 끝난다.
- 무한 그래프(infinite graph)의 경우에는 해를 찾지도 못하고, 끝내지도 못한다.
- 경로가 매우 길 경우에는 탐색 가지가 급격히 증가함에 따라 보다 많은 기억 공간을 필요로 하게 된다.
코드 설계
- 기준이 되는 노드의 상위 노드 검색, 없을 경우 인접 노드 검색
- 같은 레벨의 노드 검색 후, 해당 레벨 + 1의 인접 노드 검색
- 리스트를 확인할 수 있는 Queue, 방문 확인을 위한 visit 배열 생성
- 사용자가 입력한 기준 값으로 queue 넣고 해당 queue의 node 값에 인접한 adjacent node 리스트를 검색하여 방문 표시
코드 구현
- 인접행렬을 기반으로 한 너비 우선 탐색
public class GraphArr {
private int[][] MATRIX; // 그래프 배열 선언
public GraphArr() {}
public GraphArr(int initSize) {
this.MATRIX = new int[initSize + 1][initSize + 1];
}
public void put(int x, int y) {
MATRIX[x][y] = MATRIX[y][x] = 1;
}
public void printGraph() {
for (int i = 0; i < MATRIX.length; i++) {
for (int j = 0; j < MATRIX[i].length; j++) {
System.out.print(MATRIX[i][j] + " ");
}
System.out.println();
}
}
/**
* 너비 우선 탐색
* @param V
*/
private void bfs(int V) {
Queue<Integer> queue = new LinkedList<>();
boolean[] visit = new boolean[MATRIX.length]; // 간선 갯수
queue.offer(V);
visit[V] = true;
while(!queue.isEmpty()) {
int x = queue.poll(); // 방문한 노드를 큐에서 꺼내 인접한 모든 노드를 조회
System.out.print(x + "\t");
for(int i = 0 ; i < MATRIX.length ; i++) {
if(!visit[i] && MATRIX[x][i] == 1) { // 간선확인, 노드 방문 여부 확인
visit[i] = true;
queue.offer(i);
}
}
}
}
public static void main(String[] args) {
int initSize = 7;
GraphArr graph = new GraphArr(initSize);
graph.put(1, 2);
graph.put(1, 3);
graph.put(2, 3);
graph.put(2, 4);
graph.put(2, 5);
graph.put(3, 6);
graph.put(3, 7);
graph.put(4, 5);
graph.put(6, 7);
graph.printGraph();
graph.bfs(1); /* 주어진 노드를 기준으로 BFS 탐색 */
}
}
- 인접 배열 출력 및 BFS 탐색 결과
// 배열 출력
0 0 0 0 0 0 0 0
0 0 1 1 0 0 0 0
0 1 0 1 1 1 0 0
0 1 1 0 0 0 1 1
0 0 1 0 0 1 0 0
0 0 1 0 1 0 0 0
0 0 0 1 0 0 0 1
0 0 0 1 0 0 1 0
// BFS 탐색
1 2 3 4 5 6 7
- 인접 리스트를 기반으로 한 너비 우선 탐색
public class GraphList {
static class Graph {
private int V;
private LinkedList<Integer>[] adjacent;
@SuppressWarnings("unchecked")
public Graph(int V) {
this.V = V;
this.adjacent = new LinkedList[V];
for (int i = 0; i < V; i++) adjacent[i] = new LinkedList<>();
}
public void addEdge(Graph graph, int src, int dest) {
graph.adjacent[src].add(dest);
graph.adjacent[dest].add(src);
}
public void printGraph(Graph graph) {
for (int v = 1; v < graph.V; v++) {
System.out.println("Node [" + v + "]");
System.out.print("adjacent Node: ");
for (Integer node : graph.adjacent[v]) {
System.out.print("[" + node + "]\t");
}
System.out.println();
}
}
/**
* 너비 우선 탐색
* @param start
*/
public void bfs(int start) {
boolean[] visit = new boolean[V];
LinkedList<Integer> queue = new LinkedList<>();
visit[start] = true;
queue.offer(start);
while(!queue.isEmpty()) {
start = queue.poll();
System.out.print(start + "\t");
Iterator<Integer> i = adjacent[start].listIterator();
while(i.hasNext()) {
int n = i.next();
if(!visit[n]) {
visit[n] = true;
queue.offer(n);
}
}
}
}
}
public static void main(String[] args) {
int initSize = 8;
Graph graph = new Graph(initSize);
graph.addEdge(graph, 1, 2);
graph.addEdge(graph, 1, 3);
graph.addEdge(graph, 2, 3);
graph.addEdge(graph, 2, 4);
graph.addEdge(graph, 2, 5);
graph.addEdge(graph, 3, 6);
graph.addEdge(graph, 3, 7);
graph.addEdge(graph, 4, 5);
graph.addEdge(graph, 6, 7);
graph.printGraph(graph);
graph.bfs(1); /* 주어진 노드를 기준으로 BFS 탐색 */
}
}
- 인접 리스트 출력 및 BFS 탐색 결과
// 기준 노드 및 인접 노드 출력
Node [1]
adjacent Node: [2] [3]
Node [2]
adjacent Node: [1] [3] [4] [5]
Node [3]
adjacent Node: [1] [2] [6] [7]
Node [4]
adjacent Node: [2] [5]
Node [5]
adjacent Node: [2] [4]
Node [6]
adjacent Node: [3] [7]
Node [7]
adjacent Node: [3] [6]
// BFS 탐색 1 기준
1 2 3 4 5 6 7
분석
- 인접 리스트 시간 복잡도
- O(N+E)
- 인접 행렬 시간 복잡도
- O(N^2)
- 그래프 내에 적은 숫자의 간선만을 갖는 희소 그래프(Sparse Graph)의 경우 인접 행렬보다 인접 리스트를 사용하는 것이 유리
'알고리즘&자료구조 > basic' 카테고리의 다른 글
[알고리즘] 깊이 우선 탐색(Deap First Search, DFS) (0) | 2020.05.15 |
---|---|
[자료구조] 그래프(Graph) (0) | 2020.05.13 |
[자료구조] 트리(Tree) (0) | 2020.05.12 |
[자료구조] 큐(Queue) (0) | 2020.05.12 |
[자료구조] 스택(Stack) (0) | 2020.05.10 |