https://school.programmers.co.kr/learn/courses/30/lessons/42579
문제
입출력 예
꾸준히 하고 있는 알고리즘 스터디에서 lv 2 까지의 문제들은 풀이를 보지 않아도 혼자서 얼추 풀 수 있었던 문제들이 대부분이었다. 모르겠는 문제의 풀이를 찾아봤을 때 이해가 바로바로 되기도 했었고 🥲
이 문제를 읽고 이해한 후, 어느 정도 해시를 써서 풀어야 겠다는 생각은 바로 했지만Dictionary 2개를 사용하는 문제였기 때문에 헷갈려서 문제 풀이를 많이 해맸다. 결국 풀이 + 스터디원의 설명을 듣고 이해했고(thanks to 준익지훈)
처음 알게된 딕셔너리 함수도 있고, 간만에 풀이 보자마자 아 이거구나! 무릎탁 쳤던 문제가 아니라 앞으로 비슷한 문제를 보면 꼭 내힘으로 풀으리 .. 하는 마음가짐으로 코드 분석까지 하나하나 포스팅해본다 !!!!!
정답 코드
def solution(genres, plays):
answer = []
playDic = {} # {장르: 총 재생 횟수}
dic = {} # {장르: [(플레이 횟수, 고유번호)]}
# 해시 만들기
for i in range(len(genres)):
playDic[genres[i]] = playDic.get(genres[i], 0) + plays[i]
dic[genres[i]] = dic.get(genres[i], []) + [(plays[i], i)]
# 재생 횟수 내림차순으로 장르 별 정렬
genreSort = sorted(playDic.items(), key = lambda x: x[1], reverse = True)
# 재생 횟수 내림차순, 인덱스 오름차순 정렬
# 장르 별로 최대 2개 선택
for (genre, totalPlay) in genreSort:
dic[genre] = sorted(dic[genre], key = lambda x: (-x[0], x[1]))
answer += [idx for (play, idx) in dic[genre][:2]]
return answer
코드 분석
- 해시 만들기
1) playDic : 각 장르의 총 재생 횟수를 저장하는 해시를 만든다. (key : 각 장르, value : 해당 장르의 총 재생 횟수)
- ' playDic.get(genres[i], 0) '을 사용하여 해당 장르의 총 재생 횟수를 가져온다.
- 만약 해당 장르가 딕셔너리에 없다면, 초기값으로 0을 반환한다.
- 그 후, 현재 노래의 재생 횟수 ('plays[i]') 를 더해서, 각 장르별로 누적된 총 재생 횟수를 구한다.
2) dic : 각 장르에 속한 노래들의 정보를 저장하는 해시를 만든다. (key : 장르, value : 해당 장르에 속한 노래들의 리스트)
- ' dic.get(genres[i], []) ' 을 사용하여 해당 장르에 속한 노래들의 리스트를 가져온다.
- 만약 해당 장르가 딕셔너리에 없다면, 초기값으로 빈 리스트 ([]) 를 반환한다.
- 그 후, 현재 노래의 정보(재생 횟수와 인덱스) ( '[(plays[i], i)]' ) 를 리스트에 더해서, dic[genres[i]] (해당 장르의 노래들 리스트)에 저장한다.
# 해시 만들기 for i in range(len(genres)): playDic[genres[i]] = playDic.get(genres[i], 0) + plays[i] dic[genres[i]] = dic.get(genres[i], []) + [(plays[i], i)]
- playDic 을, 재생 횟수가 내림차순 대로 장르별 정렬한다.
1) playDic을 items() 함수를 이용해, playDic 딕셔너리의 모든 키-값 쌍을 튜플로 만든다.
2) sorted() 함수를 사용하여 각 튜플의 두 번째 요소인 재생 횟수를 기준으로 정렬한다.
3) reverse = True 를 통해 내림차순으로 정렬한다. (따라서 가장 많이 재생된 장르가 먼저 나오게 된다)
4) 정렬된 결과는 genreSort 리스트에 저장된다. 따라서 이 리스트엔 장르 별로 총 재생 횟수가 내림차순으로 정렬된 튜플들이 저장되게 된다.
# 재생 횟수 내림차순으로 장르 별 정렬 genreSort = sorted(playDic.items(), key = lambda x: x[1], reverse = True)
- dic 을 재생 횟수 내림차순 & 인덱스 오름차순 대로 (장르 별로 2개를 선택해야 하기 때문에) 정렬한다.
1) 2번에서 언급한 genreSort 리스트의 각 요소를 순회한다.
2) sorted() 함수를 사용해 dic[genre] 리스트를 정렬할 거다. 여기서 'key' 매개변수에는 정렬 기준을 지정하는 함수가 주어지는데, 여기선 람다 함수를 사용해서 재생 횟수와 인덱스를 기준으로 정렬한다.
- '-x[0]' : 재생 횟수를 기준으로 내림차순으로 정렬
- 'x[1]' : 재생 횟수가 같을 경우, 인덱스 (고유번호)를 기준으로 오름차순으로 정렬
3) 정렬된 결과에서 슬라이싱(:2)으로 최대 2개의 노래를 선택해서 answer 리스트에 추가해준다. 최종적으론 선택된 노래의 고유번호를 반환해야 하므로, answer 리스트에 선택된 노래의 고유번호도 추가한다.
4) 모든 장르에 대해 위 과정을 반복한 후, answer 리스트를 반환한다.
# 재생 횟수 내림차순, 인덱스 오름차순 정렬 # 장르 별로 최대 2개 선택 for (genre, totalPlay) in genreSort: dic[genre] = sorted(dic[genre], key = lambda x: (-x[0], x[1])) answer += [idx for (play, idx) in dic[genre][:2]] return answer
이 문제를 통해 알게된 함수 w/ 예시 코드
- 딕셔너리의 get() 함수
get() 함수는 딕셔너리에서 주어진 키에 대한 값을 반환한다. 만약 해당 키가 존재하지 않으면 '기본값' 을 반환한다.
이 말만 들어선 잘 감이 안 잡힐 것이다.
예시 코드로, 'my_dict' 라는 딕셔너리가 있다고 가정해보자.
my_dict = {'a': 1, 'b': 2, 'c': 3}
이때, get() 함수를 사용하여 특정 키에 대한 값을 가져올 수 있다.
value_a = my_dict.get('a') # 'a' 키에 대한 값인 1을 반환
print(value_a) # 출력: 1
value_d = my_dict.get('d') # 'd' 키는 존재하지 않으므로 기본값인 None을 반환
print(value_d) # 출력: None
value_d = my_dict.get('d', 0) # 'd' 키는 존재하지 않지만 기본값으로 0을 설정하여 반환
print(value_d) # 출력: 0
위 예시에서 get() 함수는 주어진 키에 대한 값을 반환하거나, 해당 키가 존재하지 않을 경우에는 기본값을 반환한다.
이를 통해 딕셔너리에서 특정 키가 있는지 확인하고, 해당 키에 대한 값을 안전하게 가져올 수 있다.
마치며
나는 아직 멀었구나 ~ 느끼게 해 준 문제다 ㅎ
그래도 이렇게 포스팅하면서 풀이를 자세히 분석해보고, 몰랐던 딕셔너리의 get 함수도 알게 되었으니,
다음 해시에 관한 문제를 풀 때 유념하면서 풀 수 있을 것 같다.
아는 게 힘이다 !!!
'Algorithm > Programmers' 카테고리의 다른 글
[programmers] 피자 나눠 먹기 (2) 파이썬 풀이 (1) | 2024.06.13 |
---|---|
[programmers lv1.python] 기사단원의 무기 풀이 (0) | 2024.05.16 |
[programmers/python] 무작위로 k개의 수 뽑기 풀이 (0) | 2024.05.14 |
[programmers lv2.python] 롤케이크 자르기 풀이 (2) | 2024.04.30 |
[programmers lv2.python] 이진 변환 반복하기 풀이 (리팩토링 다..수..) (2) | 2024.04.29 |