https://www.acmicpc.net/problem/2108
Problem 💻
문제
수를 처리하는 것은 통계학에서 상당히 중요한 일이다. 통계학에서 N개의 수를 대표하는 기본 통계값에는 다음과 같은 것들이 있다. 단, N은 홀수라고 가정하자.
- 산술평균 : N개의 수들의 합을 N으로 나눈 값
- 중앙값 : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값
- 최빈값 : N개의 수들 중 가장 많이 나타나는 값
- 범위 : N개의 수들 중 최댓값과 최솟값의 차이
N개의 수가 주어졌을 때, 네 가지 기본 통계값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 수의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 단, N은 홀수이다. 그다음 N개의 줄에는 정수들이 주어진다. 입력되는 정수의 절댓값은 4,000을 넘지 않는다.
출력
첫째 줄에는 산술평균을 출력한다. 소수점 이하 첫째 자리에서 반올림한 값을 출력한다.
둘째 줄에는 중앙값을 출력한다.
셋째 줄에는 최빈값을 출력한다. 여러 개 있을 때에는 최빈값 중 두 번째로 작은 값을 출력한다.
넷째 줄에는 범위를 출력한다.
Solution 💡
입력값으로 정수값에 대한 리스트가 나오고 문제에선 수학적 조건 네가지가 나온다.
- 산술평균 : N개의 수들의 합을 N으로 나눈 값
- 중앙값 : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값
- 최빈값 : N개의 수들 중 가장 많이 나타나는 값
- 범위 : N개의 수들 중 최댓값과 최솟값의 차이
주어진 입력을 위 조건에 맞게 하나씩 출력하면 된다. 단, 아래와 같이 출력에 있어 세부조건들이 있다.
- 첫째 줄에는 산술평균을 출력한다. 소수점 이하 첫째 자리에서 반올림한 값을 출력한다.
- 둘째 줄에는 중앙값을 출력한다.
- 셋째 줄에는 최빈값을 출력한다. 여러 개 있을 때에는 최빈값 중 두 번째로 작은 값을 출력한다.
- 넷째 줄에는 범위를 출력한다.
간단한 계산식이지만 문제를 대충 본다면 헤맬 수 있는 요소들은 있다.
우선 첫번째 조건을 본다면 간단한 평균을 구하는 방식인데 소수점에 대한 언급이 있으니 `int나 long` 같은 정수형을 사용하면 안 되고 `double` 같이 소수점을 나타낼 수 있는 실수형을 사용해야 한다.
두 번째는 입력배열에 첫 인덱스와 마지막 인덱스를 더해 2로 나눈 인덱스를 활용하면 된다.
세 번째 최빈값에 경우 `countMap`을 활용하는 방식으로 진행했다. `countMap`의 최댓값을 `Collections.max()` 메서드를 통해 얻은 다음 `entrySet()` 이용해 루프를 돌아 해당값과 동일한 `value`의 `key`값들을 리스트로 모아 오름차순 정렬하였다. 오름차순 정렬의 이유는 동일 값이 여러 개의 경우 작은 값 중 두 번째 값을 구해야 하므로 정렬을 해야 쉽게 구할 수 있다.
네 번째의 경우 문제에 나온 것과 같이 가장 작은 값과 큰 값을 빼주면 된다. (입력값을 오름차순 정렬하여 처음과 끝값을 활용하면 된다.)
package backjoon.silver.lv3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class B2108 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
// 데이터 입력 및 초기화
int[] numbers = new int[n];
Map<Integer, Integer> frequencyMap = new HashMap<>();
double total = 0;
for (int i = 0; i < n; i++) {
int num = Integer.parseInt(br.readLine());
numbers[i] = num;
total += num;
frequencyMap.put(num, frequencyMap.getOrDefault(num, 0) + 1);
}
// 데이터 정렬
Arrays.sort(numbers);
// 결과 계산
int mean = calculateMean(total, n);
int median = calculateMedian(numbers);
int mode = calculateMode(frequencyMap);
int range = calculateRange(numbers);
// 결과 출력
printResults(mean, median, mode, range);
}
private static int calculateMean(double total, int n) {
return (int) Math.round(total / n);
}
private static int calculateMedian(int[] numbers) {
return numbers[numbers.length / 2];
}
private static int calculateMode(Map<Integer, Integer> frequencyMap) {
int maxFrequency = Collections.max(frequencyMap.values());
List<Integer> candidates = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
if (entry.getValue() == maxFrequency) {
candidates.add(entry.getKey());
}
}
Collections.sort(candidates);
return candidates.size() == 1 ? candidates.get(0) : candidates.get(1);
}
private static int calculateRange(int[] numbers) {
return numbers[numbers.length - 1] - numbers[0];
}
private static void printResults(int mean, int median, int mode, int range) {
StringBuilder sb = new StringBuilder();
sb.append(mean).append("\n");
sb.append(median).append("\n");
sb.append(mode).append("\n");
sb.append(range).append("\n");
System.out.println(sb);
}
}
초기엔 아래와 같이 `StringBuilder` 안에 각 로직을 넣었는데, 코드를 읽는데 어려움을 느껴 위와 같이 최종적으로 리팩토링 하였다. 메서드 분리를 한 것이기 때문에 속도적 이점은 크게 없겠지만 객체지향적으로 코드 구성하는 데 연습이 될 만한 문제였다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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());
double total = 0;
Map<Integer, Integer> countMap = new HashMap<>();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
int input = Integer.parseInt(st.nextToken());
arr[i] = input;
total += input;
countMap.put(input, countMap.getOrDefault(input, 0) + 1);
}
Arrays.sort(arr);
int minIndex = (0 + arr.length - 1) / 2;
// 출력값
StringBuilder sb = new StringBuilder();
sb.append((int)Math.round(total / n)).append("\n");
sb.append(arr[minIndex]).append("\n");
sb.append(findFrequence(countMap)).append("\n");
sb.append(arr[arr.length - 1] - arr[0]).append("\n");
System.out.println(sb);
}
private static int findFrequence(Map<Integer, Integer> countMap) {
int max = Collections.max(countMap.values()); // 가장 빈번하게 호출되는 값
List<Integer> list = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : countMap.entrySet()) {
if (entry.getValue() == max) {
list.add(entry.getKey());
}
}
Collections.sort(list);
if (list.size() == 1) {
return list.get(0);
} else {
return list.get(1);
}
}
}
'코딩테스트 > 백준' 카테고리의 다른 글
[코딩테스트] 백준 1012번 유기농 배추 - JAVA (1) | 2024.12.11 |
---|---|
[코딩테스트] 백준 1697번 숨바꼭질 - JAVA (0) | 2024.12.11 |
[코딩테스트] 백준 2644번 촌수계산 - JAVA (0) | 2024.12.08 |
[코딩테스트] 백준 2805번 나무 자르기 - Java (1) | 2024.12.07 |
[코딩테스트] 백준 24444번 알고리즘 수업 - 너비 우선 탐색 1 - Java (0) | 2024.12.07 |