[Algorithm/Java] 백준 2108번 - 통계학
https://www.acmicpc.net/problem/2108
📌 문제
문제 유형
- 수학
- 구현
- 정렬
문제 설명
수를 처리하는 것은 통계학에서 상당히 중요한 일이다. 통계학에서 N개의 수를 대표하는 기본 통계값에는 다음과 같은 것들이 있다. 단, N은 홀수라고 가정하자.
- 산술평균 : N개의 수들의 합을 N으로 나눈 값
- 중앙값 : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값
- 최빈값 : N개의 수들 중 가장 많이 나타나는 값
- 범위 : N개의 수들 중 최댓값과 최솟값의 차이
N개의 수가 주어졌을 때, 네 가지 기본 통계값을 구하는 프로그램을 작성하시오.
입출력 예시
입력
첫째 줄에 수의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 단, N은 홀수이다. 그 다음 N개의 줄에는 정수들이 주어진다. 입력되는 정수의 절댓값은 4,000을 넘지 않는다.
5
1
3
8
-2
2
출력
첫째 줄에는 산술평균을 출력한다. 소수점 이하 첫째 자리에서 반올림한 값을 출력한다.
둘째 줄에는 중앙값을 출력한다.
셋째 줄에는 최빈값을 출력한다. 여러 개 있을 때에는 최빈값 중 두 번째로 작은 값을 출력한다.
넷째 줄에는 범위를 출력한다.
2
2
1
10
🔍 문제 풀이 (최빈값 구하기)
- 정수 배열에 입력값을 저장하고, 정렬 후 중앙값과 범위를 구함
- HashMap을 통해 등장 횟수를 세고, 최빈값 후보들을 리스트에 모아 정렬
- 최빈값이 2개 이상이면 두 번째로 작은 값 출력, 아니면 첫 번째 값 출력
- 평균은 소수점 첫째 자리에서 반올림 처리 (
Math.round()
사용)
Collections.max(map.values())
Collections.max(map.values())
는 컬렉션(이 문제에서는 map의 value들 중) 에서 가장 큰 값을 찾는 메서드이다.
map.values()
- → Map에 저장된 모든 값(value) 들을 가져온다.
- e.g.,
{1=2, 2=3, 3=3}
이면map.values()
는 `[2, 3, 3].
Collections.max(...)
- → 주어진 값들 중 최댓값을 리턴한다.
map이 아래와 같다고 할 때:
{10=2, 20=3, 30=1, 40=3}
→ 각 숫자(10, 20, 30, 40)가 나온 횟수(빈도)를 저장한 것
→ 가장 많이 나온 횟수(최빈값의 빈도수)는 3
이를 얻기 위해 사용하는 게 Collections.max(map.values())
그럼 최빈 값이 여러개일 경우 어떻게하지?
여러 개일 수 있으니 리스트에 담고 정렬 후 조건 처리한다.
int maxFreq = Collections.max(map.values());
List<Integer> modeList = new ArrayList<>();
for (int key : map.keySet()) {
if (map.get(key) == maxFreq) {
modeList.add(key);
}
}
Collections.sort(modeList); // 오름차순 정렬
int mode = modeList.size() >= 2 ? modeList.get(1) : modeList.get(0); // 두 번째로 작은 값
entrySet()과 Map.Entry
코드 | 의미 |
---|---|
Map.Entry<K, V> |
Map의 key-value 한 쌍을 표현하는 객체 |
map.entrySet() |
Map 안의 모든 entry(key, value)를 Set으로 반환 |
entry.getKey() |
해당 쌍의 key |
entry.getValue() |
해당 쌍의 value |
entrySet()
entrySet()
은Map
에 저장된(key, value)
쌍 전체를Set
형태로 반환한다.- 즉, Map을 반복문으로 순회할 때 사용하는 대표적인 방식이다.
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
int key = entry.getKey();
int value = entry.getValue();
// key: 숫자, value: 등장 횟수
}
왜 사용할까?
최빈값을 구하기 위해선 모든 (숫자, 횟수) 쌍을 하나씩 검사해야 한다.
이때 entrySet()
을 사용하면 key와 value를 동시에 쉽게 꺼낼 수 있다.
// value가 maxFreq인 key들만 리스트에 모으기
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if (entry.getValue() == maxFreq) {
modes.add(entry.getKey());
}
}
💻 전체 코드
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int[] nums = new int[n];
int sum = 0;
for (int i = 0; i < n; i++) {
nums[i] = Integer.parseInt(br.readLine());
sum += nums[i];
}
Arrays.sort(nums);
int avg = (int) Math.round((double) sum / n);
int center = nums[n / 2];
int range = nums[n - 1] - nums[0];
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
int maxFreq = Collections.max(map.values());
List<Integer> modes = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if (entry.getValue() == maxFreq) {
modes.add(entry.getKey());
}
}
Collections.sort(modes);
int mode = (modes.size() > 1) ? modes.get(1) : modes.get(0);
System.out.println(avg);
System.out.println(center);
System.out.println(mode);
System.out.println(range);
}
}
💭 배운 점
- 최빈값을 구할 때 단순히 Map에서 찾는 것만으로는 부족하고,
최빈값 후보들을 List에 모아 정렬한 후 두 번째로 작은 값을 출력해야 한다. Map.Entry
와entrySet()
을 통해 (key, value) 쌍을 순회할 수 있다.Math.round()
를 쓰면 산술평균을 정확하게 반올림할 수 있다.
🔁 복습 템플릿
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
int maxFreq = Collections.max(map.values());
List<Integer> modes = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if (entry.getValue() == maxFreq) {
modes.add(entry.getKey());
}
}
Collections.sort(modes);
int mode = (modes.size() > 1) ? modes.get(1) : modes.get(0);
댓글남기기