Algorithm/Programmers

[programmers lv3.python] 베스트 앨범 풀이

쉬지마 이굥진 2024. 3. 8. 01:28

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

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr


문제

입출력 예

 

꾸준히 하고 있는 알고리즘 스터디에서 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. 해시 만들기
     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)]


  2. playDic 을, 재생 횟수가 내림차순 대로 장르별 정렬한다.
     1) playDic을 items() 함수를 이용해, playDic 딕셔너리의 모든 키-값 쌍을 튜플로 만든다. 

     2) sorted() 함수를 사용하여 각 튜플의 두 번째 요소인 재생 횟수를 기준으로 정렬한다.
     3) reverse = True 를 통해 내림차순으로 정렬한다.  (따라서 가장 많이 재생된 장르가 먼저 나오게 된다)
     4) 정렬된 결과는 genreSort 리스트에 저장된다. 따라서 이 리스트엔 장르 별로 총 재생 횟수가 내림차순으로 정렬된 튜플들이 저장되게 된다.
        # 재생 횟수 내림차순으로 장르 별 정렬
        genreSort = sorted(playDic.items(), key = lambda x: x[1], reverse = True)


  3. 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 함수도 알게 되었으니,

다음 해시에 관한 문제를 풀 때 유념하면서 풀 수 있을 것 같다. 

아는 게 힘이다 !!!