이번 시간에는 보간 탐색(Interpolation Search), 삼진 탐색(Ternary Search), 지수 탐색(Exponential Search)에 대해서 알아 보도록 하겠습니다.
이번 포스팅의 내용은 사실 코딩테스트 를 준비하시는 분들이라면 이런게 있구나 하고 쓱 넘어가도 되는 파트입니다.
1. 보간 탐색
보간 탐색(Interpoation search)은 정렬된 리스트 에서 범위를 좁혀 가면서 탐색해 나가는 알고리즘입니다.
보간 탐색은 전화부에서 이름(책의 항목이 정렬되는 키 값)을 검색하는 방법과 매우 유사합니다.
동작하는 방식이 이진 탐색과 거의 유사하지만 탐색 위치를 정하는 방식에 있어서 차이점이 존재하게 됩니다.
이진 탐색과 보간 탐색에서 탐색 위치를 정하는 공식은 다음과 같습니다.
arr[]: 데이터가 들어가 있는 배열
low: arr[] 배열의 시작 index
high: arr[] 배열의 마지막 index
target: 검색 값
pos: 탐색 위치 index
이진 탐색은 탐색 위치 가 중간 값을 잡았기 때문에 다음과 같은 공식이 나왔습니다.
pos = (low + high) /2
or = low + (high - low) / 2
보간 탐색의 탐색 위치 를 정하는 공식은 다음과 같습니다.
위 그림은 검색 값 3 을 찾을 때 첫 번째 탐색 위치를 보여줍니다.
이진 탐색은 항상 중간 위치로 탐색 위치를 결정하는 반면, 보간 탐색은 검색 key값에 따라 다른 위치로 이동하게 됩니다.
예를 들어, 검색 key 값이 상대적으로 앞쪽에 있다고 판단되면 비교적 앞쪽에서 탐색을 진행하게 되고 뒷쪽에 있다고 판단되면 뒷쪽에서 탐색을 진행하는 것입니다.
데이터가 선형으로 분포되어 있다면 위의 그림처럼 보간 탐색은 한 번에 데이터를 찾을 수 있기도 합니다. 이처럼 보간 탐색은
데이터가 선형으로 분포되어 있을 때 가장 검색 효율이 좋다고 볼 수 있습니다.
데이터가 선형으로 분포되어 있다는 것은 데이터가 인덱스 값에 비례하여 분포해 있다는 의미이다.
1-1. 보간 탐색 방식
보간 탐색의 방식 을 살펴봅시다.
보간 탐색은 다른 탐색과 마찬가지로 정렬된 배열에서만 사용할 수 있고 이진 탐색과 동작 방식은 동일합니다.
그 과정은 다음과 같습니다.
탐색 위치(pos)를 구한다.
탐색 위치(pos) 값과 구하고자 하는 검색 key값을 비교한다.
값이 같다면 종료
arr[pos] < key(검색 값이 더 큰) 인 경우, 탐색 위치 기준 배열의 오른쪽 구간을 대상으로 탐색한다.
arr[pos] > key(검색 값이 더 작은) 인 경우, 탐색 위치 기준 배열의 왼쪽 구간을 대상으로 탐색한다.
1-2. 종료 조건
보간 탐색의 종료 조건에도 두 가지 방법이 존재하며 이 중 한 가지라도 만족하면 탐색이 종료됩니다.
데이터 탐색에 성공한 경우
검색 key 값을 발견한 경우
arr[pos] == key
검색에 실패한 경우
검색 범위를 벗어나게 되는 경우(반복문 종료)
target < arr[low] && target > arr[high]
1-3. Search 방식
아래의 그림을 보면서 보간 탐색이 어떻게 이루어 지는지 보도록 하겠습니다.
보간 탐색을 통해 다음 배열에서 32라는 key값을 찾는 과정을 보도록 합시다.
탐색 위치 pos를 구한다.
계산 결과 나온 pos값은 4이고 배열의 4번 index가 pos가 됩니다.
arr[pos] (27)와 검색 key값 (32)을 비교한다.
arr[pos] < target 이므로 배열의 오른쪽 구간을 탐색 범위로 하여 탐색을 다시 진행합니다.
바뀐 값으로 다시 탐색 위치 pos를 구한다.
arr[pos] 값과 검색 key값을 비교한다.
두 값이 같기 때문에 원하는 key값을 찾은 것이므로 탐색을 종료한다.
1-4. 보간 탐색 구현
위에서 진행한 것을 통해 코드로 구현해 보겠습니다.
몇몇 특징적인 부분만 설명을 하겠습니다.
arr[low] == arr[high] 일 때 pos값은 low입니다.
pos값을 구할 때 드는 시간 소모를 줄이기 위해 arr[low] != arr[high] 조건을 추가하여 반복문을 벗어나면 target값과 arr[low] 값을 비교하는 logic을 추가하였습니다.
1-5. 시간 복잡도
보간 탐색은 데이터가 선형적으로 균등하게 분포가 되어 있다면 평균 O(log(logn))의 시간 복잡도가 요구 되지만 최악의 경우 데이터가 기하 급수적으로 늘어난다면 O(n)의 시간 복잡도를 소모할 수도 있습니다.
연산
Best
Average
Worst
Search
O(1)
O(log(logn))
O(n)
2. 삼진 탐색(Ternary Search)
삼진 탐색은 이진 탐색과 진짜 거의 동일하다고 볼 수 있는데, 이진 탐색은 중앙을 기준으로 좌/우로 구간을 나누었다면 삼진은 왼쪽, 중간, 오른쪽 3개의 구간으로 나누는 방식입니다.
mid1 값을 좌측 중간값, mid2 값을 우측 중간값이라고 하자. 그러면 다음과 같은 식을 통해 탐색 범위가 정해집니다.
이 식을 보고 드는 의문이 있을 것입니다.
동일한 O(logn)의 시간 복잡도가 요구되어도 분모의 값이 삼진은 3이고 이진은 2이기 때문에 더 효과적인 것인가?
하지만 예상과 다르기 실제로는
이진 탐색이 훨씬 더 선호됩니다
. 그 이유는 최악의 경우, 삼진 탐색 방식은 비교를 더 많이 해야하기 때문입니다.
2-1. 삼진 탐색 구현 코드
3. 지수 탐색(Exponential Search)
이것도 크게 다른 점은 없지만 찾는 방식이 조금 다릅니다.
1, 2, 4, 8, ... 이런 식으로 탐색 index 값을 지수 형태로 높여가면서 구간을 탐색하는 방식입니다. 다른 탐색과 마찬가지로 이런 과정을 통해 탐색을 진행하다가 해당되는 범위를 찾으면 그 안에서 다시 선형 혹은 이진 탐색을 통해 최종 값을 찾는 것입니다.
시간 복잡도는 O(logn)이 되겠습니다.
3-1. 지수 탐색 구현 코드