Skip to content

Commit 917d177

Browse files
committed
#74 merge_sorted_lists
1 parent 869418f commit 917d177

2 files changed

Lines changed: 121 additions & 18 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from __future__ import annotations
2+
3+
from typing import Generic
4+
5+
from book.data_structures import Array
6+
from book.data_structures import KeyObject
7+
from book.data_structures import PriorityQueue
8+
from book.data_structures import T
9+
from solutions.chapter6.section5.exercise3 import min_heap_extract_min
10+
from solutions.chapter6.section5.exercise3 import min_heap_insert
11+
from util import range_of
12+
13+
14+
# TODO(#20) replace by the general list node and list classes once they will have been added
15+
class SortedListNode(Generic[T]):
16+
key: T
17+
next: SortedListNode[T] | None = None
18+
19+
20+
class SortedList(Generic[T]):
21+
head: SortedListNode[T] | None = None
22+
23+
24+
def merge_sorted_lists(sorted_lists: Array[SortedList[T]], k: int) -> SortedList[T]:
25+
"""Merges sorted lists into one sorted list.
26+
27+
Args:
28+
sorted_lists: an array of sorted lists
29+
k: the number of sorted lists in the array
30+
31+
Returns:
32+
The sorted list containing all elements from the input sorted lists.
33+
"""
34+
Q = PriorityQueue(k)
35+
for i in range_of(1, to=k):
36+
sorted_list = sorted_lists[i]
37+
if sorted_list.head is not None:
38+
x = sorted_list.head
39+
sorted_list.head = sorted_list.head.next
40+
x.next = None
41+
min_heap_insert(Q, KeyObject(key=x.key, data=(x, sorted_list)), k)
42+
merged_list = SortedList[T]()
43+
tail = None
44+
while Q.heap_size > 0:
45+
element, list_ = min_heap_extract_min(Q).data
46+
if merged_list.head is None:
47+
tail = merged_list.head = element
48+
else:
49+
tail.next = element
50+
tail = tail.next
51+
if list_.head is not None:
52+
min_heap_insert(Q, KeyObject(key=list_.head.key, data=(list_.head, list_)), k)
53+
list_.head = list_.head.next
54+
return merged_list

test/test_solutions/test_chapter6.py

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import random
24
from typing import Union
35

@@ -11,11 +13,15 @@
1113
from book.data_structures import CT
1214
from book.data_structures import Heap
1315
from book.data_structures import KeyObject
16+
from book.data_structures import T
1417
from solutions.chapter6.problem2 import multiary_child
1518
from solutions.chapter6.problem2 import multiary_parent
1619
from solutions.chapter6.section2.exercise3 import min_heapify
1720
from solutions.chapter6.section2.exercise6 import iterative_max_heapify
1821
from solutions.chapter6.section5.exercise10 import max_heap_delete
22+
from solutions.chapter6.section5.exercise11 import SortedList
23+
from solutions.chapter6.section5.exercise11 import SortedListNode
24+
from solutions.chapter6.section5.exercise11 import merge_sorted_lists
1925
from solutions.chapter6.section5.exercise3 import min_heap_decrease_key
2026
from solutions.chapter6.section5.exercise3 import min_heap_extract_min
2127
from solutions.chapter6.section5.exercise3 import min_heap_insert
@@ -28,6 +34,7 @@
2834
from solutions.chapter6.section5.exercise9 import min_heap_dequeue
2935
from solutions.chapter6.section5.exercise9 import min_heap_enqueue
3036
from test_case import ClrsTestCase
37+
from test_util import create_array
3138
from test_util import create_heap
3239
from test_util import create_priority_queue
3340
from util import range_of
@@ -44,6 +51,30 @@ def build_min_heap_by_inversion(A: Union[Heap[CT], Heap[KeyObject]], n: int) ->
4451
A[i] = -A[i]
4552

4653

54+
def create_sorted_list(elements: list[T]) -> SortedList[T]:
55+
sortedList = SortedList[T]()
56+
tail = None
57+
for element in elements:
58+
x = SortedListNode[T]()
59+
x.key = element
60+
if sortedList.head is None:
61+
sortedList.head = x
62+
tail = x
63+
else:
64+
tail.next = x
65+
tail = x
66+
return sortedList
67+
68+
69+
def convert_sorted_list(sorted_list: SortedList[T]) -> list[T]:
70+
result = []
71+
x = sorted_list.head
72+
while x is not None:
73+
result.append(x.key)
74+
x = x.next
75+
return result
76+
77+
4778
class TestChapter6(ClrsTestCase):
4879

4980
@given(st.data())
@@ -261,6 +292,26 @@ def test_max_heap_increase_key_(self, data):
261292
self.assertArrayPermuted(A, key_objects, end=n)
262293
self.assertPriorityQueueMappingConsistent(A)
263294

295+
@given(st.data())
296+
def test_max_heap_increase_key__invalid_key(self, data):
297+
keys = data.draw(lists(integers(), min_size=1))
298+
key_objects = [KeyObject(key, data.draw(text())) for key in keys]
299+
heap = create_heap(key_objects)
300+
n = len(key_objects)
301+
build_max_heap(heap, n)
302+
A = create_priority_queue(heap, n)
303+
x = random.choice(key_objects)
304+
k = x.key - data.draw(integers(min_value=1))
305+
306+
with self.assertRaisesRegex(ValueError, "new key is smaller than current key"):
307+
max_heap_increase_key_(A, x, k)
308+
309+
self.assertNotEquals(x.key, k)
310+
self.assertMaxHeap(A)
311+
self.assertEqual(A.heap_size, n)
312+
self.assertArrayPermuted(A, key_objects, end=n)
313+
self.assertPriorityQueueMappingConsistent(A)
314+
264315
@given(st.data())
265316
def test_min_heap_queue(self, data):
266317
# simulate the queue - enqueue, dequeue, then again enqueue and dequeue
@@ -350,24 +401,22 @@ def test_max_heap_delete(self, data):
350401
self.assertPriorityQueueMappingConsistent(A)
351402

352403
@given(st.data())
353-
def test_max_heap_increase_key__invalid_key(self, data):
354-
keys = data.draw(lists(integers(), min_size=1))
355-
key_objects = [KeyObject(key, data.draw(text())) for key in keys]
356-
heap = create_heap(key_objects)
357-
n = len(key_objects)
358-
build_max_heap(heap, n)
359-
A = create_priority_queue(heap, n)
360-
x = random.choice(key_objects)
361-
k = x.key - data.draw(integers(min_value=1))
362-
363-
with self.assertRaisesRegex(ValueError, "new key is smaller than current key"):
364-
max_heap_increase_key_(A, x, k)
365-
366-
self.assertNotEquals(x.key, k)
367-
self.assertMaxHeap(A)
368-
self.assertEqual(A.heap_size, n)
369-
self.assertArrayPermuted(A, key_objects, end=n)
370-
self.assertPriorityQueueMappingConsistent(A)
404+
def test_merge_sorted_lists(self, data):
405+
k = data.draw(integers(min_value=1, max_value=10))
406+
sorted_lists = [create_sorted_list(data.draw(lists(integers(), min_size=1, max_size=10).map(sorted)))
407+
for _ in range_of(1, to=k)]
408+
sorted_lists_array = create_array(sorted_lists)
409+
all_elements = []
410+
for sorted_list in sorted_lists:
411+
all_elements.extend(convert_sorted_list(sorted_list))
412+
413+
actual_merged_list = merge_sorted_lists(sorted_lists_array, k)
414+
415+
actual_all_elements = convert_sorted_list(actual_merged_list)
416+
actual_all_elements_array = create_array(actual_all_elements)
417+
n = len(all_elements)
418+
self.assertArraySorted(actual_all_elements_array, end=n)
419+
self.assertArrayPermuted(actual_all_elements_array, all_elements, end=n)
371420

372421
@given(st.data())
373422
def test_multiary_parent_child(self, data):

0 commit comments

Comments
 (0)