문제
N개의 수가 주어졌을 때, 이를 오름차순으로 정렬하는 프로그램을 작성하시오.
첫째 줄에 수의 개수 N(1 ≤ N ≤ 10,000,000)이 주어진다. 둘째 줄부터 N개의 줄에는 숫자가 주어진다. 이 수는 10,000보다 작거나 같은 자연수이다.
첫째 줄부터 N개의 줄에 오름차순으로 정렬한 결과를 한 줄에 하나씩 출력한다.
입력
10
5
2
3
1
4
2
3
5
1
7
출력
1
1
2
2
3
3
4
5
5
7
풀이
이번에도 수를 정렬하는 문제이다. 수 정렬하기 2번과 크게 다를건 없다. 단지 메모리 제한을 제외하고는 말이다.
이런식으로 메모리에 제한이 걸려있다. 하지만 일단 아무 생각없이 2에서 사용했던 힙을 사용해보니 당연하게도 메모리에서 걸렸다.
하지만 사실은 이 문제도 처음에 제목에서 어떤 문제인지 알려준다.
바로 카운팅 정렬이라는 것을 사용해야 한다. 카운팅 정렬이란 전체 숫자에서 각 숫자의 빈도 수를 구한 다음 차례대로 빈도수 만큼 출력하는 것을 말한다.
위의 예시로 풀이를 해보자면 5 2 3 1 4 2 4 5 1 7의 숫자들이 있다. 여기서 각 숫자의 빈도수를 계산하여 배열에 집어 넣는다.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
0 | 2 | 2 | 1 | 2 | 2 | 0 | 1 |
이렇게 빈도수를 구 할 수 있다. 그리고 이 빈도수를 토대로 0에서 순차 탐색을 진행하면 된다.
0은 빈도수가 0, 패스
1은 빈도수가 2, 1을 두 번 출력
2는 빈도수가 2, 2를 두 번 출력
3은 빈도수가 1, 3을 한 번 출력
4는 빈도수가 2, 4를 두 번 출력
5는 빈도수가 2, 5를 두 번 출력
6은 빈도수가 0, 패스
7은 빈도수가 1, 7을 한 번 출력
이걸 순서대로 적으면 1 1 2 2 3 4 4 5 5 7로 자동으로 정렬되는 것을 볼 수 있다. 이것을 응용해서 누적합 방식으로도 카운팅 정렬이 사용가능하다.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
0 | 2 | 4 | 5 | 7 | 9 | 9 | 10 |
이렇게 누적합으로 계산을 하면 1은 1, 0/ 2는 3, 2/ 3은 4 이런식으로 누적합 - 1 에서 부터 전 숫자가 있을 때까지로 범위를 지정하여 누적합으로도 계산이 가능하지만 메모리 때문인가 누적합으로 풀이를 진행하니 안되더라...
그래서 위의 방식으로 진행했다.
코드
#include <iostream>
using namespace std;
int main(void){
int counting_array[10001] = {0,}, n;
scanf("%d", &n);
for(int i = 0 ; i < n ; i++){ //1. 수열의 배열을 초기화
int temp;
scanf("%d", &temp);
counting_array[temp]++; //각 값들을 누적
}
for(int i = 0 ; i < 10001; i++){
for(int j = 0 ; j < counting_array[i]; j++){
printf("%d\n", i);
}
}
return 0;
}
굉장히 코드가 간략하다. 처음에는 빈도수 배열, 인덱스 배열, 최초 배열 등 여러개의 배열을 만들다 실패를 해서 다시 곰곰히 생각해서 배열을 최소한으로 줄여서 문제를 풀이 하였다.
배열을 많이 만든 이유가 for문을 중첩시키지 않고도 충분히 풀 수 있을 거라고 생각했는데 결국 이 문제에서 해결해야하는 부분은 시관초과가 아니라 메모리였기 때문에 크게 연연하지 않아도 되는 부분에 너무 연연했던 것 같다.
'공부 > C++' 카테고리의 다른 글
[C/C++] 소트인사이드 (백준 1427) (0) | 2020.08.19 |
---|---|
[C/C++] 통계학 (백준 2108) (0) | 2020.08.18 |
[C/C++] 수 정렬하기 2 (백준 2751) (0) | 2020.08.18 |
[C/C++] 영화감독 숌 (백준 1436) (0) | 2020.08.14 |
[C/C++] 체스판 다시 칠하기 (백준 1018) (0) | 2020.08.14 |