* feat(ko): add the complete Korean online edition * Use UTF-8 for Python CI output on Windows * Remove print-book promotion from the Korean site * fix(ko): preserve emphasis before Korean particles
3.1 KiB
이진 탐색으로 경계 찾기
왼쪽 경계 찾기
!!! question "문제"
중복 원소를 포함할 수 있는 길이 $n$의 정렬된 배열 `nums`가 주어진다. 배열에서 가장 왼쪽에 있는 원소 `target`의 인덱스를 반환하라. 해당 원소가 없으면 $-1$을 반환하라.
이진 탐색으로 삽입 위치를 찾는 방법을 떠올려 보자. 탐색이 끝나면 $i$는 가장 왼쪽의 target을 가리킨다. 따라서 삽입 위치 찾기는 본질적으로 가장 왼쪽 target의 인덱스를 찾는 과정이다.
삽입 위치를 찾는 함수를 이용하여 왼쪽 경계를 찾아 보자. 배열에 target이 없을 수도 있으며, 이때는 다음 두 결과 중 하나가 나올 수 있다.
- 삽입 위치의 인덱스 $i$가 범위를 벗어난다.
- 원소
nums[i]가target과 다르다.
위 두 경우에는 $-1$을 바로 반환하면 된다. 코드는 다음과 같다.
[file]{binary_search_edge}-[class]{}-[func]{binary_search_left_edge}
오른쪽 경계 찾기
가장 오른쪽에 있는 target은 어떻게 찾을까? 가장 직접적인 방법은 코드를 수정하여 nums[m] == target일 때 포인터로 구간을 줄이는 방식을 바꾸는 것이다. 코드는 생략하니, 관심 있는 독자는 직접 구현해 보자.
아래에서는 더 기발한 두 가지 방법을 소개한다.
왼쪽 경계 찾기 재활용
가장 왼쪽 원소를 찾는 함수로 가장 오른쪽 원소도 찾을 수 있다. 구체적으로는 가장 오른쪽 target을 찾는 문제를 가장 왼쪽 target + 1을 찾는 문제로 바꾼다.
아래 그림과 같이 탐색이 끝나면 포인터 $i$는 가장 왼쪽 target + 1(존재한다면)을 가리키고, $j$는 가장 오른쪽 target을 가리킨다. 따라서 $j$를 반환하면 된다.
반환되는 삽입 위치는 $i$이므로, 여기서 $1$을 빼야 $j$를 얻을 수 있다.
[file]{binary_search_edge}-[class]{}-[func]{binary_search_right_edge}
원소 찾기로 변환
배열에 target이 없으면 최종적으로 $i$는 target보다 큰 첫 번째 원소를, $j$는 target보다 작은 가장 오른쪽 원소를 가리킨다.
따라서 아래 그림과 같이 배열에 없는 원소를 만들어 왼쪽 경계와 오른쪽 경계를 찾을 수 있다.
- 가장 왼쪽
target찾기:target - 0.5를 탐색하고 포인터 $i$를 반환하는 것으로 바꿀 수 있다. - 가장 오른쪽
target찾기:target + 0.5를 탐색하고 포인터 $j$를 반환하는 것으로 바꿀 수 있다.
코드는 생략하며, 다음 두 가지에 주의하자.
- 주어진 배열에는 소수가 없으므로 값이 같은 경우의 처리는 고려하지 않아도 된다.
- 이 방법은 소수를 사용하므로 함수의 변수
target을 부동 소수점 타입으로 바꿔야 한다(Python은 수정할 필요가 없다).

