

1. 문제설명
스트리밍 사이트에서 장르 별로 가장 많이 재생된 노래를 두 개씩 모아 베스트 앨범을 출시하려 합니다. 노래는 고유 번호로 구분하며, 노래를 수록하는 기준은 다음과 같습니다.
- 속한 노래가 많이 재생된 장르를 먼저 수록합니다.
- 장르 내에서 많이 재생된 노래를 먼저 수록합니다.
- 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다.
노래의 장르를 나타내는 문자열 배열 genres와 노래별 재생 횟수를 나타내는 정수 배열 plays가 주어질 때, 베스트 앨범에 들어갈 노래의 고유 번호를 순서대로 return 하도록 solution 함수를 완성하세요.
제한사항
- genres[i]는 고유번호가 i인 노래의 장르입니다.
- plays[i]는 고유번호가 i인 노래가 재생된 횟수입니다.
- genres와 plays의 길이는 같으며, 이는 1 이상 10,000 이하입니다.
- 장르 종류는 100개 미만입니다.
- 장르에 속한 곡이 하나라면, 하나의 곡만 선택합니다.
- 모든 장르는 재생된 횟수가 다릅니다.
입출력 예
gernes | plays | return |
["classic", "pop", "classic", "classic", "pop"] |
[500, 600, 150, 800, 2500] | [4, 1, 3, 0] |
classic 장르는 1,450회 재생되었으며, classic 노래는 다음과 같습니다.
- 고유 번호 3: 800회 재생
- 고유 번호 0: 500회 재생
- 고유 번호 2: 150회 재생
pop 장르는 3,100회 재생되었으며, pop 노래는 다음과 같습니다.
- 고유 번호 4: 2,500회 재생
- 고유 번호 1: 600회 재생
따라서 pop 장르의 [4, 1]번 노래를 먼저, classic 장르의 [3, 0]번 노래를 그다음에 수록합니다.
- 장르 별로 가장 많이 재생된 노래를 최대 두 개까지 모아 베스트 앨범을 출시하므로 2번 노래는 수록되지 않습니다.
2. 접근방식
2-1. 문제 단순화
- 음악 장르가 담긴 String 타입의 배열과 재생 횟수가 담긴 Integer 배열이 있다.
- 장르별로 가장 많이 재생된 음악과 두 번째로 많이 재생된 음악을 베스트 앨범에 싣는다. => 장르별로 1,2위 두 곡씩 선발
2-2. 해결책
- 장르별 재생횟수 저장
HashMap<String, Integer> num
classic | pop |
800+500+150 | 2,500+600 |
장르에 속한 노래의 <인덱스 번호, 재생 횟수> 저장
- HashMap<String, HashMap<Integer, Integer>> music
classic | pop |
[0:500, 2:150, 3:800] | [1:600, 4:2,500] |
속한 노래가 가장 많은 장르를 먼저 재생해야 하므로, num을 총 재생횟수가 많은 순으로 내림차순 정렬
pop | classic |
3,100 | 1,450 |
장르 내에서 가장 많이 재생된 노래를 추출해야 하므로, music의 재생횟수대로 내림차순 정렬
classic | pop |
[3:800, 0:500, 2:150] | [4:2,500, 1:600] |
정렬된 num의 순서대로 music에서 정렬된 곡들 중 상위 2곡의 인덱스 번호를 album(ArrayList)에 추가
ArrayList<Integer> album
=> 4,1,3,0 이 추가됨
ArrayList를 배열로 바꾸어 리턴
[4, 1, 3, 0]
3. 문제풀이
import java.util.*;
class Solution {
public int[] solution(String[] genres, int[] plays) {
// 1. 베스트앨범에 들어갈 노래 담을 ArrayList 생성
ArrayList<Integer> album = new ArrayList<>();
// 2. 장르별 총 재생횟수 저장할 HashMap num 선언
HashMap<String, Integer> num = new HashMap<>();
// 3. 장르별로 각 노래(고유 인덱스 번호)의 재생횟수를
// 저장할 HashMap music 선언
HashMap<String, HashMap<Integer, Integer>> music = new HashMap<>();
for(int i = 0; i < genres.length; i++) {
// genres 배열을 순회하며 각 노래에 대한 정보를 수집
if(!num.containsKey(genres[i])) {
HashMap<Integer, Integer> map = new HashMap<>();
map.put(i, plays[i]); // 새로운 노래 인덱스와 재생 횟수 추가
music.put(genres[i], map); // 새로운 장르와 노래 정보를 추가
num.put(genres[i], plays[i]); // 해당 장르의 총 재생 횟수를 초기화
} else {
music.get(genres[i]).put(i, plays[i]);
// 기존 장르에 노래 인덱스와 재생 횟수 추가
num.put(genres[i], num.get(genres[i]) + plays[i]);
// 장르별 총 재생 횟수 누적
}
}
// 4. 정렬을 위해서 num의 KeySet(장르명)을 리스트로 변환
List<String> keySet = new ArrayList<>(num.keySet());
Collections.sort(keySet, (s1, s2) -> num.get(s2) - num.get(s1));
// 장르별 총 재생 횟수에 따라 내림차순 정렬
for (String key : keySet) {
HashMap<Integer, Integer> map = music.get(key);
List<Integer> genreKey = new ArrayList<>(map.keySet());
// 재생 횟수에 따라 노래 인덱스 정렬
Collections.sort(genreKey, (s1, s2) -> map.get(s2) - map.get(s1));
album.add(genreKey.get(0)); // 첫 번째 곡 추가
if(genreKey.size() > 1) {
album.add(genreKey.get(1)); // 두 번째 곡 추가 (있을 경우)
}
}
// ArrayList를 int[]로 변환하여 반환
return album.stream().mapToInt(i -> i).toArray();
}
}
num 해시맵에서 총 재생 횟수가 큰 장르 순서대로 정렬한 값을 받아오는 keySet 리스트
List<String> keySet = new ArrayList<>(num.keySet());
Collections.sort(keySet, (s1, s2) -> num.get(s2) - num.get(s1));
- List<String> keySet = new ArrayList<>(num.keySet());
- num.keySet()은 num이라는 해시맵의 키들을 가져옴. => 각 장르명을 포함하는 Set<String>이 반환됨
- 이 키들을 ArrayList로 변환하여 keySet 리스트에 저장한 것.
- Collections.sort(keySet, (s1, s2) -> num.get(s2) - num.get(s1));:
- Collections.sort() 메서드로 keySet을 정렬
- (s1, s2) -> num.get(s2) - num.get(s1)는 람다식으로, 장르명을 비교해 내림차순으로 정렬
- s1, s2는 genreKey 리스트의 인덱스
- num.get(s1)와 num.get(s2)는 두 장르 s1과 s2의 총 재생 횟수
- num.get(s2) - num.get(s1)는 두 장르 s1과 s2의 총 재생 횟수 차이를 구하여, 재생 횟수가 큰 장르가 앞에 오도록 내림차순 정렬합니다. <-> num.get(s1) - num.get(s2)은 오름차순 정렬
- 양수: 만약 map.get(s2)가 map.get(s1)보다 크면 (즉, s2의 재생 횟수가 더 크면), 결과는 양수가 됩니다. 이 경우, s2가 s1보다 앞에 오도록 정렬 => 그래서 내림차순
- 음수: 반대로, map.get(s1)이 map.get(s2)보다 크면, 결과는 음수가 되어 s1이 s2보다 앞에 옴.
- 0 : 두 재생 횟수가 같으면 결과는 0이 되어 두 요소의 순서는 변경 안 됨.
return album.stream().mapToInt(i -> i).toArray();
문제에서 배열 형식으로 값을 리턴하므로, album이라는 ArrayList를 int 배열로 변환
3. album.stream():
- album은 ArrayList<Integer> 형식입니다. stream() 메서드는 이 리스트를 스트림으로 변환
4. mapToInt(i -> i):
- 스트림의 각 요소를 int 형식으로 변환(매핑) : mapToInt(Integer::intValue)
- 여기서 i는 album 리스트의 각 요소
- i -> i는 Integer 객체를 int 타입으로 변환하는 람다 표현식
5. toArray():
- 스트림의 요소들을 배열로 수집
- mapToInt를 사용하여 int 타입으로 변환된 스트림이 int[] 배열로 변환됨.
- 즉 toArray()는 int 배열을 반환
4. 같은 유형 문제 (해시맵)
[프로그래머스] (Java) 의상 해시 문제 풀이 (Iterator, getOrDefault 메서드)
1. 문제설명코니는 매일 다른 옷을 조합하여 입는것을 좋아합니다. 예를 들어 코니가 가진 옷이 아래와 같고, 오늘 코니가 동그란 안경, 긴 코트, 파란색 티셔츠를 입었다면 다음날은 청바지를
awesomepossum.tistory.com
[프로그래머스] (Java) 전화번호 목록 해시 문제 풀이 (substring, starsWith 문자열 메서드)
1. 문제설명 전화번호부에 적힌 전화번호 중, 한 번호가 다른 번호의 접두어인 경우가 있는지 확인하려 합니다.전화번호가 다음과 같을 경우, 구조대 전화번호는 영석이의 전화번호의 접두사입
awesomepossum.tistory.com
[프로그래머스] (Java) 코딩테스트 완주하지 못한선수 문제풀이 🚴♂️
❤️ 문제설명수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.마라톤에 참여한 선수들의 이름이 담긴 배열 participant와
awesomepossum.tistory.com
[프로그래머스] (Java) 코딩테스트 폰켓몬 문제풀이 🐣
❤️ 문제설명 당신은 폰켓몬을 잡기 위한 오랜 여행 끝에, 홍 박사님의 연구실에 도착했습니다. 홍 박사님은 당신에게 자신의 연구실에 있는 총 N 마리의 폰켓몬 중에서 N/2마리를 가져가도 좋
awesomepossum.tistory.com
'Algorithm > Programmers_Best' 카테고리의 다른 글
[프로그래머스] (Java) 가장 큰 수 문제 풀이 (정렬) - compareTo() 메서드 (3) | 2024.11.02 |
---|---|
[프로그래머스] (Java) K번째수 문제 풀이 (정렬) - Arrays.copyOfRange() 메서드 (5) | 2024.11.02 |
[프로그래머스] (Java) 의상 문제 풀이 (해시) - Iterator, getOrDefault 메서드 (7) | 2024.10.31 |
[프로그래머스] (Java) 전화번호 목록 문제 풀이 (해시) - substring, starsWith 메서드 (4) | 2024.10.31 |
[프로그래머스] (Java) 코딩테스트 완주하지 못한선수 문제풀이 🚴♂️ (해시) - map.getOrDefault() (2) | 2024.10.29 |


1. 문제설명
스트리밍 사이트에서 장르 별로 가장 많이 재생된 노래를 두 개씩 모아 베스트 앨범을 출시하려 합니다. 노래는 고유 번호로 구분하며, 노래를 수록하는 기준은 다음과 같습니다.
- 속한 노래가 많이 재생된 장르를 먼저 수록합니다.
- 장르 내에서 많이 재생된 노래를 먼저 수록합니다.
- 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다.
노래의 장르를 나타내는 문자열 배열 genres와 노래별 재생 횟수를 나타내는 정수 배열 plays가 주어질 때, 베스트 앨범에 들어갈 노래의 고유 번호를 순서대로 return 하도록 solution 함수를 완성하세요.
제한사항
- genres[i]는 고유번호가 i인 노래의 장르입니다.
- plays[i]는 고유번호가 i인 노래가 재생된 횟수입니다.
- genres와 plays의 길이는 같으며, 이는 1 이상 10,000 이하입니다.
- 장르 종류는 100개 미만입니다.
- 장르에 속한 곡이 하나라면, 하나의 곡만 선택합니다.
- 모든 장르는 재생된 횟수가 다릅니다.
입출력 예
gernes | plays | return |
["classic", "pop", "classic", "classic", "pop"] |
[500, 600, 150, 800, 2500] | [4, 1, 3, 0] |
classic 장르는 1,450회 재생되었으며, classic 노래는 다음과 같습니다.
- 고유 번호 3: 800회 재생
- 고유 번호 0: 500회 재생
- 고유 번호 2: 150회 재생
pop 장르는 3,100회 재생되었으며, pop 노래는 다음과 같습니다.
- 고유 번호 4: 2,500회 재생
- 고유 번호 1: 600회 재생
따라서 pop 장르의 [4, 1]번 노래를 먼저, classic 장르의 [3, 0]번 노래를 그다음에 수록합니다.
- 장르 별로 가장 많이 재생된 노래를 최대 두 개까지 모아 베스트 앨범을 출시하므로 2번 노래는 수록되지 않습니다.
2. 접근방식
2-1. 문제 단순화
- 음악 장르가 담긴 String 타입의 배열과 재생 횟수가 담긴 Integer 배열이 있다.
- 장르별로 가장 많이 재생된 음악과 두 번째로 많이 재생된 음악을 베스트 앨범에 싣는다. => 장르별로 1,2위 두 곡씩 선발
2-2. 해결책
- 장르별 재생횟수 저장
HashMap<String, Integer> num
classic | pop |
800+500+150 | 2,500+600 |
장르에 속한 노래의 <인덱스 번호, 재생 횟수> 저장
- HashMap<String, HashMap<Integer, Integer>> music
classic | pop |
[0:500, 2:150, 3:800] | [1:600, 4:2,500] |
속한 노래가 가장 많은 장르를 먼저 재생해야 하므로, num을 총 재생횟수가 많은 순으로 내림차순 정렬
pop | classic |
3,100 | 1,450 |
장르 내에서 가장 많이 재생된 노래를 추출해야 하므로, music의 재생횟수대로 내림차순 정렬
classic | pop |
[3:800, 0:500, 2:150] | [4:2,500, 1:600] |
정렬된 num의 순서대로 music에서 정렬된 곡들 중 상위 2곡의 인덱스 번호를 album(ArrayList)에 추가
ArrayList<Integer> album
=> 4,1,3,0 이 추가됨
ArrayList를 배열로 바꾸어 리턴
[4, 1, 3, 0]
3. 문제풀이
import java.util.*; class Solution { public int[] solution(String[] genres, int[] plays) { // 1. 베스트앨범에 들어갈 노래 담을 ArrayList 생성 ArrayList<Integer> album = new ArrayList<>(); // 2. 장르별 총 재생횟수 저장할 HashMap num 선언 HashMap<String, Integer> num = new HashMap<>(); // 3. 장르별로 각 노래(고유 인덱스 번호)의 재생횟수를 // 저장할 HashMap music 선언 HashMap<String, HashMap<Integer, Integer>> music = new HashMap<>(); for(int i = 0; i < genres.length; i++) { // genres 배열을 순회하며 각 노래에 대한 정보를 수집 if(!num.containsKey(genres[i])) { HashMap<Integer, Integer> map = new HashMap<>(); map.put(i, plays[i]); // 새로운 노래 인덱스와 재생 횟수 추가 music.put(genres[i], map); // 새로운 장르와 노래 정보를 추가 num.put(genres[i], plays[i]); // 해당 장르의 총 재생 횟수를 초기화 } else { music.get(genres[i]).put(i, plays[i]); // 기존 장르에 노래 인덱스와 재생 횟수 추가 num.put(genres[i], num.get(genres[i]) + plays[i]); // 장르별 총 재생 횟수 누적 } } // 4. 정렬을 위해서 num의 KeySet(장르명)을 리스트로 변환 List<String> keySet = new ArrayList<>(num.keySet()); Collections.sort(keySet, (s1, s2) -> num.get(s2) - num.get(s1)); // 장르별 총 재생 횟수에 따라 내림차순 정렬 for (String key : keySet) { HashMap<Integer, Integer> map = music.get(key); List<Integer> genreKey = new ArrayList<>(map.keySet()); // 재생 횟수에 따라 노래 인덱스 정렬 Collections.sort(genreKey, (s1, s2) -> map.get(s2) - map.get(s1)); album.add(genreKey.get(0)); // 첫 번째 곡 추가 if(genreKey.size() > 1) { album.add(genreKey.get(1)); // 두 번째 곡 추가 (있을 경우) } } // ArrayList를 int[]로 변환하여 반환 return album.stream().mapToInt(i -> i).toArray(); } }
num 해시맵에서 총 재생 횟수가 큰 장르 순서대로 정렬한 값을 받아오는 keySet 리스트
List<String> keySet = new ArrayList<>(num.keySet()); Collections.sort(keySet, (s1, s2) -> num.get(s2) - num.get(s1));
- List<String> keySet = new ArrayList<>(num.keySet());
- num.keySet()은 num이라는 해시맵의 키들을 가져옴. => 각 장르명을 포함하는 Set<String>이 반환됨
- 이 키들을 ArrayList로 변환하여 keySet 리스트에 저장한 것.
- Collections.sort(keySet, (s1, s2) -> num.get(s2) - num.get(s1));:
- Collections.sort() 메서드로 keySet을 정렬
- (s1, s2) -> num.get(s2) - num.get(s1)는 람다식으로, 장르명을 비교해 내림차순으로 정렬
- s1, s2는 genreKey 리스트의 인덱스
- num.get(s1)와 num.get(s2)는 두 장르 s1과 s2의 총 재생 횟수
- num.get(s2) - num.get(s1)는 두 장르 s1과 s2의 총 재생 횟수 차이를 구하여, 재생 횟수가 큰 장르가 앞에 오도록 내림차순 정렬합니다. <-> num.get(s1) - num.get(s2)은 오름차순 정렬
- 양수: 만약 map.get(s2)가 map.get(s1)보다 크면 (즉, s2의 재생 횟수가 더 크면), 결과는 양수가 됩니다. 이 경우, s2가 s1보다 앞에 오도록 정렬 => 그래서 내림차순
- 음수: 반대로, map.get(s1)이 map.get(s2)보다 크면, 결과는 음수가 되어 s1이 s2보다 앞에 옴.
- 0 : 두 재생 횟수가 같으면 결과는 0이 되어 두 요소의 순서는 변경 안 됨.
return album.stream().mapToInt(i -> i).toArray();
문제에서 배열 형식으로 값을 리턴하므로, album이라는 ArrayList를 int 배열로 변환
3. album.stream():
- album은 ArrayList<Integer> 형식입니다. stream() 메서드는 이 리스트를 스트림으로 변환
4. mapToInt(i -> i):
- 스트림의 각 요소를 int 형식으로 변환(매핑) : mapToInt(Integer::intValue)
- 여기서 i는 album 리스트의 각 요소
- i -> i는 Integer 객체를 int 타입으로 변환하는 람다 표현식
5. toArray():
- 스트림의 요소들을 배열로 수집
- mapToInt를 사용하여 int 타입으로 변환된 스트림이 int[] 배열로 변환됨.
- 즉 toArray()는 int 배열을 반환
4. 같은 유형 문제 (해시맵)
[프로그래머스] (Java) 의상 해시 문제 풀이 (Iterator, getOrDefault 메서드)
1. 문제설명코니는 매일 다른 옷을 조합하여 입는것을 좋아합니다. 예를 들어 코니가 가진 옷이 아래와 같고, 오늘 코니가 동그란 안경, 긴 코트, 파란색 티셔츠를 입었다면 다음날은 청바지를
awesomepossum.tistory.com
[프로그래머스] (Java) 전화번호 목록 해시 문제 풀이 (substring, starsWith 문자열 메서드)
1. 문제설명 전화번호부에 적힌 전화번호 중, 한 번호가 다른 번호의 접두어인 경우가 있는지 확인하려 합니다.전화번호가 다음과 같을 경우, 구조대 전화번호는 영석이의 전화번호의 접두사입
awesomepossum.tistory.com
[프로그래머스] (Java) 코딩테스트 완주하지 못한선수 문제풀이 🚴♂️
❤️ 문제설명수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.마라톤에 참여한 선수들의 이름이 담긴 배열 participant와
awesomepossum.tistory.com
[프로그래머스] (Java) 코딩테스트 폰켓몬 문제풀이 🐣
❤️ 문제설명 당신은 폰켓몬을 잡기 위한 오랜 여행 끝에, 홍 박사님의 연구실에 도착했습니다. 홍 박사님은 당신에게 자신의 연구실에 있는 총 N 마리의 폰켓몬 중에서 N/2마리를 가져가도 좋
awesomepossum.tistory.com
'Algorithm > Programmers_Best' 카테고리의 다른 글
[프로그래머스] (Java) 가장 큰 수 문제 풀이 (정렬) - compareTo() 메서드 (3) | 2024.11.02 |
---|---|
[프로그래머스] (Java) K번째수 문제 풀이 (정렬) - Arrays.copyOfRange() 메서드 (5) | 2024.11.02 |
[프로그래머스] (Java) 의상 문제 풀이 (해시) - Iterator, getOrDefault 메서드 (7) | 2024.10.31 |
[프로그래머스] (Java) 전화번호 목록 문제 풀이 (해시) - substring, starsWith 메서드 (4) | 2024.10.31 |
[프로그래머스] (Java) 코딩테스트 완주하지 못한선수 문제풀이 🚴♂️ (해시) - map.getOrDefault() (2) | 2024.10.29 |