🧠 Merge K Sorted Lists

문제

You are given an array of k linked-lists lists, each linked-list is sorted in ascending order. Merge all the linked-lists into one sorted linked-list and return it.

풀이 1

from typing import List, Optional


class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next


class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        """
        Brute Force

        Time complexity: O(N log N)
        Space complexity: O(N)
        """
        self.nodes = []
        head = point = ListNode(0)
        for l in lists:
            while l:
                self.nodes.append(l.val)
                l = l.next
        for x in sorted(self.nodes):
            point.next = ListNode(x)
            point = point.next
        return head.next

κ°€μž₯ μ‰½κ²Œ λ– μ˜¬λ¦΄ 수 μžˆλŠ” Brute Force 방식이라 ν•  수 μžˆλ‹€.

리슀트λ₯Ό λŒλ©΄μ„œ μƒˆλ‘œμš΄ λ¦¬μŠ€νŠΈμ— 값을 μΆ”κ°€ν•˜κ³  sorted() ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄ 리슀트λ₯Ό μ •λ ¬ν•œ ν›„ μƒˆλ‘œμš΄ μ—°κ²° 리슀트λ₯Ό λ§Œλ“œλŠ” 방식이닀.

μœ„ λ°©λ²•μ˜ μ‹œκ°„ λ³΅μž‘λ„λŠ” sorted() ν•¨μˆ˜λ‘œ 정렬을 ν•˜κΈ° λ•Œλ¬Έμ— O(N log N)이며, 곡간 λ³΅μž‘λ„λŠ” O(N)이닀.

μ—¬κΈ°μ„œ λ§ν•˜λŠ” N은 리슀트의 길이가 μ•„λ‹Œ νŒŒλΌλ―Έν„°λ‘œ 받은 λͺ¨λ“  μ—°κ²°λ¦¬μŠ€νŠΈ λ…Έλ“œμ˜ 합이닀.

μ—°κ²° 리슀트λ₯Ό μ •λ ¬ν•  수 μžˆλŠ” 방법 쀑 μ‹œκ°„ λ³΅μž‘λ„λ₯Ό 쀄일 수 μžˆλŠ” 방식이 무엇이 μžˆμ„κΉŒ? 닡은 νž™μ΄λ‹€.

풀이 2

import heapq
from typing import List, Optional


class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        """
        Optimize Approach 2 by heapq
        """
        h = [(l.val, idx) for idx, l in enumerate(lists) if l]
        heapq.heapify(h)
        head = cur = ListNode()
        while h:
            val, idx = heapq.heappop(h)
            cur.next = ListNode(val)
            cur = cur.next
            node = lists[idx] = lists[idx].next
            if node:
                heapq.heappush(h, (node.val, idx))
        return head.next

python의 heapq λͺ¨λ“ˆμ€ μš°μ„ μˆœμœ„ μ•Œκ³ λ¦¬μ¦˜μ˜ κ΅¬ν˜„μ„ μ œκ³΅ν•œλ‹€.

μš°μ„ μˆœμœ„ 큐 μ•Œκ³ λ¦¬μ¦˜μ„ κ΅¬ν˜„ν•œ λͺ¨λ“ˆλ‘œλŠ” priorityQueue도 μžˆλŠ”λ°, 이 λͺ¨λ“ˆ λ˜ν•œ heapq λͺ¨λ“ˆμ„ 기반으둜 λ§Œλ“€μ–΄μ‘Œμ§€λ§Œ λ‘˜μ€ Thread-safe 지원 여뢀에 따라 λ‹€λ₯΄λ‹€. priorityQueueλŠ” λ©€ν‹° μ“°λ ˆλ“œ ν™˜κ²½μ—μ„œ 데이터λ₯Ό μ•ˆμ „ν•˜κ²Œ κ΅ν™˜ν•΄μ•Ό ν•˜λŠ” 경우 μœ μš©ν•˜λ©°, heapqλŠ” μ΄λŸ¬ν•œ Thread-safe λ₯Ό μ§€μ›ν•˜μ§€λŠ” μ•ŠλŠ”λ‹€. 락킹을 ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— heapq의 속도가 더 λΉ λ₯Έ μž₯점이 있으며, 사싀상 λ©€ν‹° μ“°λ ˆλ“œ ν™˜κ²½μ—μ„œ κ°œλ°œν•˜λŠ” 것이 μ•„λ‹Œ κ²½μš°μ—λŠ” heapqλ₯Ό λŒ€λΆ€λΆ„ μ‚¬μš©ν•œλ‹€.

νž™(min heap)은 λͺ¨λ“  λΆ€λͺ¨ λ…Έλ“œκ°€ μžμ‹λ³΄λ‹€ μž‘κ±°λ‚˜ 같은 값을 κ°–λŠ” 이진 트리이기 λ•Œλ¬Έμ—, μš°λ¦¬λŠ” 이 νž™μ„ μ‚¬μš©ν•΄μ„œ μ—°κ²° 리슀트λ₯Ό μ˜€λ¦„μ°¨μˆœμœΌλ‘œ μ •λ ¬ν•˜μ—¬ μ‚¬μš©ν•  수 μžˆλ‹€. heapq λͺ¨λ“ˆμ˜ heapify() ν•¨μˆ˜λ₯Ό 톡해 νž™νλ₯Ό λ§Œλ“€ 수 있으며, heappop()을 톡해 νž™νμ—μ„œ κ°€μž₯ μž‘μ€ μš”μ†Œλ₯Ό κΊΌλ‚Ό 수 μžˆλ‹€.

λ¨Όμ € 리슀트λ₯Ό νž™νλ‘œ λ§Œλ“  λ‹€μŒ μš”μ†Œκ°€ μ—†μ–΄μ§ˆ λ•ŒκΉŒμ§€ λ°˜λ³΅ν•˜λ©° νž™νμ—μ„œ μš”μ†Œλ₯Ό κΊΌλ‚Έλ‹€. κΊΌλ‚Έ μš”μ†ŒλŠ” μ—°κ²° λ¦¬μŠ€νŠΈμ— μΆ”κ°€ν•˜λ©°, κΈ°μ‘΄ λ¦¬μŠ€νŠΈμ—μ„œ λ‹€μŒ λ…Έλ“œλ₯Ό 가져와 νž™νμ— λ„£λŠ”λ‹€. νž™νμ— 남은 μš”μ†Œκ°€ μ—†μœΌλ©΄ λ§Œλ“€μ–΄μ§„ μ—°κ²° 리슀트의 λ‘λ²ˆμ§Έ λ…Έλ“œ(head.next)λ₯Ό 리턴해주면 λœλ‹€.

μ΄λ ‡κ²Œ κ΅¬ν˜„μ„ ν•  경우, μ‹œκ°„ λ³΅μž‘λ„λŠ” O(N log k)둜 λͺ¨λ“  리슀트λ₯Ό λŒμ•˜μ„ λ•Œλ³΄λ‹€ λ‹¨μΆ•λœ μ‹œκ°„μœΌλ‘œ 정닡을 ꡬ할 수 μžˆλ‹€.

λͺ¨λ“  κ²½μš°μ— sorted()보닀 νž™μ„ μ΄μš©ν•œ 정렬이 λΉ λ₯Έ 것은 μ•„λ‹ˆμ§€λ§Œ, 이와 같이 λ‚΄λΆ€ μˆœμ„œλ₯Ό λ³΄μ‘΄ν•˜λ©° μ‚½μž…μ„ ν•˜λŠ” κ²½μš°μ—λŠ” νž™νλ₯Ό ν™œμš©ν•˜λŠ” 것이 더 효율적인 방법이닀.

References


Written by@ugaemi
Record things I want to remember

🐱 GitHubπŸ“š Reading Space