|
| 1 | +/* |
| 2 | +풀이 1 |
| 3 | +- lists를 순회하면서 순서대로 링크드리스트 두 개를 짝 지어 병합하는 방식으로 풀이할 수 있습니다 |
| 4 | +
|
| 5 | +Big O |
| 6 | +- K: 배열 lists의 길이 |
| 7 | +- N: 모든 링크드리스트의 노드 개수의 합 |
| 8 | +- n_i: i번 인덱스 링크드리스트의 노드의 개수 |
| 9 | +- Time complexity: O(KN) |
| 10 | + - K-1 번의 병합을 진행합니다 |
| 11 | + - i번째 병합 때, 병합하는 두 링크드리스트는 각각 𝚺(n_(i-1)), n_i입니다 |
| 12 | + 이 때 𝚺(n_(i-1))의 상한을 고려한다면 두 링크드리스트의 병합에 걸리는 시간복잡도는 O(N)입니다 |
| 13 | + - O((K-1)N) = O(KN) |
| 14 | + - 풀이 2로 시간복잡도를 O((logK)N)으로 최적화할 수 있습니다 |
| 15 | +- Space complexity: O(1) |
| 16 | + - res, dummy, curr 등의 추가적인 포인터를 생성하긴 하지만 기존에 주어져 있던 ListNode의 Next만 조작하므로 K, N과 상관 없이 공간복잡도는 상수값을 가집니다 |
| 17 | +*/ |
| 18 | + |
| 19 | +/** |
| 20 | + * Definition for singly-linked list. |
| 21 | + * type ListNode struct { |
| 22 | + * Val int |
| 23 | + * Next *ListNode |
| 24 | + * } |
| 25 | + */ |
| 26 | + func mergeKLists(lists []*ListNode) *ListNode { |
| 27 | + n := len(lists) |
| 28 | + |
| 29 | + if n == 0 { |
| 30 | + return nil |
| 31 | + } |
| 32 | + |
| 33 | + res := lists[0] |
| 34 | + for i := 1; i < n; i++ { |
| 35 | + res = mergeTwoLists(res, lists[i]) |
| 36 | + } |
| 37 | + return res |
| 38 | +} |
| 39 | + |
| 40 | +func mergeTwoLists(first *ListNode, second *ListNode) *ListNode { |
| 41 | + dummy := &ListNode{} |
| 42 | + curr := dummy |
| 43 | + |
| 44 | + for first != nil && second != nil { |
| 45 | + if first.Val < second.Val { |
| 46 | + curr.Next = first |
| 47 | + first = first.Next |
| 48 | + } else { |
| 49 | + curr.Next = second |
| 50 | + second = second.Next |
| 51 | + } |
| 52 | + curr = curr.Next |
| 53 | + } |
| 54 | + |
| 55 | + if first != nil { |
| 56 | + curr.Next = first |
| 57 | + } |
| 58 | + if second != nil { |
| 59 | + curr.Next = second |
| 60 | + } |
| 61 | + |
| 62 | + return dummy.Next |
| 63 | +} |
| 64 | + |
| 65 | + |
| 66 | +/* |
| 67 | +풀이 2 |
| 68 | +- Divide and Conquer 방식으로 시간복잡도를 최적화할 수 있습니다 |
| 69 | +- 하지만 공간복잡도 측면에서는 trade-off가 있습니다 |
| 70 | +
|
| 71 | +Big O |
| 72 | +- K: 배열 lists의 길이 |
| 73 | +- N: 모든 링크드리스트의 노드 개수의 합 |
| 74 | +- Time complexity: O((logK)N) |
| 75 | + - lists를 반으로 쪼개 가면서 재귀호출을 진행하므로 재귀호출은 logK 레벨에 걸쳐 이루어집니다 -> O(logK) |
| 76 | + - 각 계층마다 우리는 모든 노드를 최대 한 번씩 조회합니다 -> O(N) |
| 77 | +- Space complexity: O(logK) |
| 78 | + - 풀이 1과 비슷하지만 재귀호출 스택을 고려해야 합니다 |
| 79 | +*/ |
| 80 | + |
| 81 | +/** |
| 82 | + * Definition for singly-linked list. |
| 83 | + * type ListNode struct { |
| 84 | + * Val int |
| 85 | + * Next *ListNode |
| 86 | + * } |
| 87 | + */ |
| 88 | + func mergeKLists(lists []*ListNode) *ListNode { |
| 89 | + n := len(lists) |
| 90 | + |
| 91 | + if n == 0 { |
| 92 | + return nil |
| 93 | + } |
| 94 | + if n == 1 { |
| 95 | + return lists[0] |
| 96 | + } |
| 97 | + |
| 98 | + left := mergeKLists(lists[:n/2]) |
| 99 | + right := mergeKLists(lists[n/2:]) |
| 100 | + |
| 101 | + return mergeTwoLists(left, right) |
| 102 | +} |
| 103 | + |
| 104 | +func mergeTwoLists(first *ListNode, second *ListNode) *ListNode { |
| 105 | + dummy := &ListNode{} |
| 106 | + curr := dummy |
| 107 | + |
| 108 | + for first != nil && second != nil { |
| 109 | + if first.Val < second.Val { |
| 110 | + curr.Next = first |
| 111 | + first = first.Next |
| 112 | + } else { |
| 113 | + curr.Next = second |
| 114 | + second = second.Next |
| 115 | + } |
| 116 | + curr = curr.Next |
| 117 | + } |
| 118 | + |
| 119 | + if first != nil { |
| 120 | + curr.Next = first |
| 121 | + } |
| 122 | + if second != nil { |
| 123 | + curr.Next = second |
| 124 | + } |
| 125 | + |
| 126 | + return dummy.Next |
| 127 | +} |
0 commit comments