Algorithm

[프로그래머스] Lv.2 귤 고르기 | Python

chae_on 2023. 6. 7. 22:01
반응형

 

프로그래머스 - 코딩테스트 연습 - 귤 고르기(138476)

 

프로그래머스

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

programmers.co.kr


Lv2. 귤 고르기 (파이썬 문제 풀이)


문제 설명

경화는 과수원에서 귤을 수확했습니다. 경화는 수확한 귤 중 'k'개를 골라 상자 하나에 담아 판매하려고 합니다. 그런데 수확한 귤의 크기가 일정하지 않아 보기에 좋지 않다고 생각한 경화는 귤을 크기별로 분류했을 때 서로 다른 종류의 수를 최소화하고 싶습니다.
예를 들어, 경화가 수확한 귤 8개의 크기가 [1, 3, 2, 5, 4, 5, 2, 3] 이라고 합시다. 경화가 귤 6개를 판매하고 싶다면, 크기가 1, 4인 귤을 제외한 여섯 개의 귤을 상자에 담으면, 귤의 크기의 종류가 2, 3, 5로 총 3가지가 되며 이때가 서로 다른 종류가 최소일 때입니다.
경화가 한 상자에 담으려는 귤의 개수 k와 귤의 크기를 담은 배열 tangerine이 매개변수로 주어집니다. 경화가 귤 k개를 고를 때 크기가 서로 다른 종류의 수의 최솟값을 return 하도록 solution 함수를 작성해 주세요.

제한 사항

  • 1 ≤ k  tangerine의 길이 ≤ 100,000
  • 1 ≤ tangerine의 원소 ≤ 10,000,000

오답 풀이 (시간초과)

#138476 귤고르기

def solution(k, tangerine):
    answer = 0
    ###1###
    
    typeOfTangerine = list(set(tangerine))
    typeNumber = {}
    
    ###2###
    
    for i in typeOfTangerine:
        typeNumber[i] = tangerine.count(i)

    ###3###
    sortedTypeNumber = sorted(typeNumber.items(), key=lambda x: x[1], reverse=True)
    for i in range(len(sortedTypeNumber)):
        k -= sortedTypeNumber[i][1]
        answer += 1
        if k >= 0:
            break

    return answer

첫 풀이에서 시간 초과가 났다.

이에 대해서 확인을 해보니 리스트에서 i 원소를 찾는 count() 함수는 리스트를 다 검색하는 절차가 필요하기에 시간복잡도가 O(n)이다.

따라서, for문을 돌면서 count를 하는 것이 시간초과가 나온 원인으로 보인다.

그렇기에 주석으로 2번 코드블록 - `typeNumber` 딕셔너리에 요소를 추가하는 부분 - 을 아래와 같이 변경해 주었다.

    for i in tangerine:
        if i in typeNumber:
            typeNumber[i] += 1
        else:
            typeNumber[i] = 1

count 함수를 사용하는 대신 직접 for문을 통해 key에 맞는 value값을 1씩 추가하는 방식으로 대신했다.

 

정답풀이

#138476 귤고르기

def solution(k, tangerine):
    answer = 0
    
    ###1###
#    typeOfTangerine = list(set(tangerine))
    typeNumber = {}
    
    ###2###
    for i in tangerine:
        if i in typeNumber:
            typeNumber[i] += 1
        else:
            typeNumber[i] = 1

    ###3###
    sortedTypeNumber = sorted(typeNumber.items(), key=lambda x: x[1], reverse=True)
    
    for i in range(len(sortedTypeNumber)):
        k -= sortedTypeNumber[i][1]
        answer += 1
        if k >= 0:
            break

    return answer

 

풀이과정

주석으로 ###번호###을 한 블록이라고 생각하고 보고자 한다.

1번 블록

주석처리된 부분은 Tangerine의 종류를 set()을 이용해 중복을 제거 후 다시 list로 만들어서 요소를 count 하기 위해 쓴 코드이다. 하지만 이는 for문으로 tangerine요소를 count 하는 방식으로 변하면서 필요가 없어져 주석처리했다. 

typeNumber라는 딕셔너리를 만들어서 type-count를 묶었다.

2번 블록

시간 초과가 나지 않게 typeNumber딕셔너리에 type-count를 넣는 과정

3번 블록

딕셔너리를 value(종류에 따른 개수)에 따라 내림차순으로 정렬했습니다. 이때 sorted를 이용하면 튜플리스트로 반환된다

그다음 value가 많은 순대로 주어진 귤 k개에 할당했다.


개인적 깨달음

 

1. 딕셔너리를 정렬하는 것은 자주 사용해보지 않아서 헤맸습니다. lambda를 통해 key순서로 정렬할 건지, value순서로 정렬할 건지 선택할 수 있음을 배웠습니다.

2. 쉽게 count() 함수를 사용하려고 했는데 시간복잡도를 고려해야 하는 점을 다시 생각해 보게 되었습니다.

3. 마지막 if문에서 `k==0`으로 조건을 줬었는데 그렇게 하면 음수(마이너스)가 되는 상황이 고려 안된다는 사실을 테스트를 통해 깨닫고 조건을 변경하였습니다.

 

 

반응형