본문 바로가기
Algorithm/프로그래머스

[프로그래머스]해시 : 베스트앨범 (level 3) (java)

by HBGB 2019. 12. 18.

https://programmers.co.kr/learn/courses/30/lessons/42579

 

코딩테스트 연습 - 베스트앨범 | 프로그래머스

스트리밍 사이트에서 장르 별로 가장 많이 재생된 노래를 두 개씩 모아 베스트 앨범을 출시하려 합니다. 노래는 고유 번호로 구분하며, 노래를 수록하는 기준은 다음과 같습니다. 속한 노래가 많이 재생된 장르를 먼저 수록합니다. 장르 내에서 많이 재생된 노래를 먼저 수록합니다. 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다. 노래의 장르를 나타내는 문자열 배열 genres와 노래별 재생 횟수를 나타내는 정수 배열 play

programmers.co.kr

 

 

방법1 : Comparable 활용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import java.util.*;
 
 
class Solution {
    public class Music implements Comparable<Music> {
 
        private int played;
        private String genre;
        private int index;
 
        public Music(String genre, int played, int index) {
            this.genre = genre;
            this.played = played;
            this.index = index;
        }
 
        @Override
        public int compareTo(Music other) {
            // 많이 재생된 장르를 먼저
            int iOrder = CountMap.get(other.genre) - CountMap.get(this.genre);
            if (iOrder != 0) {
                return iOrder;
            }
            // 많이 재생된 노래를 먼저
            if (iOrder == 0) {
                return other.played - this.played;
            }
            // 고유 번호가 낮은 노래를 먼저
            if (iOrder == 0) {
                return this.index - other.index;
            }
            return iOrder;
        }
    }
 
    Map<String, Integer> CountMap;
 
    public int[] solution(String[] genres, int[] plays) {
 
        CountMap = new HashMap<String, Integer>();
        List<Music> infoList = new ArrayList<Music>();
 
        // infoList : 노래 정보  
        // CountMap : 장르별 재생횟수 카운트
        for (int i = 0; i < genres.length; i++) {
 
            String genre = genres[i];
            int played = plays[i];
 
            Music music = new Music(genre, played, i);
            infoList.add(music);
 
            if (CountMap.containsKey(genre)) {
                CountMap.put(genre, CountMap.get(genre) + played);
                continue;
            }
            CountMap.put(genre, played);
        }
 
        // 주어진 조건대로 정렬
        Collections.sort(infoList);
 
        // 장르별 최대 2곡씩 선택
        return pickSongs(infoList);
    }
 
    private int[] pickSongs(List<Music> infoList) {
 
        int count = 1;
        for (int i = 1; i < infoList.size(); i++) {
            Music music1 = infoList.get(i);
            Music music2 = infoList.get(i - 1);
 
            if (count >= 2) {
                if (music1.genre.equals(music2.genre)) {
                    infoList.remove(i);
                    i--;
                    continue;
                }
                count = 0;
            }
            if (!music1.genre.equals(music2.genre)) {
                count = 1;
                continue;
            }
            if (count < 2) {
                count++;
            }
        }
 
        // 리스트 -> 배열
        int[] answer = new int[infoList.size()];
 
        for (int i = 0; i < answer.length; i++) {
            answer[i] = infoList.get(i).index;
        }
        return answer;
    }
}
Colored by Color Scripter

 

방법2: Comparator 활용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import java.util.*;
import java.util.Map.Entry;
 
class Solution {
    public int[] solution(String[] genres, int[] plays) {
 
        Map<String, Integer> CountMap = new HashMap<String, Integer>();
        Map<Integer, Integer> playsMap = new HashMap<Integer, Integer>();
        
        // playsMap : plays 배열 -> 맵
        // CountMap : 장르별 재생횟수 카운트
        for (int i = 0; i < genres.length; i++) {
 
            String key = genres[i];
            playsMap.put(i, plays[i]);
 
            if (CountMap.containsKey(key)) {
                CountMap.put(key, CountMap.get(key) + plays[i]);
                continue;
            }
            CountMap.put(key, plays[i]);
        }
 
        // 주어진 조건대로 정렬
        List<Map.Entry<Integer, Integer>> playsList = new LinkedList<>(playsMap.entrySet());
 
        Collections.sort(playsList, new Comparator<Map.Entry<Integer, Integer>>() {
 
            @Override
            public int compare(Entry<Integer, Integer> o1, Entry<Integer, Integer> o2) {
 
                // 많이 재생된 장르를 먼저
                int iOrder = CountMap.get(genres[o2.getKey()]) - CountMap.get(genres[o1.getKey()]);
                // 많이 재생된 노래를 먼저
                iOrder = (iOrder == 0) ? o2.getValue() - o1.getValue() : iOrder;
                // 고유 번호가 낮은 노래를 먼저
                iOrder = (iOrder == 0) ? o1.getKey() - o2.getKey() : iOrder;
 
                return iOrder;
            }
        });
 
        // 장르별 최대 2곡씩 선택
        int count = 0;
        List<Integer> liAnswer = new ArrayList<Integer>();
        
        Iterator<Map.Entry<Integer, Integer>> iter = playsList.iterator();
        while (iter.hasNext()) {
            Map.Entry<Integer, Integer> entry = iter.next();
            
            if (count >= 2) {
                if (genres[liAnswer.get(liAnswer.size() - 1)].equals(genres[entry.getKey()])) {
                    continue;
                }
                count = 0;
            } 
            
            if (liAnswer.size() > 0 
                    && !genres[liAnswer.get(liAnswer.size() - 1)].equals(genres[entry.getKey()])) {
                count = 0;
            }
            
            if (count < 2) {
                liAnswer.add(entry.getKey());
                count++;
            }
        }
 
        // 리스트 -> 배열
        int[] answer = new int[liAnswer.size()];
        
        for (int i = 0; i < answer.length; i++) {
            answer[i] = liAnswer.get(i);
        }
        
        return answer;
    }
}
Colored by Color Scripter

 

 

속도차이는 크게 나지 않는다.

하지만 방법1이 훨씬 깔끔하다.

 

정렬이 포인트인 문제였는데,

이번 문제로 어떨 때 Comparable과 Comparator을 쓰는지 감을 잡았다. 

 

 

TIP1

정보가 3개 이상 쓰일 때는 따로 클래스를 만들어서 담아주자

이 문제에서 노래 하나에 연관된 정보는 인덱스값, 장르, 플레이횟수로 총 3개이다. 

 

이것을 map과 주어진 배열로만 푼게 방법 2번인데, 

코드가 상당히 지저분해졌다. 

방법1처럼 <노래 = Music 객체> 이렇게 만들어서

리스트에 담아주니 훨씬 깔끔해졌다. 

 

장르별 총 플레이 횟수도 Music 객체에 담을 수도 있었겠지만, 

정보의 완결성(?)을 침해하고 2중 for문이 필요할 것 같아서 

장르별 플레이 횟수는 따로 HashMap으로 담았다. 

그리고 어쨌든 Hash 문제이니깐... 

 

 

TIP2

Comparable과 Comparator 구현 방식 차이

 

Comparable방식도 compareTo 메소드를 오버라이드 해서 구현한다. 

이 때, 전역변수 this = o1, 매개변수 other = o2로 생각해서 순서를 구현하면 된다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//1. Comparable
public class Music implements Comparable<Music> {
    
    @Override
    public int compareTo(Music other) {
        // 많이 재생된 장르를 먼저
        int iOrder = CountMap.get(other.genre) - CountMap.get(this.genre);
        if (iOrder != 0) {
            return iOrder;
        }
        // 많이 재생된 노래를 먼저
        if (iOrder == 0) {
            return other.played - this.played;
        }
        // 고유 번호가 낮은 노래를 먼저
        if (iOrder == 0) {
            return this.index - other.index;
        }
        return iOrder;
    }
}
 
//2. Comparator
Collections.sort(playsList, new Comparator<Map.Entry<Integer, Integer>>() {
 
    @Override
    public int compare(Entry<Integer, Integer> o1, Entry<Integer, Integer> o2) {
 
        // 많이 재생된 장르를 먼저
        int iOrder = CountMap.get(genres[o2.getKey()]) - CountMap.get(genres[o1.getKey()]);
        // 많이 재생된 노래를 먼저
        iOrder = (iOrder == 0) ? o2.getValue() - o1.getValue() : iOrder;
        // 고유 번호가 낮은 노래를 먼저
        iOrder = (iOrder == 0) ? o1.getKey() - o2.getKey() : iOrder;
 
        return iOrder;
    }
});
Colored by Color Scripter

 

 

 

 

댓글