Skip to content

Commit ad6325c

Browse files
committed
feat: add Merge k Sorted Lists
1 parent 7eff232 commit ad6325c

File tree

8 files changed

+290
-8
lines changed

8 files changed

+290
-8
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"problem_name": "merge_k_sorted_lists",
3+
"solution_class_name": "Solution",
4+
"problem_number": "23",
5+
"problem_title": "Merge k Sorted Lists",
6+
"difficulty": "Hard",
7+
"topics": "Linked List, Divide and Conquer, Heap (Priority Queue), Merge Sort",
8+
"tags": ["grind-75"],
9+
"readme_description": "You are given an array of `k` linked-lists `lists`, each linked-list is sorted in ascending order.\n\n*Merge all the linked-lists into one sorted linked-list and return it.*",
10+
"readme_examples": [
11+
{
12+
"content": "```\nInput: lists = [[1,4,5],[1,3,4],[2,6]]\nOutput: [1,1,2,3,4,4,5,6]\n```\n**Explanation:** The linked-lists are:\n```\n[\n 1->4->5,\n 1->3->4,\n 2->6\n]\n```\nmerging them into one sorted linked list:\n```\n1->1->2->3->4->4->5->6\n```"
13+
},
14+
{ "content": "```\nInput: lists = []\nOutput: []\n```" },
15+
{ "content": "```\nInput: lists = [[]]\nOutput: []\n```" }
16+
],
17+
"readme_constraints": "- `k == lists.length`\n- `0 <= k <= 10^4`\n- `0 <= lists[i].length <= 500`\n- `-10^4 <= lists[i][j] <= 10^4`\n- `lists[i]` is sorted in ascending order.\n- The sum of `lists[i].length` will not exceed `10^4`.",
18+
"readme_additional": "",
19+
"solution_imports": "from leetcode_py import ListNode",
20+
"solution_methods": [
21+
{
22+
"name": "merge_k_lists",
23+
"parameters": "lists: list[ListNode | None]",
24+
"return_type": "ListNode | None",
25+
"dummy_return": "None"
26+
}
27+
],
28+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom leetcode_py import ListNode\nfrom .solution import Solution",
29+
"test_class_name": "MergeKSortedLists",
30+
"test_helper_methods": [
31+
{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }
32+
],
33+
"test_methods": [
34+
{
35+
"name": "test_merge_k_lists",
36+
"parametrize": "lists_data, expected_data",
37+
"parametrize_typed": "lists_data: list[list[int]], expected_data: list[int]",
38+
"test_cases": "[([[1, 4, 5], [1, 3, 4], [2, 6]], [1, 1, 2, 3, 4, 4, 5, 6]), ([], []), ([[]], []), ([[1]], [1]), ([[1, 2], [3, 4]], [1, 2, 3, 4]), ([[5], [1, 3], [2, 4, 6]], [1, 2, 3, 4, 5, 6]), ([[-1, 0, 1], [-2, 2]], [-2, -1, 0, 1, 2]), ([[1, 1, 1], [2, 2, 2]], [1, 1, 1, 2, 2, 2]), ([[]], []), ([[], [1], []], [1])]",
39+
"body": "lists = [ListNode.from_list(lst) for lst in lists_data]\nresult = self.solution.merge_k_lists(lists)\nexpected = ListNode.from_list(expected_data)\nassert result == expected"
40+
}
41+
],
42+
"playground_imports": "from solution import Solution\nfrom leetcode_py import ListNode",
43+
"playground_test_case": "# Example test case\nlists_data = [[1, 4, 5], [1, 3, 4], [2, 6]]\nlists = [ListNode.from_list(lst) for lst in lists_data]\nexpected_data = [1, 1, 2, 3, 4, 4, 5, 6]",
44+
"playground_execution": "result = Solution().merge_k_lists(lists)\nListNode.to_list(result) if result else []",
45+
"playground_assertion": "expected = ListNode.from_list(expected_data)\nassert result == expected"
46+
}

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PYTHON_VERSION = 3.13
2-
PROBLEM ?= basic_calculator
2+
PROBLEM ?= merge_k_sorted_lists
33
FORCE ?= 0
44
COMMA := ,
55

leetcode/basic_calculator/playground.ipynb

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"cells": [
33
{
44
"cell_type": "code",
5-
"execution_count": null,
5+
"execution_count": 1,
66
"id": "imports",
77
"metadata": {},
88
"outputs": [],
@@ -12,7 +12,7 @@
1212
},
1313
{
1414
"cell_type": "code",
15-
"execution_count": null,
15+
"execution_count": 2,
1616
"id": "setup",
1717
"metadata": {},
1818
"outputs": [],
@@ -24,17 +24,29 @@
2424
},
2525
{
2626
"cell_type": "code",
27-
"execution_count": null,
27+
"execution_count": 3,
2828
"id": "execute",
2929
"metadata": {},
30-
"outputs": [],
30+
"outputs": [
31+
{
32+
"data": {
33+
"text/plain": [
34+
"23"
35+
]
36+
},
37+
"execution_count": 3,
38+
"metadata": {},
39+
"output_type": "execute_result"
40+
}
41+
],
3142
"source": [
32-
"result = Solution().calculate(s)\nresult"
43+
"result = Solution().calculate(s)\n",
44+
"result"
3345
]
3446
},
3547
{
3648
"cell_type": "code",
37-
"execution_count": null,
49+
"execution_count": 4,
3850
"id": "test",
3951
"metadata": {},
4052
"outputs": [],
@@ -57,7 +69,7 @@
5769
"file_extension": ".py",
5870
"mimetype": "text/x-python",
5971
"name": "python",
60-
"nbconvert_exporter": "python3",
72+
"nbconvert_exporter": "python",
6173
"pygments_lexer": "ipython3",
6274
"version": "3.13.7"
6375
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Merge k Sorted Lists
2+
3+
**Difficulty:** Hard
4+
**Topics:** Linked List, Divide and Conquer, Heap (Priority Queue), Merge Sort
5+
**Tags:** grind-75
6+
7+
**LeetCode:** [Problem 23](https://leetcode.com/problems/merge-k-sorted-lists/description/)
8+
9+
## Problem Description
10+
11+
You are given an array of `k` linked-lists `lists`, each linked-list is sorted in ascending order.
12+
13+
_Merge all the linked-lists into one sorted linked-list and return it._
14+
15+
## Examples
16+
17+
### Example 1:
18+
19+
```
20+
Input: lists = [[1,4,5],[1,3,4],[2,6]]
21+
Output: [1,1,2,3,4,4,5,6]
22+
```
23+
24+
**Explanation:** The linked-lists are:
25+
26+
```
27+
[
28+
1->4->5,
29+
1->3->4,
30+
2->6
31+
]
32+
```
33+
34+
merging them into one sorted linked list:
35+
36+
```
37+
1->1->2->3->4->4->5->6
38+
```
39+
40+
### Example 2:
41+
42+
```
43+
Input: lists = []
44+
Output: []
45+
```
46+
47+
### Example 3:
48+
49+
```
50+
Input: lists = [[]]
51+
Output: []
52+
```
53+
54+
## Constraints
55+
56+
- `k == lists.length`
57+
- `0 <= k <= 10^4`
58+
- `0 <= lists[i].length <= 500`
59+
- `-10^4 <= lists[i][j] <= 10^4`
60+
- `lists[i]` is sorted in ascending order.
61+
- The sum of `lists[i].length` will not exceed `10^4`.

leetcode/merge_k_sorted_lists/__init__.py

Whitespace-only changes.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 7,
6+
"id": "imports",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"from solution import Solution\n",
11+
"\n",
12+
"from leetcode_py import ListNode"
13+
]
14+
},
15+
{
16+
"cell_type": "code",
17+
"execution_count": 8,
18+
"id": "setup",
19+
"metadata": {},
20+
"outputs": [],
21+
"source": [
22+
"# Example test case\n",
23+
"lists_data = [[1, 4, 5], [1, 3, 4], [2, 6]]\n",
24+
"lists = [ListNode.from_list(lst) for lst in lists_data]\n",
25+
"expected_data = [1, 1, 2, 3, 4, 4, 5, 6]"
26+
]
27+
},
28+
{
29+
"cell_type": "code",
30+
"execution_count": 9,
31+
"id": "execute",
32+
"metadata": {},
33+
"outputs": [
34+
{
35+
"data": {
36+
"text/plain": [
37+
"[1, 1, 2, 3, 4, 4, 5, 6]"
38+
]
39+
},
40+
"execution_count": 9,
41+
"metadata": {},
42+
"output_type": "execute_result"
43+
}
44+
],
45+
"source": [
46+
"result = Solution().merge_k_lists(lists)\n",
47+
"ListNode.to_list(result) if result else []"
48+
]
49+
},
50+
{
51+
"cell_type": "code",
52+
"execution_count": 10,
53+
"id": "test",
54+
"metadata": {},
55+
"outputs": [],
56+
"source": [
57+
"expected = ListNode.from_list(expected_data)\n",
58+
"assert result == expected"
59+
]
60+
}
61+
],
62+
"metadata": {
63+
"kernelspec": {
64+
"display_name": "leetcode-py-py3.13",
65+
"language": "python",
66+
"name": "python3"
67+
},
68+
"language_info": {
69+
"codemirror_mode": {
70+
"name": "ipython",
71+
"version": 3
72+
},
73+
"file_extension": ".py",
74+
"mimetype": "text/x-python",
75+
"name": "python",
76+
"nbconvert_exporter": "python3",
77+
"pygments_lexer": "ipython3",
78+
"version": "3.13.7"
79+
}
80+
},
81+
"nbformat": 4,
82+
"nbformat_minor": 5
83+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from leetcode_py import ListNode
2+
3+
4+
class Solution:
5+
# Time: O(n log k) where n is total nodes, k is number of lists
6+
# Space: O(log k) for recursion stack
7+
def merge_k_lists(self, lists: list[ListNode | None]) -> ListNode | None:
8+
if not lists:
9+
return None
10+
return self._divide_conquer(lists, 0, len(lists) - 1)
11+
12+
def _divide_conquer(self, lists: list[ListNode | None], left: int, right: int) -> ListNode | None:
13+
if left == right:
14+
return lists[left]
15+
16+
mid = (left + right) // 2
17+
l1 = self._divide_conquer(lists, left, mid)
18+
l2 = self._divide_conquer(lists, mid + 1, right)
19+
return self._merge_two(l1, l2)
20+
21+
def _merge_two(self, l1: ListNode | None, l2: ListNode | None) -> ListNode | None:
22+
dummy = ListNode(0)
23+
curr = dummy
24+
25+
while l1 and l2:
26+
if l1.val <= l2.val:
27+
curr.next = l1
28+
l1 = l1.next
29+
else:
30+
curr.next = l2
31+
l2 = l2.next
32+
curr = curr.next
33+
34+
curr.next = l1 or l2
35+
return dummy.next
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import pytest
2+
3+
from leetcode_py import ListNode
4+
from leetcode_py.test_utils import logged_test
5+
6+
from .solution import Solution
7+
8+
9+
class TestMergeKSortedLists:
10+
def setup_method(self):
11+
self.solution = Solution()
12+
13+
@pytest.mark.parametrize(
14+
"lists_data, expected_data",
15+
[
16+
([[1, 4, 5], [1, 3, 4], [2, 6]], [1, 1, 2, 3, 4, 4, 5, 6]),
17+
([], []),
18+
([[]], []),
19+
([[1]], [1]),
20+
([[1, 2], [3, 4]], [1, 2, 3, 4]),
21+
([[5], [1, 3], [2, 4, 6]], [1, 2, 3, 4, 5, 6]),
22+
([[-1, 0, 1], [-2, 2]], [-2, -1, 0, 1, 2]),
23+
([[1, 1, 1], [2, 2, 2]], [1, 1, 1, 2, 2, 2]),
24+
([[]], []),
25+
([[], [1], []], [1]),
26+
([[0]], [0]),
27+
([[-10, -5, -1], [-8, -3], [-7, -2, 0]], [-10, -8, -7, -5, -3, -2, -1, 0]),
28+
([[1, 2, 3, 4, 5]], [1, 2, 3, 4, 5]),
29+
([[10], [9], [8], [7]], [7, 8, 9, 10]),
30+
([[1, 100], [2, 99], [3, 98]], [1, 2, 3, 98, 99, 100]),
31+
([[], [], []], []),
32+
([[0, 0, 0], [0, 0]], [0, 0, 0, 0, 0]),
33+
([[1, 3, 5, 7], [2, 4, 6, 8]], [1, 2, 3, 4, 5, 6, 7, 8]),
34+
(
35+
[[100, 200, 300], [50, 150, 250], [75, 125, 175]],
36+
[50, 75, 100, 125, 150, 175, 200, 250, 300],
37+
),
38+
],
39+
)
40+
@logged_test
41+
def test_merge_k_lists(self, lists_data: list[list[int]], expected_data: list[int]):
42+
lists = [ListNode.from_list(lst) for lst in lists_data]
43+
result = self.solution.merge_k_lists(lists)
44+
expected = ListNode.from_list(expected_data)
45+
assert result == expected

0 commit comments

Comments
 (0)