diff --git a/lib/src/collection/heap.dart b/lib/src/collection/heap.dart index dbd2a21..8436104 100644 --- a/lib/src/collection/heap.dart +++ b/lib/src/collection/heap.dart @@ -1,7 +1,9 @@ +import 'package:collection/collection.dart' show PriorityQueue; + import '../../comparator.dart'; /// A priority queue implemented using a binary heap. -class Heap extends Iterable { +class Heap extends Iterable implements PriorityQueue { /// Constructs an empty max-heap with an optional [comparator]. To create a /// min-heap invert the comparator. Heap({Comparator? comparator}) @@ -38,27 +40,31 @@ class Heap extends Iterable { @override bool get isNotEmpty => _values.isNotEmpty; - /// Returns an [Iterator] over the underlying values. - @override - Iterator get iterator => _values.iterator; - /// Returns the last/largest value from this heap. - E get peek { + @override + E get first { _checkNotEmpty(); return _values[0]; } /// Adds a new value onto this heap. - void push(E value) { + @override + void add(E value) { _values.add(value); _siftDown(0, _values.length - 1); } /// Adds multiple new values onto this heap. - void pushAll(Iterable values) => values.forEach(push); + @override + void addAll(Iterable values) => values.forEach(add); + + /// Removes all objects from this heap. + @override + void clear() => _values.clear(); /// Removes and returns the last/largest value from this heap. - E pop() { + @override + E removeFirst() { _checkNotEmpty(); final value = _values.removeLast(); if (_values.isNotEmpty) { @@ -70,9 +76,21 @@ class Heap extends Iterable { return value; } - /// A pop immediately followed by a push. Contrary to [pushAndPop] this + /// Remove an element from the heap. + @override + bool remove(E value) => _values.remove(value); + + /// Removes all objects from this heap. + @override + Iterable removeAll() { + final result = _values.toList(); + _values.clear(); + return result; + } + + /// A pop immediately followed by a push. Contrary to [addAndRemoveFirst] this /// requires a non-empty heap and never returns `value`. - E popAndPush(E value) { + E removeFirstAndAdd(E value) { _checkNotEmpty(); final result = _values[0]; _values[0] = value; @@ -80,9 +98,9 @@ class Heap extends Iterable { return result; } - /// A push immediately followed by a pop. Contrary to [popAndPush] this + /// A push immediately followed by a pop. Contrary to [removeFirstAndAdd] this /// works on an empty heap and might directly return `value`. - E pushAndPop(E value) { + E addAndRemoveFirst(E value) { if (_values.isEmpty || _comparator(_values[0], value) < 0) { return value; } @@ -92,13 +110,17 @@ class Heap extends Iterable { return result; } - /// Removes all objects from this heap. - void clear() => _values.clear(); + @override + Iterator get iterator => _values.iterator; + + @override + Iterable get unorderedElements => _values; + + @override + List toUnorderedList() => _values.toList(); void _checkNotEmpty() { - if (_values.isEmpty) { - throw StateError('No element'); - } + if (_values.isEmpty) throw StateError('No element'); } void _siftDown(int start, int stop) { diff --git a/lib/src/collection/sortedlist.dart b/lib/src/collection/sortedlist.dart index c946830..15f23cd 100644 --- a/lib/src/collection/sortedlist.dart +++ b/lib/src/collection/sortedlist.dart @@ -1,10 +1,12 @@ import 'dart:collection' show ListBase; import 'dart:math'; +import 'package:collection/collection.dart' show PriorityQueue; + import '../../comparator.dart'; /// A sorted-list that remains sorted by a [Comparator] as elements get added. -class SortedList extends ListBase { +class SortedList extends ListBase implements PriorityQueue { /// Constructs an empty sorted-list with an optional `ordering`. SortedList({Comparator? comparator, bool growable = true}) : _values = List.empty(growable: growable), @@ -64,12 +66,37 @@ class SortedList extends ListBase { return true; } + @override + Iterable removeAll() { + final result = _values.toList(); + _values.clear(); + return result; + } + + @override + E removeAt(int index) => _values.removeAt(index); + + @override + E removeFirst() => _values.removeAt(0); + + @override + E removeLast() => _values.removeLast(); + + @override + void clear() => _values.clear(); + @override void sort([int Function(E a, E b)? compare]) => _throw(); @override void shuffle([Random? random]) => _throw(); + @override + Iterable get unorderedElements => _values; + + @override + List toUnorderedList() => _values.toList(); + static void _throw() => throw UnsupportedError('Cannot modify the order of a sorted list'); } diff --git a/lib/src/comparator/operations/smallest.dart b/lib/src/comparator/operations/smallest.dart index 97f0ebd..ff0c512 100644 --- a/lib/src/comparator/operations/smallest.dart +++ b/lib/src/comparator/operations/smallest.dart @@ -10,13 +10,13 @@ extension SmallestComparator on Comparator { List smallest(Iterable iterable, int k) { final heap = Heap(comparator: this); for (final each in iterable) { - heap.push(each); + heap.add(each); if (heap.length > k) { - heap.pop(); // drop the largest element + heap.removeFirst(); // drop the largest element } } final result = List.generate( - math.min(k, heap.length), (index) => heap.pop(), + math.min(k, heap.length), (index) => heap.removeFirst(), growable: false); result.reverseRange(0, result.length); return result; diff --git a/test/collection_test.dart b/test/collection_test.dart index efabfa8..3404fc6 100644 --- a/test/collection_test.dart +++ b/test/collection_test.dart @@ -665,7 +665,7 @@ void main() { group( 'pushAll', () => allHeapTests((List list, {Comparator? comparator}) => - Heap(comparator: comparator)..pushAll(list))); + Heap(comparator: comparator)..addAll(list))); }); group('iterable', () { group('chunked', () { @@ -4629,24 +4629,24 @@ void allHeapTests( expect(heap.isEmpty, isTrue); expect(heap.isNotEmpty, isFalse); expect(heap.length, 0); - expect(() => heap.peek, throwsStateError); - expect(() => heap.pop(), throwsStateError); - expect(() => heap.popAndPush('Hello'), throwsStateError); - expect(heap.pushAndPop('World'), 'World'); + expect(() => heap.first, throwsStateError); + expect(() => heap.removeFirst(), throwsStateError); + expect(() => heap.removeFirstAndAdd('Hello'), throwsStateError); + expect(heap.addAndRemoveFirst('World'), 'World'); expect(heap.length, 0); }); - test('popAndPush', () { + test('removeFirstAndAdd', () { final heap = createHeap(['Olivia', 'Emma', 'Sophia']); - expect(heap.popAndPush('Amelia'), 'Sophia'); - expect(heap.popAndPush('Nora'), 'Olivia'); - expect(heap.popAndPush('Violet'), 'Nora'); + expect(heap.removeFirstAndAdd('Amelia'), 'Sophia'); + expect(heap.removeFirstAndAdd('Nora'), 'Olivia'); + expect(heap.removeFirstAndAdd('Violet'), 'Nora'); expect(heap.toList()..sort(), ['Amelia', 'Emma', 'Violet']); }); - test('pushAndPop', () { + test('addAndRemoveFirst', () { final heap = createHeap(['Olivia', 'Emma', 'Sophia']); - expect(heap.pushAndPop('Amelia'), 'Sophia'); - expect(heap.pushAndPop('Nora'), 'Olivia'); - expect(heap.pushAndPop('Violet'), 'Violet'); + expect(heap.addAndRemoveFirst('Amelia'), 'Sophia'); + expect(heap.addAndRemoveFirst('Nora'), 'Olivia'); + expect(heap.addAndRemoveFirst('Violet'), 'Violet'); expect(heap.toList()..sort(), ['Amelia', 'Emma', 'Nora']); }); test('clear', () { @@ -4670,8 +4670,8 @@ void allHeapTests( expect(heap.isNotEmpty, isTrue); expect(heap.length, source.length); final value = source.removeLast(); - expect(heap.peek, value); - expect(heap.pop(), value); + expect(heap.first, value); + expect(heap.removeFirst(), value); } expect(heap.isEmpty, isTrue); expect(heap.isNotEmpty, isFalse); @@ -4741,6 +4741,11 @@ void allSortedListTests( final list = createSortedList([5, 1, 3], growable: false); expect(() => list.addAll([2, 4]), throwsUnsupportedError); }); + test('clear', () { + final list = createSortedList([5, 1, 3]); + list.clear(); + expect(list, isEmpty); + }); test('remove', () { final list = createSortedList([5, 1, 3]); expect(list, [1, 3, 5]); @@ -4753,6 +4758,34 @@ void allSortedListTests( final list = createSortedList([5, 1, 3], growable: false); expect(() => list.remove(3), throwsUnsupportedError); }); + test('removeAt', () { + final list = createSortedList([5, 1, 3]); + expect(list.removeAt(1), 3); + expect(list, [1, 5]); + }); + test('removeFirst', () { + final list = createSortedList([5, 1, 3]); + expect(list.removeFirst(), 1); + expect(list, [3, 5]); + }); + test('removeLast', () { + final list = createSortedList([5, 1, 3]); + expect(list.removeLast(), 5); + expect(list, [1, 3]); + }); + test('removeAll', () { + final list = createSortedList([5, 1, 3]); + expect(list.removeAll(), [1, 3, 5]); + expect(list, isEmpty); + }); + test('toUnorderedList', () { + final list = createSortedList([5, 1, 3]); + expect(list.toUnorderedList(), [1, 3, 5]); + }); + test('unorderedElements', () { + final list = createSortedList([5, 1, 3]); + expect(list.unorderedElements, [1, 3, 5]); + }); test('stress', () { final random = Random(6412); final numbers = {};