Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
Tags
- Dependency Injection
- lombok
- 일급 컬렉션
- Spring
- java
- spring security
- OAuth 2.0
- synchronized
- builder
- Google OAuth
- Volatile
- factory
- 일급 객체
Archives
- Today
- Total
HJW's IT Blog
Algorithm: RMQ(Range Minimum Query) 본문
RMQ란 배열에서 일부 구간을 선택하여 그 구간에서의 최소값을 찾는 알고리즘이다.
RMQ를 해결함에 있어서 여러 방법이 존제하는데 살펴보자.
- Brute-force Method
- 이 방법은 가장 단순한 방법으로 해당 구간 내에 모든 원소를 비교하는 방법이다.
- 각 쿼리가 주어질때마다 해당 구간에서 최소값을 찾기 위해 O(n) 시간이 소요된다
- 이 방법은 구현이 간단하고 쿼리가 적은 경우에는 유용할 수 있지만 보통은 그렇지 않다
- 전처리: O(n^2), 질의: O(1)
- 가능한 a, b 모두에 대해 미리 답을 다른 배열 B[a][b]에 미리 답을 저장해 두는 방법이다.
- 값이 변하지만 않는다면 유용하지만 그렇지 못할경우 비효율적
- 전처리: O(n), 질의 O(sqrt(n))
- 이 방법은 square root decomposition을 이용하는 방법이다.
- 배열을 일정한 크기의 블록으로 분할하고 각 블록에 대한 최소값을 미리 계산하여 저장하는 방식을 사용한다.
- 쿼리할 구간이 블록의 범위를 벗어난다면 내부의 값들과 비교하며 최소값을 찾고, 블록에 포함된 경우는 저장된 값을 출력하면 된다.
- 알고리즘의 동작과정은 다음과 같다
- 블록 크기를 정한다. 일반적으로 sqrt(n)
- 블록 내에서 최소값을 저장할 배열 초기화
- 블록 내에서의 최소값을 구하고 저장 --> 모든 블록에 대해 동일
- 구간 [a,b] 에 대한 쿼리 발생시 a와 b가 속한 블록을 찾는다
- a, b가 같은 블록에 속할경우, 해당 블록 내에서 a부터 b까지의 최소값을 직접 계산한다.
- 다른 블록에 속할 경우 첫 블록부터 a 블록 끝까지, 마지막 블록에서 R까지의 부분 구간 최소값을 구하고 중간 블록들에 대해서는 미리 저장된 최소값을 계산
- ex) 배열 A[4,2,1,5,6,9,11,0,4,2,33,12,52,9,17,13] 에 대해 [2,9] 구간의 RMQ
- 배열의 블록을 나눈다 (sqrt(len(A)) = 4)
- Block 1: [4,2,1,5]
- Block 2: [6,9,11,0]
- Block 3: [4,2,33,12]
- Block 4: [52,9,17,13]
- 각 블록의 최소값을 계산한다
- Block 1: 1
- Block 2: 0
- Block 3: 2
- Block 4: 9
- 구간 [2,9]에서 최소값을 구하기 위해 해당 구간이 포함된 블록들을 찾는다
- Block 1: [2,5], 가 포함되므로 Block 1의 최솟값 1과 비교
- Block 2: [6,9], 가 포함되므로 Block 2의 최솟값 0과 비교
- Block 3: [10, 13], 이 포함되므로 Block 3의 최솟값 2 와 비교
- Block 4: 포함되지 않으므로 비교하지 않음
- 각 블록에서 미리 계산해둔 최소값을 이용하여 해당 구간에서의 최소값 계산
- [2,5] : 1
- [6:9] : 0
- [10,13] : 2
- 최소값중 가장 작은 값을 선택하여 반환 (0 반환)
- 배열의 블록을 나눈다 (sqrt(len(A)) = 4)
- 전처리: O(n log n), 질의: O(1)
- 이 방법은 Sparse Table 을 구성하여 해결한다
- 이 방법은 각 a[i] 마다 RMQ(i,i+1), RMQ(i,i+2),..를 미리 구해 놓는 방법이다
- 구해야 할 정답의 수는 최대 n log n 개
- 왜일까?
- 예를 들어 RMQ(i, i+4) 를 구할때, a[i], ... , a[i+4] 를 모두 구하는 것이 아닌, RMQ(i, i+1) 과 RMQ(i+3, i+4)만 비교하면 되기 때문이다
- 왜일까?
- 동작과정
- ST[i][i] = i 로 초기화 한다
- 구간 길이가 2^k인 구간에 대해 최소값을 계산 (단 k는 log(n) 이하의 자연수)
- 구간 길이가 2^k인 구간에 대해 인접한 두 구간의 최소값을 계산하여 저장한다
- ST[i][i + 2^(k-1)] = min ( ST[i][i + 2^(k-1)-1], ST[i + 2^(k-1)][i + 2^(k-1)]
- 여기서 [i, i+2^(k-1)]란, 배열의 인덱스 i 부터 i+2^(k-1) 까지의 구간이다
- 예를 들어 배열 A = [3, 1, 4, 2, 6, 7, 8, 5] 의 배열이 있다고 치자. 다음은 배열 A의 ST 배열이다
| 구간 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 3 | 3 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | ||
| 4 | 4 | 2 | 2 | 2 | 2 | |||
| 2 | 2 | 2 | 2 | 2 | ||||
| 6 | 6 | 6 | 5 | |||||
| 7 | 7 | 5 | ||||||
| 8 | 5 |
'알고리즘' 카테고리의 다른 글
| Algorithm: 과반수 찾기 (0) | 2023.04.14 |
|---|---|
| Algorithm: 트리 지름 (0) | 2023.04.14 |
| Algorithm: 부분합(Prefix Sum) (1) | 2023.04.13 |
| Euclid Algorithm: GCD (0) | 2023.04.11 |
| CCW(Counter-Clockwise) Algorithm (0) | 2023.03.31 |