본문 바로가기

Algorithm/Algorithm patterns

DFS를 사용한 순열 구하기

더보기

순열이란 n개의 값 중에서 r개의 값을 뽑는 모든 경우의 수를 의미합니다.

예를 들어 1, 2, 3 값 중에서 2개의 값을 뽑는 모든 경우의 수는 다음과 같습니다. 순열은 값의 중복을 배제하지 않기 때문에 1, 2 그리고 2, 1이 동시에 나타납니다.

shell
닫기
[1, 2] [1, 3] [2, 1] [2, 3] [3, 1] [3, 2]

DFS란?

깊이 우선 탐색(DFS, Depth-First Search)은 그래프의 시작 노드를 기준으로 탐색 할 수 있는 가장 깊은 노드까지 진행합니다.

탐색은 마지막 노드에 도달했을 때 종료되고 이전 노드로 돌아온다음 분기된 다른 노드를 이어서 탐색합니다.

0
1
4
2
3
5
6
7
0
1
4
2
3
5
6
7

예시의 그래프에서 DFS를 실행하면 0, 1, 2 0, 1, 3 0, 4, 5, 6 0, 4, 5, 7의 탐색 경로를 구할 수 있습니다.

java
닫기
public class DFS { protected static void dfs(int[] arr, boolean[] visited, int depth, int r) { ‌‌if (depth == r) ‌‌‌return; ‌‌ ‌‌for (int i = 0; i < arr.length; ++i) ‌‌{ ‌‌‌if (visited[i]) ‌‌‌‌continue; ‌‌‌visited[i] = true; ‌‌‌dfs(arr, visited, depth + 1, r); ‌‌‌// 원위치 ‌‌‌visited[i] = false; ‌‌} }
코드 비고
Line 3 boolean[] visited 현재 탐색 경로가 어떤 노드를 방문했는지 마킹합니다.
Line 8:16 for (i = 0; i < arr.length; ++i) { } 현재 노드를 탐색하고 다음 노드로 DFS합니다.
다음 노드 탐색이 완료되면 현재 노드의 마킹을 취소합니다.

DFS를 사용한 순열 흐름도

DFS를 진행하면서 순열을 계산합니다.

깊이(depth)는 0부터 시작해서 뽑으려는 수 r에 도달하면 탐색을 마치고 이전 depth로 돌아갑니다.

depth = 3
depth = 2
depth = 1
1
(1-2) DFS 2 in (2, 3)
(1-4) DFS 3 in (2, 3)
1
2
3
1
3
2
1
3
(1-5) DFS 2 in (2)
1
2
(1-3) DFS 3 in (3)
(1-1) DFS 1 in (1, 2, 3)
(2-1) DFS 2 in (1, 2, 3)
(3-1) DFS 3 in (1, 2, 3)
2
(2-2) DFS 1 in (1, 3)
(2-4) DFS 3 in (1, 3)
2
1
3
2
3
1
2
3
(2-5) DFS 1 in (1)
2
1
(2-3) DFS 3 in (3)
3
(3-2) DFS 1 in (1, 2)
(3-4) DFS 2 in (1, 2)
3
1
2
3
2
1
3
2
(3-5) DFS 1 in (1)
3
1
(3-3) DFS 2 in (2)
depth = 0

예시의 흐름도에서 depth = 3의  1, 2, 3 1, 3, 2 2, 1, 3 2, 3, 1 3, 1, 2 3, 2, 1 6개의 경우의 수가 발생합니다. 

더보기

이 블로그의 문서: Swap을 사용한 순열 구하기와 달리 DFS를 사용하면 순열의 순서가 보장됩니다.

예를 들어 흐름도의 마지막 2개 순열은 기대값 3, 1, 2 그리고 3, 2, 1과 일치합니다.

DFS를 사용한 순열 구현

순열을 계산하기 위한 인자는 arr depth r 그리고 순열의 결과를 저장하는 out과 탐색 경로를 마킹하는 visited입니다.

depth의 초기 값은 0으로 시작합니다. r은 주어진 배열에서 몇 개의 수를 뽑아낼 것인가를 의미합니다.

java
닫기
public class DFS { protected static void permutation(int[] arr, int[] out, boolean[] visited, int depth, int r) { ‌‌if (depth == r) ‌‌{ ‌‌‌printResult(out, r); ‌‌‌return; ‌‌} ‌‌for (int i = 0; i < arr.length; ++i) ‌‌{ ‌‌‌if (visited[i]) ‌‌‌‌continue; ‌‌‌visited[i] = true; ‌‌‌out[depth] = arr[i]; ‌‌‌permutation(arr, out, visited, depth + 1, r); ‌‌‌// 원위치 ‌‌‌out[depth] = 0; ‌‌‌visited[i] = false; ‌‌} } }
코드 비고
Line 5:9 if (depth == r) depth r에 도달하면 하나의 경우의 수가 발생합니다.
Line 11 for (i = 0; i < arr.length; ++i) 현재 depth에서 방문하지 않은 노드를 모두 방문합니다.
Line 15:17 visited[i] = true 현재 인덱스의 노드를 방문합니다.
out[depth] = arr[i] 현재 인덱스의 노드를 순열에 저장합니다.
permutation() 다음 depth를 진행하기 위해 재귀호출합니다.
Line 18:20 out[depth] = 0 visited[i] = false 새로운 순열을 찾기 위해 현재 인덱스의 노드를 원위치합니다.

'Algorithm > Algorithm patterns' 카테고리의 다른 글

중복 순열 구하기  (0) 2023.12.28
Swap을 사용한 순열 구하기  (0) 2023.12.06
너비 우선 탐색(BFS, Breadth-First Search)  (0) 2023.12.05