프로그래밍 면접 문제 17: Search Unknown Length Array

프로그래밍 면접 문제 17: Search Unknown Length Array

 

이 글은 블로그 http://www.ardendertat.com/프로그래밍 면접에 대한 글을 번역한 글입니다.

이전글 – 프로그래밍 면접 문제 16: Anagram Strings

 

길이를 모르는 정렬된 배열과 찾아야 하는 숫자가 주어지면 배열에서 해당 숫자의 인덱스(index)를 반환하는 기능을 구현하는 문제입니다. 범위를 벗어난 요소에 접근하면 예외를 발생시킵니다. 찾는 숫자가 여러 개 존재하는 경우, 여러 인덱스 중 아무 인덱스나 반환합니다. 찾는 숫자가 배열에 존재하지 않으면 -1을 반환합니다.

간단한 해결방법은 배열을 선형적으로 순회하면서 숫자를 찾고, 범위를 벗어나면 예외를 발생시키는 것입니다. 전자의 경우 해당 인덱스를 반환하고, 후자의 경우에는 -1을 반환합니다. 이 해결방법의 복잡도는 O(N)이며, 여기에서 N은 배열에 존재하는 요소의 수이며, 이는 미리 알 수 없습니다. 하지만, 이 접근 방법에서는 정렬된 배열의 장점을 얻을 수 없습니다. 따라서 이진 탐색을 사용하면, 정렬된 순서의 이점을 얻을 수 있습니다.

하지만 배열의 크기를 미리 알 수 없어, 최고 인덱스 제한 값을 제공할 수 없기 때문에 표준 이진 탐색은 동작하지 않습니다. 따라서 배열의 크기와 배열 요소 자체에 대한 일방 이진 탐색(one-sided binary search)을 동시에 수행합니다. 값 K를 찾는다고 가정해 보겠습니다. 루프에서 예외가 발생하거나 K 보다 큰 요소를 찾을 때까지 0, 1, 2, 4, 8, 16, …, 2^N 배열 인덱스를 확인합니다. 값이 K보다 작으면 계속 진행하고(continue), 운좋게 값 K를 찾으면 해당 인덱스를 반환합니다.

인덱스 2^m에서 K 보다 큰 값을 확인한 경우에는 배열이 정렬되어 있기 때문에, 값 K는(존재하는 경우) 반드시 2^(m-1)+1과 2^m-1(포함) 사이에 존재한다는 의미가 됩니다. 예외가 발생한 경우에도 2^(m-1)이 K보다 작다는 것을 알고 해당 인덱스로 접근했을 때는 예외가 발생하지 않는 다는 것을 알기 때문에 위와 동일합니다. 2^m에서 예외가 발생한 경우에는, 배열이 2^(m-1)과 2^m-1 사이의 크기를 갖는다는 의미입니다. 두 경우 모두, 루프를 중단 시키고, 2^(m-1)과 2^m-1 사이의 범위에서 다른 버전의 이진 탐색을 시작합니다. 이전에 인덱스 2^m에서 예외가 발생한 경우, 이진 탐색 중에 더 많은 예외가 발생할 수 있기 때문에 최고 인덱스 제한 값을 해당 위치에 할당해가면서 이를 처리해야 합니다. 다음 코드를 통해 지금까지 설명한 내용을 구현해 보겠습니다.

def getIndex(arr, num):
    #check array indexes 0, 2^0, 2^1, 2^2, ...
    index, exp = 0, 0
    while True:
        try:
            if arr[index]==num:
                return index
            elif arr[index]<num:
                index=2**exp
                exp+=1
            else:
                break
        except IndexError:
            break
 
    #Binary Search
    left=(index/2)+1
    right=index-1
    while left<=right:
        try:
            mid=left+(right-left)/2
            if arr[mid]==num:
                return mid
            elif arr[mid]<num:
                left=mid+1
            else:
                right=mid-1
        except IndexError:
            right=mid-1
 
    return -1

모든 경우에서 이진 탐색을 사용했고 선형 탐색을 수행하지 않기 때문에, 이 방법의 복잡도는 O(logN) 입니다. 그리고 이 방법은 최적의 방법입니다. 이진 탐색은 가장 중요한 알고리즘 중 하나이며 이 문제는 이진 탐색의 흥미로운 사용법을 보여줍니다.

 

다음글 – 프로그래밍 면접 문제 18: Find Even Occurring Element

RonnieJ

프리랜서 IT강사로 활동하고 있습니다. 게임 개발, C++/C#, 1인 기업에 관심이 많습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Please turn AdBlock off

Notice for AdBlock users

Please turn AdBlock off