자바/알고리즘 문제 풀이

백준/2108 통계학

backend dev 2022. 12. 4.

통계학

 
시간 제한메모리 제한제출정답맞힌 사람정답 비율
2 초 256 MB 118444 25940 20924 25.389%

문제

수를 처리하는 것은 통계학에서 상당히 중요한 일이다. 통계학에서 N개의 수를 대표하는 기본 통계값에는 다음과 같은 것들이 있다. 단, N은 홀수라고 가정하자.

  1. 산술평균 : N개의 수들의 합을 N으로 나눈 값
  2. 중앙값 : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값
  3. 최빈값 : N개의 수들 중 가장 많이 나타나는 값
  4. 범위 : N개의 수들 중 최댓값과 최솟값의 차이

N개의 수가 주어졌을 때, 네 가지 기본 통계값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 수의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 단, N은 홀수이다. 그 다음 N개의 줄에는 정수들이 주어진다. 입력되는 정수의 절댓값은 4,000을 넘지 않는다.

출력

첫째 줄에는 산술평균을 출력한다. 소수점 이하 첫째 자리에서 반올림한 값을 출력한다.

둘째 줄에는 중앙값을 출력한다.

셋째 줄에는 최빈값을 출력한다. 여러 개 있을 때에는 최빈값 중 두 번째로 작은 값을 출력한다.

넷째 줄에는 범위를 출력한다.

예제 입력 1 복사

5
1
3
8
-2
2

예제 출력 1 복사

2
2
1
10

예제 입력 2 복사

1
4000

예제 출력 2 복사

4000
4000
4000
0

예제 입력 3 복사

5
-1
-2
-3
-1
-2

예제 출력 3 복사

-2
-2
-1
2

예제 입력 4 복사

3
0
0
-1

예제 출력 4 복사

0
0
0
1

(0 + 0 + (-1)) / 3 = -0.333333... 이고 이를 첫째 자리에서 반올림하면 0이다. -0으로 출력하면 안된다.


풀이

빈도수를 얻기위해 Map을 이용

중앙값을 얻기위해 List를 이용

 

주의할부분 : 나눗셈할때 정수끼리하면 소숫점자리가 안나온다.

 

public class Main {

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

    public static void main(String[] args) throws IOException {

        int n = Integer.parseInt(br.readLine());
        HashMap<Integer, Integer> numbersMap = new HashMap<>(); //숫자,빈도수 저장할 MAP -> 최대 빈도수 구할라고
        StringBuilder sb = new StringBuilder(); //결과 출력 저장할 Strinbuilder

        int sum =0; //총 합 저장
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        int maxTimes =0; // 최대 빈도수
        List<Integer> maxTimeValues = new ArrayList<>(); // 최대 빈도수만큼 나온 숫자들 저장할 리스트
        List<Integer> numberList = new ArrayList<>(); // 입력된 숫자 받는 리스트 -> 중앙값 구할라고

        for (int i = 0; i < n; i++) {
            int inputNumber = Integer.parseInt(br.readLine());
            numberList.add(inputNumber);
            if (numbersMap.containsKey(inputNumber)) { //해당 수가 이미 있는지 체크하고
                numbersMap.put(inputNumber, numbersMap.get(inputNumber) + 1); //있다면 빈도수 증가
            }
            else{
                numbersMap.put(inputNumber, 1); // 없다면 Map에 추가
            }
            sum += inputNumber; // 총 합 증가시킨다.
            if (max < inputNumber) { // 최대값인지 체크
                max = inputNumber;
            }
            if (min > inputNumber) { // 최소값인지 체크
                min = inputNumber;
            }
        }

        numberList.sort(Comparator.naturalOrder()); // 중간값을 구하기위해 전체 숫자리스트 정렬해주고

        Collection<Integer> values = numbersMap.values(); // 빈도수만 빼서
        List<Integer> valueList = new ArrayList<>(values); //리스트에 옮기고
        valueList.sort(Comparator.naturalOrder()); // 정렬해서
        maxTimes = valueList.get(valueList.size() - 1); // 가장 큰 빈도수를 구한다.

        for (Entry<Integer, Integer> entry : numbersMap.entrySet()) { // 가장 큰 빈도수를 가지는 숫자가 여러개일수있으므로 체크해본다.
            int currentValue = entry.getValue();
            int currentKey = entry.getKey();
            if (maxTimes == currentValue) { //가장 큰 빈도수인 숫자는 리스트에 저장한다.
                maxTimeValues.add(currentKey);
            }
        }

        maxTimeValues.sort(Comparator.naturalOrder());//max 빈도수인 숫자들을 정렬해주고



        sb.append((int)Math.round((double)sum/n)  + "\n"); //평균값 구해준다. double을 붙여줘야 소숫점으로 결과가 나오고, 그결과를 이용하여 반올림한후 정수형으로 바꾼다.
        sb.append(numberList.get(n / 2) + "\n"); // 중앙값 구한다.
        if (maxTimeValues.size() > 1) { //max 빈도수인 숫자가 2개이상인경우
            sb.append(maxTimeValues.get(1) + "\n"); // 두번째 작은값을 택하고
        }
        else{ // 아니라면
            sb.append(maxTimeValues.get(0) + "\n"); // 하나있는 그 값을 택한다.
        }1

        sb.append(max - min + "\n"); // 최댓값과 최솟값의 차이
        bw.write(sb.toString());

        bw.flush();
        bw.close();
    }
}

 

다른사람풀이

1.

같이 map을 이용했지만 Hashmap의 getOrDefault라는 메소드를 사용해서 더 깔끔한 코드를 만들어냈다.

최대값,최소값을 Collections.max(), Collections.min() 이라는 메소드를 이용했다.

+ List.clear()를 이용하여 리스트의 재활용을 해냈다 

 

다음정보를 이용하여 내 코드를 더 깔끔하게 리팩토링 해보았다.

수정된부분 

getOrDefault를 사용

Collections.max(), Collections.min()  메소드 사용

public class Main {

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

    public static void main(String[] args) throws IOException {

        int n = Integer.parseInt(br.readLine());
        HashMap<Integer, Integer> numbersMap = new HashMap<>(); //숫자,빈도수 저장할 MAP -> 최대 빈도수 구할라고
        StringBuilder sb = new StringBuilder(); //결과 출력 저장할 Strinbuilder

        int sum =0; //총 합 저장
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        int maxTimes =0; // 최대 빈도수
        List<Integer> maxTimeValues = new ArrayList<>(); // 최대 빈도수만큼 나온 숫자들 저장할 리스트
        List<Integer> numberList = new ArrayList<>(); // 입력된 숫자 받는 리스트 -> 중앙값 구할라고

        for (int i = 0; i < n; i++) {
            int inputNumber = Integer.parseInt(br.readLine());
            numberList.add(inputNumber);
            numbersMap.put(inputNumber, numbersMap.getOrDefault(inputNumber, 0)+1); //해당 숫자가 있다면 빈도수 +1, 없다면 빈도수 1로 map에 삽입
            sum += inputNumber; // 총 합 증가시킨다.
        }

        numberList.sort(Comparator.naturalOrder()); // 중간값을 구하기위해 전체 숫자리스트 정렬해주고

        max = Collections.max(numberList);
        min = Collections.min(numberList);


        Collection<Integer> values = numbersMap.values(); // 빈도수만 빼서
        List<Integer> valueList = new ArrayList<>(values); //리스트에 옮기고
        valueList.sort(Comparator.naturalOrder()); // 정렬해서
        maxTimes = valueList.get(valueList.size() - 1); // 가장 큰 빈도수를 구한다.-> 이 부분은 정렬을 이용하지않고 ,반복문으로 전체돌아서 빈도수 최대값을 구할수도 있다.

        for (Entry<Integer, Integer> entry : numbersMap.entrySet()) { // 가장 큰 빈도수를 가지는 숫자가 여러개 일수있으므로 체크해본다.
            int currentValue = entry.getValue();
            int currentKey = entry.getKey();
            if (maxTimes == currentValue) { //가장 큰 빈도수인 숫자는 리스트에 저장한다.
                maxTimeValues.add(currentKey);
            }
        }

        maxTimeValues.sort(Comparator.naturalOrder());//max 빈도수인 숫자들을 정렬해주고



        sb.append((int)Math.round((double)sum/n)  + "\n"); //평균값 구해준다. double을 붙여줘야 소숫점으로 결과가 나오고, 그결과를 이용하여 반올림한후 정수형으로 바꾼다.
        sb.append(numberList.get(n / 2) + "\n"); // 중앙값 구한다.
        if (maxTimeValues.size() > 1) { //max 빈도수인 숫자가 2개이상인경우
            sb.append(maxTimeValues.get(1) + "\n"); // 두번째 작은값을 택하고
        }
        else{ // 아니라면
            sb.append(maxTimeValues.get(0) + "\n"); // 하나있는 그 값을 택한다.
        }
        sb.append(max - min + "\n"); // 최댓값과 최솟값의 차이
        bw.write(sb.toString());

        bw.flush();
        bw.close();
    }
}

걸린시간이 2배가 되었는데 아무래도 Collections.max와 min 메소드때문인것 같다.

 

추가적으로 빈도수 최대값구하는부분도 values를 이용한 반복문을 통해 쉽게 구하는게 더 보기 편할거 같았다.

+ 리스트를 비우고 재활용하거나 + values를 받아서 리스트를 만드는부분을 한줄로 수정하면 더 코드가 깔끔할것 같다.

 

 

 

 

 

 

 

참고

https://velog.io/@lifeisbeautiful/Java-%EB%B0%B1%EC%A4%80-2108%EB%B2%88-%ED%86%B5%EA%B3%84%ED%95%99-%EC%9E%90%EB%B0%94

 

[Java] 백준 2108번 [통계학] 자바

[Java] 백준 2108번 [통계학] 자바

velog.io

 

 

 

 

 

 

 

 

 

 

 

 

'자바 > 알고리즘 문제 풀이' 카테고리의 다른 글

백준/11650 좌표 정렬하기  (1) 2022.12.04
백준/1427 소트인사이드  (0) 2022.12.04
백준/10989 수 정렬하기 3  (0) 2022.12.03
백준/2751 수 정렬하기 2  (0) 2022.12.03
백준/25305 커트라인  (0) 2022.12.03

댓글