Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

파이썬으로 정렬된 배열에서 요소의 첫 번째와 마지막 위치 찾기

정수로 이루어진 배열 A가 오름차순으로 정렬되어 있다고 가정해 봅시다. 이때 주어진 목표값(target)이 배열 안에서 처음 등장하는 위치마지막으로 등장하는 위치를 찾아야 합니다. 만약 목표값이 배열에 존재하지 않는다면 [-1, -1]을 반환하면 됩니다.

예를 들어 배열이 [2,2,2,3,4,4,4,4,5,5,6]이고 목표값이 4라면, 값 4는 인덱스 4부터 인덱스 7까지 연속해서 등장하므로 출력은 [4, 7]이 됩니다.

접근 방식: 두 번의 이분 탐색(Binary Search)

배열을 처음부터 끝까지 훑어보면 O(n)의 시간이 걸리지만, 배열이 이미 정렬되어 있다는 특성을 활용하면 이분 탐색을 두 번 수행하여 O(log n) 시간 안에 답을 구할 수 있습니다.

  • 첫 번째 탐색: 목표값을 발견하더라도 멈추지 않고 왼쪽 절반을 계속 탐색하여 가장 왼쪽(첫 번째) 위치를 찾습니다.
  • 두 번째 탐색: 첫 번째 위치 바로 다음 인덱스부터 다시 이분 탐색을 수행하여 가장 오른쪽(마지막) 위치를 찾습니다.

알고리즘 단계

  1. res := [-1, -1]로 초기화하고, low := 0, high := 배열 A의 길이로 설정합니다.
  2. low < high인 동안 아래 과정을 반복합니다.
    • mid := low + (high − low) / 2 로 중간 인덱스를 계산합니다.
    • A[mid]가 목표값과 같다면 → high := mid로 좁히고, res[0] = mid, res[1] = mid로 기록합니다.
    • A[mid]가 목표값보다 작다면 → low := mid + 1, 그 외의 경우 → high := mid로 설정합니다.
  3. 탐색이 끝난 후에도 res[0]이 -1이면 목표값이 배열에 없다는 의미이므로 res를 그대로 반환합니다.
  4. 두 번째 탐색을 위해 low := res[0] + 1, high := 배열의 길이로 다시 설정합니다.
  5. low < high인 동안 아래 과정을 반복합니다.
    • mid := low + (high − low) / 2 로 중간 인덱스를 계산합니다.
    • A[mid]가 목표값과 같다면 → low := mid + 1로 이동하며 res[1] = mid를 갱신합니다.
    • A[mid]가 목표값보다 작다면 → low := mid + 1, 크다면 → high := mid로 설정합니다.
  6. 최종 결과 res를 반환합니다.

예제 코드 (Python)

다음 구현을 통해 동작 방식을 더 자세히 이해할 수 있습니다.

class Solution(object):
    def searchRange(self, nums, target):
        res = [-1,-1]
        low = 0
        high = len(nums)
        while low<high:
            mid = int(low + (high-low)//2)
            if nums[mid] == target:
                high = mid
                res[0]=mid
                res[1]=mid
            elif nums[mid]<target:
                low = mid+1
            else:
                high = mid
        if res[0] == -1:
            return res
        low = res[0]+1
        high = len(nums)
        while low<high:
            mid = int(low + (high-low)//2)
            if nums[mid] == target:
                low = mid+1
                res[1] = mid
            elif nums[mid] < target:
                low = mid + 1
            else:
                high = mid
        return res

ob1 = Solution()
print(ob1.searchRange([2,2,2,3,3,4,4,4,4,5,5,6], 4))

입력

[2,2,2,3,3,4,4,4,4,5,5,6]
4

출력

[5, 8]

복잡도 분석

이 풀이는 이분 탐색을 두 번 수행하므로 시간 복잡도는 O(log n)입니다. 추가적인 배열을 사용하지 않으므로 공간 복잡도는 O(1)로, 대규모 정렬 배열에서도 매우 효율적으로 동작합니다.