티스토리 뷰
1부터 100까지의 숫자 중 특정한 숫자 하나를 찾는 방법은 무엇이 있을까? 가장 간단하고 정확한 방법은 찾으려는 1부터 100까지 숫자를 각각 비교해나가는 방법이다. 조금 무식하고 오래 걸리는 방법이긴 하지만 더 정확한 방법은 없다. 특히 숫자가 배열 구조로 저장되어 있다면 이런 방법을 사용할 수 밖에 없다. 이 구조를 이진 트리 구조로 바꿔보자. 최상위 노드에 중앙 값인 50을 넣고 그 아래는 각 서브트리에 해당하는 중앙 값은 25와 75를 차례대로 기록한다. 이런 방식으로 1부터 100에 대한 숫자를 이진 트리로 바꿔 저장되어 있다고 생각해보자. 찾으려는 숫자를 50과 비교하여 크다면 75서브트리로, 50보다 작다면 25서브트리로 내려간다. 이런 방식으로 숫자를 찾게 된다면 단 7번의 비교로도 원하는 숫자를 찾아낼 수 있다. 문제를 해결하기 위해 자료와 알고리즘 구조를 바꿔 문제를 해결 하는 것을 변환 정복 알고리즘 (Transform & Conquer)라고 한다.
1. Instance Simplification
n번의 비교 연산의 경우 시간복잡도가 O(n)이므로, O(n^2)에 속하게 된다. 따라서 위 알고리즘의 시간 복잡도는 O(n^2)이 된다. 최악의 연산을 보이는 경우는 리스트 안에 있는 숫자들 중 같은 숫자가 없는 경우다. 꼼짝없이 모든 숫자에 대한 비교 연산을 모두 시행해야 하기 때문이다.
이 문제를 조금더 쉽게 풀기 위해서 변환 정복 알고리즘을 적용해보자. 원리는 간단하다 보다 간소한 사례의 알고리즘을 적용하기 위해 정렬되지 않은 리스트를 정렬 시킨다. 정렬 알고리즘은 앞서 배운 알고리즘중 Merge Sort 를 사용한다. Merge Sort의 시간 복잡도는 앞서 설명한 바와 같이 O(n log n)이다. 위의 리스트를 정렬한다면 아래와 같이 정리 될 것이다.
A={1, 5, 5, 5, 6, 7, 7}
첫번쨰 숫자 1부터 리스트 상에서 나오는 길이를 계산하여 저장한다. 첫번째 숫자를 가지고 동일한 숫자가 반복되면 해당 숫자의 길이를 1 증가 시키고 다르다면 새롭게 길이를 저장한다. 1의 경우 1개의 숫자나오므로 1을 저장하고, 5는 3을 저장하게 된다. 이 과정은 총 n번의 연산으로 가능하게 된다. 이 과정의 시간 복잡도를 구하면 아래와 같다.
앞서 구힌 알고리즘 보다 더 좋은 성능으로 문제를 해결 할 수 있다.
2. Representation Change
- 각 노드에 값이 있다.
- 각 노드의 키값은 모두 달라야 한다.
- 값들은 전순서가 있다.
- 노드의 왼쪽 서브트리에는 그 노드의 값보다 작은 값들을 지닌 노드들로 이루어져 있다.
- 노드의 오른쪽 서브트리에는 그 노드의 값보다 큰 값들을 지닌 노드들로 이루어져 있다.
- 좌우 하위 트리는 각각이 다시 이진 탐색 트리여야 한다.
- 중복된 노드가 없어야 한다.
첫 번째 사진은 불균형이 발생한 최상위 노드 C 기준 좌측 서브트리 중 좌측 노드에서 불균형이 발생된 LL타입의 불균형이다. 두 번째는 최상위 노드 A 기준 우측 서브트리 중 우측 노드에서 불균형이 발생된 RR타입의 불균형이다. 그리고 세 번째는 최상위 노드 A 기준 우측 서브트리 중 좌측 노드에서 불균형이 발생한 RL타입이다. 마지막은 최상위 노드 C 기준 좌측 서브트리 중 우측 노드에서 불균형이 발생한 LR타입이다.
이렇게 4개의 타입의 불균형을 해결하기 위해서 회전을 사용합니다. LL회전과 RR회전은 매우 간단합니다. 두 타입에서 공통적으로 있는 중앙 노드인 B를 상위 노드로 변경하고, 최상위 노드를 B의 하위 노드로 전환하면 간단하게 문제가 해결됩니다. LL 회전의 경우 C를 C 자신의 좌측 서브트리로 내리고. B를 원래 C가 있던 자리에 삽입하면 됩니다. RR 회전 역시 반대방향을 동일하게 문제를 해결하면 됩니다.
하지만 RL 타입과 LR 타입의 경우 약간 복잡한 과정을 거쳐야 합니다. 두 경우 모두 위 사진을 기준으로 중앙에 존재하게 되는 값은 모두 B입니다. 그렇기 때문에 2번의 회전을 거쳐 불균형을 해결합니다. 우선, RL회전의 경우 하위 2개의 노드 C,B에 대하여 우측 회전을 실시 합니다. C를 우측 하위 노드로 내리고 그 자리에 B를 올려 RR 타입과 동일한 방법을 만듭니다. 그 이후 RR 타입을 해결했던 방식을 그대로 적용합니다. LR 타입 역시 마찬가지로 A,B에 대하여 좌측 회전을 실시하고 LL타입과 동일하게 문제를 해결하면 됩니다.
3.Problem Reduction
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 | import java.util.*; public class Solution { public static int gcd(int m, int n){ while(n!=0){ int r = m%n; m=n; n=r; } return m; } public static void main(String[] args) { // TODO Auto-generated method stub Scanner sc = new Scanner(System.in); int m = sc.nextInt(); int n = sc.nextInt(); System.out.println(gcd(m,n)); } } | cs |
직관적인 최소 공배수를 구하기 위해서는 각 숫자들의 배수를 구하고, 서로 일치하는 배수가 있을 때 까지 곱해나가야 한다. 이 과정으로 구한 코딩을 구현하면 아래와 같다.
1 2 3 4 5 6 7 | public static int lcm(int m, int n){ for (int i=1; i<n; i++){ int p = m*i; if(p%n==0) return p; } return m*n; } | cs |
하지만, 최대공약수를 풀어내는 알고리즘을 가지고 있다면 더욱 쉽게 최대 공약수를 구할 수 있다.
1 2 3 | public static int lcm(int m, int n){ return m*n/gcd(m,n); } | cs |
딱 이 한줄의 코드로 충분히 최대 공약수를 만들 수 있다.
변환 정복 알고리즘은 앞선 정복 알고리즘에 비해서 더욱 까다롭고, 사용 범위가 좁다. 그 이유는 변환 과정에 소요되는 자원이 너무 큰 경우가 매우 자주 발생하기 때문이다. 위 3가지 경우에서 모두 더 쉬운 알고리즘으로 바꾸는 작업을 수행 하는 과정이 더욱 복잡한 경우도 종종 있게된다. 변환 정복 알고리즘 사용을 위해서는 매우 제한 적으로 사용해야 할 것이다.
'Skill > Programming - Basic' 카테고리의 다른 글
11. 문자열 찾기 (String Matching Algorithm) (0) | 2018.05.24 |
---|---|
10. 시간-공간 변환 (Space-Time trade off) (0) | 2018.05.09 |
8. 분할 정복 알고리즘 (Divide and Conquer) (0) | 2018.04.03 |
7. 부분 정복 알고리즘(Decrease and Conquer) (0) | 2018.03.13 |
6.알고리즘 시간복잡도 (0) | 2018.02.27 |