N개의 원소를 포함하고 있는 수열이 오름차순으로 정렬되어 있을 때 수열에서 x가 등장하는 횟수를 계산하시오. 단, O(logN) 알고리즘으로 풀어야 한다.
이번 챕터에서는 이진 탐색의 새로운 기능을 배우게 된다.
이 문제의 제목이기도 한데, 이진 탐색을 이용하면 정렬된 배열에서 특정 원소의 개수를 구할 수 있다.
C++
에서는 특정 원소의 개수를 구할 때 아주 편리한 함수가 있다. algorithm
헤더파일에 있는 lower_bound()
와 upper_bound()
함수다.#include <iostream>
using namespace std;
static int N, x, arr[1000000];
int getFirstPos(int left, int right) {
if (left > right) return 0;
int mid = (left + right) / 2;
// `mid`가 시작 원소거나, mid는 x인데 mid - 1이 x보다 작다면 x의 가장 좌측 원소이다.
if ((mid == 0 || x > arr[mid - 1]) && x == arr[mid]) return mid;
else if (arr[mid] < x) getFirstPos(mid + 1, right);
else getFirstPos(left, mid - 1);
}
int getLastPos(int left, int right) {
if (left > right) return 0;
int mid = (left + right) / 2;
// `mid`가 끝 원소거나, mid는 x인데 mid + 1이 x보다 크다면 x의 가장 우측 원소이다.
if ((mid == N - 1 || x < arr[mid + 1]) && x == arr[mid]) return mid;
else if (arr[mid] <= x) getLastPos(mid + 1, right);
else getLastPos(left, mid - 1);
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> N >> x; // N개의 원소와 찾으려는 수 x
for (int i = 0; i < N; ++i) cin >> arr[i];
int answer = getFirstPos(0, N - 1) - getLastPos(0, N - 1);
if (answer != 0) cout << answer + 1 << '\n';
else cout << -1 << '\n';
}
getFirstPos()
함수는 원소 x
가 나타나는 시작 지점(인덱스)을 반환하는 함수다.mid
를 변경하다가 0
번 인덱스에 도착하는 경우 (범위 초과 방지)mid-1
번 인덱스에 들어있는 요소는 x
보다 작고, mid
번 인덱스는 x
인 경우mid
를 이동하는 경우mid
번 인덱스의 값이 x
보다 작다면 오른쪽 부분을 탐색해야 한다.mid
번 인덱스의 값이 x
보다 크다면 왼쪽 부분을 탐색해야 한다.getLastPos()
함수는 원소 x
가 나타나는 종료 지점(인덱스)을 반환하는 함수다.mid
를 변경하다가 N-1
번 인덱스에 도착하는 경우 (범위 초과 방지)mid+1
번 인덱스에 들어있는 요소는 x
보다 크고, mid
번 인덱스는 x
인 경우mid
를 이동하는 경우mid
번 인덱스의 값이 x
보다 작거나 같다면 오른쪽 부분을 탐색해야 한다.mid
번 인덱스의 값이 x
보다 크다면 왼쪽 부분을 탐색해야 한다.#include <iostream>
#include <algorithm>
using namespace std;
static int N, x, arr[1000000];
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> N >> x;
for (int i = 0; i < N; ++i) cin >> arr[i];
int answer = upper_bound(arr, arr + N, x) - lower_bound(arr, arr + N, x);
if (answer != 0) cout << answer << '\n';
else cout << -1 << '\n';
}
이진탐색, C++ 둘다 안쓰고(자바씀) 풀어봤는데 이게 시간복잡도를 허용할지 모르겠네요..
x = 2일때 2가 처음으로 나오는 인덱스만 구해서 거기서부터 선형탐색으로 x인것만 count해줘도 시간복잡도를 통과할 수 있을까요?