수직선 위에 N개의 좌표가 주어질 때,
각 좌표를 좌표 압축한 값을 구하는 문제이다.
좌표 압축의 정의는 다음과 같다.
Xi > Xj 를 만족하는 서로 다른 Xj의 개수
즉, 어떤 값보다 작은 값들의 개수를 구하면 된다.
첫째 줄
N
좌표 개수
둘째 줄
X1 X2 X3 ... XN
조건
1 ≤ N ≤ 1,000,000
-10^9 ≤ Xi ≤ 10^9
좌표 압축 결과
X'1 X'2 X'3 ... X'N
5
2 4 -10 4 -9
2 3 0 3 1
이 문제의 핵심은 다음이다.
값의 순서를 유지하면서
작은 값들의 개수를 빠르게 계산
즉,
정렬 + 중복 제거 + 인덱스 매핑
문제이다.
나는 배열을 복사한 뒤 정렬하고,
Map을 이용해 값 → 압축값을 저장했다.
import java.util.*;
class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int[] arr = new int[N];
int[] sorted = new int[N];
for (int i = 0; i < N; i++) {
arr[i] = sc.nextInt();
sorted[i] = arr[i];
}
Arrays.sort(sorted);
Map<Integer, Integer> map = new HashMap<>();
int idx = 0;
for (int i = 0; i < N; i++) {
if (!map.containsKey(sorted[i])) {
map.put(sorted[i], idx++);
}
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < N; i++) {
sb.append(map.get(arr[i])).append(" ");
}
System.out.println(sb);
}
}
강의에서는 값과 원래 인덱스를 함께 저장한 뒤
정렬 후 압축값을 직접 계산했다.
import java.io.*;
import java.util.*;
class Main
{
public static void main (String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
String[] inputs = br.readLine().split(" ");
int[][] xs = new int[N][2];
for (int i = 0; i < N; i++) {
xs[i][0] = Integer.parseInt(inputs[i]);
xs[i][1] = i;
}
Arrays.sort(xs, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
int[] ans = new int[N];
int idx = 0;
ans[xs[0][1]] = idx;
for (int i = 1; i < N; i++) {
if (xs[i][0] != xs[i - 1][0]) idx++;
ans[xs[i][1]] = idx;
}
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
for (int i = 0; i < N; i++)
bw.write(ans[i] + " ");
bw.flush();
}
}
또 다른 방식은 TreeSet을 이용한 정렬 + Map 매핑이다.
import java.io.*;
import java.util.*;
class Main
{
public static void main (String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
String[] inputs = br.readLine().split(" ");
int[] xs = new int[N];
Set<Integer> setX = new TreeSet<>();
for (int i = 0; i < N; i++) {
xs[i] = Integer.parseInt(inputs[i]);
setX.add(xs[i]);
}
Map<Integer, Integer> sortedIndex = new HashMap<>();
int idx = 0;
for (int x : setX)
sortedIndex.put(x, idx++);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
for (int i = 0; i < N; i++)
bw.write(sortedIndex.get(xs[i]) + " ");
bw.flush();
}
}
배열 복사 → 정렬 → Map으로 매핑
값 + 인덱스 저장 → 정렬 → 압축값 계산
Set으로 중복 제거 + 정렬 → Map 매핑
| 방식 | 특징 |
|---|---|
| 내 코드 | Map 기반 좌표 압축 |
| 강의 코드1 | 정렬 + 인덱스 추적 |
| 강의 코드2 | TreeSet + Map |
정렬: O(N log N)
Map: O(N)
→ 전체
O(N log N)
정렬 + 배열 접근
→ 역시
O(N log N)
TreeSet 삽입: O(log N)
→ 전체
O(N log N)
if (!map.containsKey(sorted[i]))
→ 중복 제거를 직접 처리
if (xs[i][0] != xs[i - 1][0])
→ 정렬 후 비교로 처리
TreeSet
→ 자동으로 정렬 + 중복 제거
값의 순위를 구하는 문제
정렬 → 순서 → 인덱스 부여
방법 3가지
1. Map containsKey
2. 정렬 후 비교
3. Set 사용
value → compressed index
O(N log N)
이 문제는 단순 정렬 문제가 아니라
값을 순위로 변환하는 좌표 압축 문제였다.
내 코드는 Map을 활용해 직관적으로 구현했고,
강의 코드는 다양한 방식으로 해결 방법을 보여주었다.
핵심 차이는 다음과 같다.
내 코드 → Map 중심 접근
강의 코드 → 정렬 중심 접근
결국 어떤 방식이든
정렬 + 중복 제거 + 인덱스 매핑
이 3가지가 핵심이다.
이 문제를 통해
좌표 압축은 정렬 기반 문제
라는 것을 이해할 수 있었다.