입력 : 첫째 줄 - 가지고 있는 숫자 카드의 개수 N (1 ≤ N ≤ 500,000)
둘째 줄 - 숫자 카드에 적혀있는 수 n[N] (-10,000,000 ≤ n[N] ≤ 10,000,000)
셋째 줄 - 가지고 있는 숫자 카드인지 구해야 할 정수 M (1 ≤ M ≤ 500,000)
넷째 줄 - 숫자 카드에 적혀있는 수 m[M] (-10,000,000 ≤ m[M] ≤ 10,000,000)
출력 : M개의 정수가 N개의 정수 안에 존재하면 1, 존재하지 않으면 0
O(Nlog N)
이진탐색
Arrays.binarySearch를 사용했더니 시간초과가 발생했다. lowerBound, upperBound 함수를 설계했다.
출력할때는 시간을 줄이고자 StringBuilder로 사용해 바꿨다.
배열 arr = [1, 2, 2, 3, 3, 3, 4, 5]
key = 3
초기 상태: low = 0, high = 8
1차 반복: mid = 4, arr[4] = 3 (key와 같으므로 high = 4)
2차 반복: mid = 2, arr[2] = 2 (key보다 작으므로 low = 3)
3차 반복: mid = 3, arr[3] = 3 (key와 같으므로 high = 3)
종료: low = 3, high = 3 → 반환값: 3
key = 3
초기 상태: low = 0, high = 8
1차 반복: mid = 4, arr[4] = 3 (key와 같으므로 low = 5)
2차 반복: mid = 6, arr[6] = 4 (key보다 크므로 high = 6)
3차 반복: mid = 5, arr[5] = 3 (key와 같으므로 low = 6)
종료: low = 6, high = 6 → 반환값: 6
lowerBound와 upperBound 함수를 통해 배열 내 특정 값의 시작과 끝+1 위치를 찾아 빈도를 계산함
import java.util.*;
public class Main {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
StringBuilder sb = new StringBuilder();
int N = sc.nextInt();
int[] n = new int[N];
for (int i=0; i<N; i++) {
n[i] = sc.nextInt();
}
Arrays.sort(n); //이진탐색을 위한 정렬
int M = sc.nextInt();
int[] m = new int[M];
for (int i=0; i<M; i++) {
m[i] = sc.nextInt();
sb.append(upperBound(n, m[i]) - lowerBound(n, m[i])).append(" ");
}
System.out.println(sb);
}
public static int lowerBound(int[] arr, int key) {
int low = 0;
int high = arr.length;
while (low < high) {
int mid = (low + high) / 2;
if (arr[mid] < key) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
public static int upperBound(int[] arr, int key) {
int low = 0;
int high = arr.length;
while (low < high) {
int mid = (low + high) / 2;
if (arr[mid] <= key) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
}