[Python] 프로그래머스 Lv_2 프린터

Jihoon Oh·2020년 12월 31일
0
post-thumbnail

문제 설명

일반적인 프린터는 인쇄 요청이 들어온 순서대로 인쇄합니다. 그렇기 때문에 중요한 문서가 나중에 인쇄될 수 있습니다. 이런 문제를 보완하기 위해 중요도가 높은 문서를 먼저 인쇄하는 프린터를 개발했습니다. 이 새롭게 개발한 프린터는 아래와 같은 방식으로 인쇄 작업을 수행합니다.

  1. 인쇄 대기목록의 가장 앞에 있는 문서(J)를 대기목록에서 꺼냅니다.
  2. 나머지 인쇄 대기목록에서 J보다 중요도가 높은 문서가 한 개라도 존재하면 J를 대기목록의 가장 마지막에 넣습니다.
  3. 그렇지 않으면 J를 인쇄합니다.

예를 들어, 4개의 문서(A, B, C, D)가 순서대로 인쇄 대기목록에 있고 중요도가 2 1 3 2 라면 C D A B 순으로 인쇄하게 됩니다.
내가 인쇄를 요청한 문서가 몇 번째로 인쇄되는지 알고 싶습니다. 위의 예에서 C는 1번째로, A는 3번째로 인쇄됩니다.
현재 대기목록에 있는 문서의 중요도가 순서대로 담긴 배열 priorities와 내가 인쇄를 요청한 문서가 현재 대기목록의 어떤 위치에 있는지를 알려주는 location이 매개변수로 주어질 때, 내가 인쇄를 요청한 문서가 몇 번째로 인쇄되는지 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 현재 대기목록에는 1개 이상 100개 이하의 문서가 있습니다.
  • 인쇄 작업의 중요도는 1~9로 표현하며 숫자가 클수록 중요하다는 뜻입니다.
  • location은 0 이상 (현재 대기목록에 있는 작업 수 - 1) 이하의 값을 가지며 대기목록의 가장 앞에 있으면 0, 두 번째에 있으면 1로 표현합니다.

나의 풀이

일단은 리스트의 맨 앞에서 요소를 빼서 비교하고 다시 뒤로 넣어 주는 작업이 필요하기 때문에 리스트 보다는 deque를 활용하는 방법을 생각했다.
리스트에서 맨 앞을 pop 하는 pop(0)의 시간복잡도는 O(N) 이지만 덱의 popleft()는 시간복잡도가 O(1)이기 때문이다.

파이썬의 Deque에 대해 잘 정리된 Velog 글이 있어서 링크로 첨부한다.
[Python] Deque

처음 짠 코드는 popleft()를 한 뒤에 큐의 최댓값과 비교해서 location과 answer값을 조정하다가 location이 0일때 popleft해서 최댓값이면 answer를 반환하는 코드였다.

하지만 이렇게 하니까 로직의 처리도 복잡했고 중간에 로직을 잘 못 짠 탓인지 테스트에 걸리는 시간이 어마어마해서 통과하지 못했다.

때문에 그다음으로 생각한 방법이 큐 안에 튜플을 넣는 방법이었다. location 변수값은 주어지기 때문에, 큐 안의 각 요소로 priority와 index 값을 튜플로 묶어서 넣으면 비교하기 편해지기 때문이었다.

코드는 다음과 같다.

from collections import deque # deque는 collections에서 import한다

def solution(priorities, location):
    answer = 0
    # max()로 priority의 최대값을 구하기 위해 idx와 value의 순서를 바꾼다
    queue = deque([(value, idx) for idx, value in enumerate(priorities)])
    # deque의 popleft()는 O(1), queue의 pop(0)은 O(N)이라 deque로 푸는게 더 효율적
    while len(queue):
        front = queue.popleft()
        if queue and front[0] < max(queue)[0]:
        # front의 priority가 queue 안에서의 최댓값보다 작으면
            queue.append(front) # queue의 맨 뒤로 front를 보낸다
        else:
            answer += 1 # 아니면 출력하므로 출력 횟수를 저장하는 answer += 1
            if front[1] == location:
                return answer
    return answer

여기서 queue를 만들 때 튜플을 enumerate에서 나온 대로 (idx, value)를 하지 않고 순서를 바꿔준 것은 max(queue)를 사용할때 기준을 튜플의 0번째 원소로 하기 위함이었다.
popleft()를 통해 맨 앞의 원소를 제거해주고, 이 원소가 큐의 최댓값보다 작으면 다시 append 함수를 통해서 큐의 맨 뒤로 보내줬다.

만약 pop한 원소의 priority가 최댓값이면, 프린터로 출력을 한다는 뜻이기 때문에 우선 answer 값을 1 증가시켜줬다.
그리고는튜플의 두번째 값인 idx 값을 확인해서 location과 같으면 answer 값을 return했다.

while문을 탈출한 경우에는 큐에 남아있는 인쇄가 없다는 뜻이므로, 마지막 인쇄가 location이라는 의미가 되기 때문에 그대로 answer를 return했다.
(나중에 생각해보니까 어차피 출력중에 한 번은 location과 idx가 일치하기 때문에 while문의 조건 자체가 무의미했다.)

다른 풀이

from collections import deque

def other_solution(priorities, location):
    # any를 사용한 풀이
    queue =  deque([(i,p) for i,p in enumerate(priorities)])
    answer = 0
    while True:
        cur = queue.popleft()
        if any(cur[1] < q[1] for q in queue):
        # any: iteration 중에 하나라도 True 면 True 반환
            queue.append(cur)
        else:
            answer += 1
            if cur[0] == location:
                return answer

가장 많은 좋아요를 받은 풀이를 가져왔다. 다만 이 풀이는 list를 사용해서 cur를 꺼내는 데 시간복잡도가 O(N)이었으므로, 해당 부분만 deque로 바꿔주었다.

이 풀이의 핵심은 any의 사용이다. any(x)는 반복 가능한 x를 parameter로 받는데, x의 요소들 중 하나라도 True면 True를 반환하고, 모두 False면 False를 반환한다.

따라서 if any(cur[1] < q[1] for q in queue): 구문을 통해 max()를 사용했던 나의 풀이의 조건식을 대체할 수 있다.

profile
Backend Developeer

0개의 댓글