Baekjoon 문제 풀기 (10989번 : 수 정렬하기 3) Java
10989번 : 수 정렬하기 3
1. 문제읽기
계수정렬(Counting sort) 문제
n의 범위가 10,000,000 까지이므로 시간복잡도가 O(N)의 정렬을 써야한다는 것을 알아냈다.
그러나 내가 알고 있는 정렬 중에 O(N)은 없어서 검색해봤는데, Counting Sort를 써야했다.
몇 번 이해하려고 했으나 실패했는데, 오늘은 시간을 좀 들인 결과 완벽히는 아니더라도 어느정도 이해할 수 있었다.
Counting Sort는 수의 범위만큼의 메모리를 필요로 하기 때문에 항상 좋은 알고리즘은 아니다.
그렇기 때문에 많은 수가 적은 범위로 있어야 하고, 또한 양수여야한다는 조건이 있다.
이 문제는 수가 10,000보다 작거나 같은 자연수로, 두 조건을 모두 만족한다.
2. 제출 코드
이해를 돕기 위해 기본적인 Counting Sort의 알고리즘을 따라 문제를 풀었다.
counting 배열은 실제로 카운팅을 하는 값의 범위이므로 0을 포함하지 않기 때문에(0인덱스를 사용하지 않음) 범위+1을 해주어야한다. (안해서 틀림)
기존 배열과 result 배열은 어차피 받는 수만큼 채워지므로 상관없다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(bf.readLine());
// 기존 배열 arr
int[] arr = new int[n];
// 카운팅할 배열 counting : 값의 범위+1
int[] counting = new int[10001];
// 정렬한 배열 result
int[] result = new int[n];
// arr 채우기
for(int i = 0; i < n; i++) {
arr[i] = Integer.parseInt(bf.readLine());
}
// counting 하기
for(int i = 0; i < n; i++) {
counting[arr[i]]++; // 기존배열의 값을 인덱스로 가지는 값을 +1씩
}
// 누적합 해주기
for(int i = 1; i < counting.length; i++) {
counting[i] += counting[i-1]; // 기존 값이 어디까지 위치하는지 알기 위해
// arr = {1,2,1}라고 쳤을 때, counting = {0,2,1}
// 누적합 해주기
// counting = {0,2,3} -> 1이 2번째까지, 2가 3번째까지라는 뜻
}
// 정렬하기
for(int i = arr.length-1; i >= 0; i--) { // 안정적으로 정렬하려면 맨 뒤에서부터 하는게 좋음
int value = arr[i]; // value = 기존 값
counting[value]--; // -1을 해줘야 result에 제대로 값이 들어감
result[counting[value]] = value; // result에 기존 값 넣기
}
StringBuilder sb = new StringBuilder();
for(int i : result) {
sb.append(i).append("\n"); // 출력
}
System.out.println(sb);
}
}
3. 공부할 것
수의 범위를 알고 출력만 하는 상황이라면 counting 배열에 바로 값을 카운트해주고, 1부터 끝까지 for문을 돌면서 인덱스값을 출력해버리면(counting 배열의 모든 값이 0이 될 때 까지) 코드를 줄일 수 있다.
아래 예시처럼 말이다.
{1,2,1} 입력 -> counting = {0,2,1} -> 1 출력 -> counting = {0,1,1} -> 1 출력 -> counting = {0,0,1} -> 2 출력 -> counting = {0,0,0}
이걸 코드로 나타내면 아래와 같다.
확실히 시간이 2064ms에서 1640ms로 줄어들었다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(bf.readLine());
int[] counting = new int[10001];
// counting 하기
for(int i = 0; i < n; i++) {
counting[Integer.parseInt(bf.readLine())]++;
}
StringBuilder sb = new StringBuilder();
// 정렬하기
for(int i = 1; i < counting.length; i++) {
while(counting[i] > 0) {
sb.append(i).append("\n");
counting[i]--;
}
}
System.out.println(sb);
}
}
댓글남기기