PS/문제

[BOJ] Python 백준 K번째 수 (11004) [feat. heap풀이, sort 풀이]

1223v 2025. 6. 25. 12:54

sort를 이용한 풀이 (메모리 : 622180KB, 실행시간 : 3452ms)

import sys
input = sys.stdin.readline

N, K = map(int,input().split())
A = list(map(int,input().split()))

A.sort()

print(A[K-1])

위 코드는 단순히 파이썬의 timsort를 사용해 정렬 후, K번째의 인덱스를 가져왔다.

시간복잡도

  • 파이썬의 내장 sort() 함수의 시간복잡도는 평균적으로나 최악의 경우로나 O(NlogN)이다.
  • 정렬 후 원소를 찾는 것은 O(1)이므로
  • 따라서, 전체 시간복잡도는 정렬에 의해 결정되어 O(NlogN)이다.

 

heap을 이용한 풀이 (메모리 : 626184KB, 실행시간 : 5164ms)

import sys
import heapq
input = sys.stdin.readline

N, K = map(int,input().split())

A = list(map(int,input().split()))

hq = []
for i in A:

    if len(hq) < K:
        heapq.heappush(hq, -i)
    elif i < -hq[0]:
        heapq.heappop(hq)
        heapq.heappush(hq, -i)

print(-hq[0])

위 코드는 heap을 사용해 N개의 수들을 hq라는 배열을 최대 힙을 이용해 넣었으며, K개 갯수 이전에는 hq에 계속 넣어주고, K개 이상이 되면, 힙에서 가장 큰 수와 i 중 더 작은 수를 힙에 넣어준다.

그러면 hq[0]에 K번째 큰 수가 들어가 있게 된다.

시간복잡도

  • 리스트 A의 모든 원소 N개를 순회해야 하므로 O(N)
  • 루프 안에서 heappush 또는 heappop연산은 힙의 크기 K에 대해 O(logK)의 시간이 걸린다.
  • 따라서, 총 시간 복잡도는 O(NlogK)가 된다.

 

그렇다면, 왜 heap 코드가 더 느릴까?

이론적으로 K가 N보다 훨씬 작을 때, O(NlogK)가 O(NlogN)보다 빠르다.

  • K가 N보다 큰 경우가 존재할 수 있기 때문이다.
  • A.sort는 상수 요인이 매우 작은, 고도로 최적화된 구현체이다. 반면, 파이썬 루프를 도는 heapq코드는 상수 요인이 상대적으로 크다
  • 파이썬의 기본 정렬 알고리즘은 TimSort는 이미 데이터가 일부 정렬되어 있는 경우 등 실제 데이터에서 나타나는 특징을 잘 활용하여 O(NlogN)보다 훨씬 빠르게 거의 O(N)에 가깝게 동작한다.
728x90