16 Chapter 16 Solutions¶
Solution to Challenge 1¶
Stepping through the code of mergeSort
one line at a time is probably the easiest way to understand what’s happening.
A few strategically placed print statements can also help:
List<E> mergeSort<E extends Comparable<dynamic>>(List<E> list) {
if (list.length < 2) {
print('recursion ending: $list');
return list;
} else {
print('recursion list in: $list');
}
final middle = list.length ~/ 2;
final left = mergeSort(list.sublist(0, middle));
final right = mergeSort(list.sublist(middle));
final merged = _merge(left, right);
print('recursion ending: merging $left and $right -> $merged');
return merged;
}
Here’s the output for sorting the list [[4, 2, 5, 1, 3]
:
recursion list in: [4, 2, 5, 1, 3]
recursion list in: [4, 2]
recursion ending: [4]
recursion ending: [2]
recursion ending: merging [4] and [2] -> [2, 4]
recursion list in: [5, 1, 3]
recursion ending: [5]
recursion list in: [1, 3]
recursion ending: [1]
recursion ending: [3]
recursion ending: merging [1] and [3] -> [1, 3]
recursion ending: merging [5] and [1, 3] -> [1, 3, 5]
recursion ending: merging [2, 4] and [1, 3, 5] -> [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
Solution to Challenge 2¶
The tricky part of this challenge is the limited capabilities of Iterable
. Traditional implementations of merge sort rely on using the indices of a list. Since Iterable
types have no notion of indices, though, you’ll make use of their iterator.
Recreate _merge
in this way:
List<E> _merge<E extends Comparable<dynamic>>(
Iterable<E> first,
Iterable<E> second,
) {
// 1
var result = <E>[];
// 2
var firstIterator = first.iterator;
var secondIterator = second.iterator;
// 3
var firstHasValue = firstIterator.moveNext();
var secondHasValue = secondIterator.moveNext();
// more to come
}
Setting up the algorithm involves the following steps:
- Create a new list to store the merged iterables.
- Grab the iterators. Iterators are objects that know how to get the next value in the iterable.
- Create two variables to keep track of when the iterators have reached the end of their content.
moveNext
returnstrue
if the iterator found a next element, orfalse
if the end of the collection was reached.
Using the iterators, you’ll decide which element should be appended into the result
list by comparing the values. Write the following at the end of the _merge
function:
while (firstHasValue && secondHasValue) {
// 1
final firstValue = firstIterator.current;
final secondValue = secondIterator.current;
// 2
if (firstValue.compareTo(secondValue) < 0) {
result.add(firstValue);
firstHasValue = firstIterator.moveNext();
// 3
} else if (firstValue.compareTo(secondValue) > 0) {
result.add(secondValue);
secondHasValue = secondIterator.moveNext();
// 4
} else {
result.add(firstValue);
result.add(secondValue);
firstHasValue = firstIterator.moveNext();
secondHasValue = secondIterator.moveNext();
}
}
// more to come
Here are some notes on the numbered comments above:
- Grab the values using the
current
property of your iterators. - If the first value is less than the second one, you’ll add the first value to
result
, and then move the iterator to the next value. - If the first value is greater than the second, you’ll do the opposite.
- If both values are equal, you’ll add them both and move the iterators on.
This process will continue until one of the iterators runs out of values to dispense. In that scenario, if the other iterator still has any values left, they’ll be equal to or greater than the ones in result
.
To add the rest of those values, write the following at the end of the _merge
function:
if (firstHasValue) {
do {
result.add(firstIterator.current);
} while (firstIterator.moveNext());
}
if (secondHasValue) {
do {
result.add(secondIterator.current);
} while (secondIterator.moveNext());
}
return result;
That completes your new implementation of _merge
.
Confirm that mergeSort
still works by running the following in main
:
final list = [7, 2, 6, 3, 9];
final sorted = mergeSort(list);
print(sorted);
You should see the console output below:
[2, 3, 6, 7, 9]