-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtest_max_heap.py
More file actions
1346 lines (1248 loc) · 49.2 KB
/
test_max_heap.py
File metadata and controls
1346 lines (1248 loc) · 49.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# DO NOT MODIFY THIS FILE
# Run me via: python3 -m unittest test_max_heap
import unittest
import time
import random
from max_heap import MaxHeap
class TestMaxHeap(unittest.TestCase):
"""
Initialization
"""
def test_instantiation(self):
"""
Test 1: A MaxHeap exists.
"""
try:
MaxHeap()
except NameError:
self.fail("Could not instantiate MaxHeap.")
# """
# A heap stores its data in an array, such as a Python list.
# """
# def test_internal_data(self):
# """
# Test 2: A MaxHeap uses an array (a dynamic array / Python list) to store its data.
# """
# h = MaxHeap()
# self.assertEqual(list, type(h._data))
# self.assertEqual(0, len(h._data))
# """
# Size is the number of items in the heap.
# """
# def test_size_initial(self):
# """
# Test 3: The _size() of a new heap is 0.
# """
# h = MaxHeap()
# self.assertEqual(0, h._size())
# def test_size_data(self):
# """
# Test 4: The _size() of a heap is equal to the number of values in its list.
# """
# h = MaxHeap()
# h._data.append('fake')
# self.assertEqual(1, h._size())
# h._data.append('fake')
# self.assertEqual(2, h._size())
# h._data.pop()
# self.assertEqual(1, h._size())
# """
# Emptiness. A warm-up. Good to know, and a handy abstraction that you might
# use elsewhere.
# """
# def test_empty_initial(self):
# """
# Test 5: A new heap is empty.
# Hint: _size is a convenient abstraction, and helps avoid repetitive code.
# """
# h = MaxHeap()
# self.assertTrue(h._is_empty())
# def test_not_empty(self):
# """
# Test 6: A heap is not empty if there are items in its data list.
# """
# h = MaxHeap()
# h._data.append('fake')
# self.assertFalse(h._is_empty())
# h._data.append('fake')
# self.assertFalse(h._is_empty())
# def test_empty(self):
# """
# Test 7: A heap with no items in its data list is empty.
# """
# h = MaxHeap()
# h._data.append('fake')
# h._data.append('fake')
# h._data = []
# self.assertTrue(h._is_empty())
# """
# Last index. The index of the last element in the heap.
# Later, when deleting from a heap, the first step in the deletion algorithm
# moves the last element to the root position. So this will be handy.
# """
# def test_last_index_initial(self):
# """
# Test 8: The 'last index' of an empty heap happens to be -1.
# Hint: Easy to calculate if you know its size.
# """
# h = MaxHeap()
# self.assertEqual(-1, h._last_index())
# def test_last_index_one(self):
# """
# Test 9: The last index of a heap with one element is 0.
# Hint: Easy, if you know how to determine the last index of a list.
# """
# h = MaxHeap()
# h._data.append('fake')
# self.assertEqual(0, h._last_index())
# def test_last_index_two(self):
# """
# Test 10: The last index of a heap with two elements is 1.
# """
# h = MaxHeap()
# h._data.append('fake')
# h._data.append('fake')
# self.assertEqual(1, h._last_index())
# def test_last_index_42(self):
# """
# Test 11: The last index of a heap with forty-two elements is 41.
# """
# h = MaxHeap()
# for _ in range(42):
# h._data.append('fake')
# self.assertEqual(41, h._last_index())
# """
# Value at an index. It's handy to grab a value at a particular index, so lets
# encapsulate this work into a method.
# """
# def test_value_at_zero(self):
# """
# Test 12: The value at index 0 is the value of the 0th item in the heap's data list.
# """
# h = MaxHeap()
# value = fake_value()
# h._data.append(value)
# self.assertEqual(value, h._value_at(0))
# def test_value_at(self):
# """
# Test 13: The value at index i is the value of the i'th item in the heap's data list.
# """
# h = MaxHeap()
# value = fake_value()
# h._data.append(value)
# self.assertEqual(value, h._value_at(0))
# value = fake_value()
# h._data.append(value)
# self.assertEqual(value, h._value_at(1))
# for i in range(2, 9):
# value = fake_value()
# h._data.append(value)
# self.assertEqual(value, h._value_at(i))
# def test_value_at_invalid_index(self):
# """
# Test 14: _value_at raises an IndexError when the index is out of bounds.
# """
# h = MaxHeap()
# self.assertRaises(IndexError, h._value_at, 0)
# h._data.append('fake')
# self.assertRaises(IndexError, h._value_at, 1)
# self.assertRaises(IndexError, h._value_at, 2)
# """
# Indexes of left child, right child, and parent.
# A heap stores values linearly in a list, but it's really a tree. The root is
# at 0, and its left child is at index 1, and its right child is at index 2.
# The element at index 1 has a left child at index 3, and a right at index 4.
# The element at index 2 has a left child at index 5, and a right at index 6.
# What's the formula for this?
# Hint: Draw it out.
# """
# def test_left_child_index(self):
# """
# Test 15: An element at index i has a left child at index ____.
# Hint: Know how the heap works. Look up and study the concept.
# """
# h = MaxHeap()
# # This method just calculates the index. It doesn't care about the data.
# self.assertEqual(1, h._left_child_index(0))
# self.assertEqual(3, h._left_child_index(1))
# self.assertEqual(5, h._left_child_index(2))
# self.assertEqual(7, h._left_child_index(3))
# self.assertEqual(8675309, h._left_child_index(4337654))
# def test_right_child_index(self):
# """
# Test 16: An element at index i has a right child at index ____.
# Hint: Know how the heap works. Look up and study the concept.
# """
# h = MaxHeap()
# # This method just calculates the index. It doesn't care about the data.
# self.assertEqual(2, h._right_child_index(0))
# self.assertEqual(4, h._right_child_index(1))
# self.assertEqual(6, h._right_child_index(2))
# self.assertEqual(8, h._right_child_index(3))
# self.assertEqual(5446, h._right_child_index(2722))
# def test_parent_index(self):
# """
# Test 17: An element at index i has a parent at index ___.
# Hints: Work this out instead of looking it up. Draw it.
# And, use integer division for natural flooring.
# Watch your order of operations.
# """
# h = MaxHeap()
# # This first one is nonsense, but is here for completeness.
# self.assertEqual(-1, h._parent_index(0))
# # The root's left child is at 1, so its parent is at index 0.
# self.assertEqual(0, h._parent_index(1))
# # The root's right child is at 2, so its parent is at index 0.
# self.assertEqual(0, h._parent_index(2))
# self.assertEqual(1, h._parent_index(3))
# self.assertEqual(1, h._parent_index(4))
# self.assertEqual(2, h._parent_index(5))
# self.assertEqual(2, h._parent_index(6))
# self.assertEqual(3, h._parent_index(7))
# self.assertEqual(4337654, h._parent_index(8675309))
# self.assertEqual(2722, h._parent_index(5446))
# """
# Left child, right child, and parent _values_.
# Now that we know that calculating the left, right and parent indexes given
# any element's index, retrieving the values there is easy.
# Hint: Use your previous abstractions. Don't repeat yourself.
# """
# def test_parent(self):
# """
# Test 18: Given an index i, the parent is the value at the 'parent index' of i.
# Hint: The phrase above is nearly identical to the code, if you use your
# abstractions.
# """
# h = MaxHeap()
# fake_root = fake_value()
# fake_left_child = fake_value()
# fake_right_child = fake_value()
# fake_left_left_child = fake_value()
# fake_left_right_child = fake_value()
# h._data.append(fake_root)
# h._data.append(fake_left_child)
# h._data.append(fake_right_child)
# h._data.append(fake_left_left_child)
# h._data.append(fake_left_right_child)
# self.assertEqual(fake_root, h._parent(1))
# self.assertEqual(fake_root, h._parent(2))
# self.assertEqual(fake_left_child, h._parent(3))
# self.assertEqual(fake_left_child, h._parent(4))
# def test_parent_invalid(self):
# """
# Test 19: Retrieving the parent value for an index without a parent is invalid.
# """
# h = MaxHeap()
# self.assertRaises(IndexError, h._parent, 0)
# self.assertRaises(IndexError, h._parent, 1)
# self.assertRaises(IndexError, h._parent, 2)
# h._data.append('fake')
# try:
# h._parent(1)
# h._parent(2)
# except IndexError:
# self.fail("Could not retrieve parent properly.")
# for i in range(3, 9):
# self.assertRaises(IndexError, h._parent, i)
# def test_left_child_none(self):
# """
# If the 'left child index' of an element at index i exceeds the bounds of
# the data list, just return None.
# Hint: Draw both a 5-element array and tree. What is the value of the left
# child of the third (index 2) element? And the fourth? And the fifth?
# """
# h = MaxHeap()
# h._data.append('fake')
# h._data.append('fake')
# h._data.append('fake')
# self.assertIsNone(h._left_child(1))
# self.assertIsNone(h._left_child(2))
# h._data.append('fake')
# h._data.append('fake')
# self.assertIsNone(h._left_child(2))
# self.assertIsNone(h._left_child(3))
# self.assertIsNone(h._left_child(4))
# def test_left_child(self):
# """
# Test 20: Given an index i, the left child is the value at the 'left child index'
# of i.
# Hint: The phrase above is nearly identical to the code, if you use your
# abstractions.
# """
# h = MaxHeap()
# fake_root = fake_value()
# fake_left_child = fake_value()
# fake_right_child = fake_value()
# fake_left_left_child = fake_value()
# fake_left_right_child = fake_value()
# h._data.append(fake_root)
# h._data.append(fake_left_child)
# h._data.append(fake_right_child)
# h._data.append(fake_left_left_child)
# h._data.append(fake_left_right_child)
# self.assertEqual(fake_left_child, h._left_child(0))
# self.assertEqual(fake_left_left_child, h._left_child(1))
# self.assertIsNone(h._left_child(2))
# self.assertIsNone(h._left_child(3))
# self.assertIsNone(h._left_child(4))
# def test_right_child_none(self):
# """
# Test 21: If the 'right child index' of an element at index i exceeds the bounds of
# the data list, just return None.
# Hint: Draw both a 5-element array and tree. What is the value of the right
# child of the third (index 2) element? And the fourth? And the fifth?
# """
# h = MaxHeap()
# h._data.append('fake')
# h._data.append('fake')
# h._data.append('fake')
# self.assertIsNone(h._right_child(2))
# h._data.append('fake')
# h._data.append('fake')
# self.assertIsNone(h._right_child(2))
# self.assertIsNone(h._right_child(3))
# self.assertIsNone(h._right_child(4))
# def test_right_child(self):
# """
# Test 22: Given an index i, the right child is the value at the 'right child index'
# of i.
# Hint: The phrase above is nearly identical to the code, if you use your
# abstractions.
# """
# h = MaxHeap()
# fake_root = fake_value()
# fake_left_child = fake_value()
# fake_right_child = fake_value()
# fake_left_left_child = fake_value()
# fake_left_right_child = fake_value()
# h._data.append(fake_root)
# h._data.append(fake_left_child)
# h._data.append(fake_right_child)
# h._data.append(fake_left_left_child)
# h._data.append(fake_left_right_child)
# self.assertEqual(fake_right_child, h._right_child(0))
# self.assertEqual(fake_left_right_child, h._right_child(1))
# self.assertIsNone(h._right_child(2))
# self.assertIsNone(h._right_child(3))
# self.assertIsNone(h._right_child(4))
# """
# Left child and right child presence. These will be handy later.
# """
# def test_has_left_child(self):
# """
# Test 23: True when an element's left child isn't None. Otherwise False.
# """
# h = MaxHeap()
# fake_root = fake_value()
# fake_left_child = fake_value()
# fake_right_child = fake_value()
# fake_left_left_child = fake_value()
# fake_left_right_child = fake_value()
# h._data.append(fake_root)
# h._data.append(fake_left_child)
# h._data.append(fake_right_child)
# h._data.append(fake_left_left_child)
# h._data.append(fake_left_right_child)
# self.assertTrue(h._has_left_child(0))
# self.assertTrue(h._has_left_child(1))
# self.assertFalse(h._has_left_child(2))
# self.assertFalse(h._has_left_child(3))
# self.assertFalse(h._has_left_child(4))
# def test_has_right_child(self):
# """
# Test 24: True when an element's right child isn't None. Otherwise False.
# """
# h = MaxHeap()
# fake_root = fake_value()
# fake_left_child = fake_value()
# fake_right_child = fake_value()
# fake_left_left_child = fake_value()
# h._data.append(fake_root)
# h._data.append(fake_left_child)
# h._data.append(fake_right_child)
# h._data.append(fake_left_left_child)
# self.assertTrue(h._has_right_child(0))
# self.assertFalse(h._has_right_child(1))
# self.assertFalse(h._has_right_child(2))
# self.assertFalse(h._has_right_child(3))
# self.assertFalse(h._has_right_child(4))
# """
# Index of the greater child.
# When we remove the root of a MaxHeap, we move the last element in the data
# list to the root position. Then, the new root gets 'pushed down' its left
# branch or its right branch. Which branch? The branch that is 'led' by the child
# with the greater value. Remember, a binary heap is not a bst: the rule is
# only that both children must be smaller than their parent.
# Therefore, it's handy to be able to determine the index of the larger child.
# """
# def test_greater_child_index_one(self):
# """
# Test 25: The 'greater child index' of an element without children is None.
# """
# h = MaxHeap()
# h._data.append('fake')
# self.assertIsNone(h._greater_child_index(0))
# def test_greater_child_index_left_only(self):
# """
# Test 26: The 'greater child index' of an element with just a left child (no right
# child) returns the index of that left child.
# """
# h = MaxHeap()
# h._data.append('fake')
# h._data.append('fake')
# self.assertEqual(1, h._greater_child_index(0))
# def test_greater_child_index_left(self):
# """
# Test 27: The 'greater child index' of an element with a left and right child, is
# the index of the left child when it has a value greater than or equal to
# the right child.
# """
# h = MaxHeap()
# h._data.append(10)
# h._data.append(5)
# h._data.append(1)
# self.assertEqual(1, h._greater_child_index(0))
# def test_greater_child_index_right(self):
# """
# Test 28: The 'greater child index' of an element with a left and right child, is
# the index of the right child when it has a larger value than the left child.
# Hint: Refine your logic. What are the possible states? No children, a
# left but no right, or a left and a right.
# """
# h = MaxHeap()
# h._data.append(10)
# h._data.append(1)
# h._data.append(5)
# self.assertEqual(2, h._greater_child_index(0))
# """
# The max-heap property. Obey.
# A MaxHeap has one rule that makes it a MaxHeap: An item in the tree has a
# value that is greater than (or equal to) both its left and right children.
# We care about this 'locally', meaning, given an index i, we verify that the
# value at i is greater than its left child value and its right child value.
# This is not recursive! Checking the whole tree is not the job of this method.
# It only checks the max-heap property at a given index, by checking the value
# at that index and the values of the immediate children at that index.
# """
# def test_heap_property_one(self):
# """
# Test 29: A heap with one element obeys the max-heap property.
# """
# h = MaxHeap()
# h._data.append('fake')
# self.assertTrue(h._obeys_heap_property_at_index(0))
# def test_heap_property_two_violate(self):
# """
# Test 30: A heap with two elements, with a parent value less than its left child's
# value violates the max-heap property.
# """
# h = MaxHeap()
# h._data.append(5)
# h._data.append(10)
# # Index 0 (root / value 5) has a child with a value 10, so it violates.
# self.assertFalse(h._obeys_heap_property_at_index(0))
# # No children at 1, so it obeys here:
# self.assertTrue(h._obeys_heap_property_at_index(1))
# def test_heap_property_two_obey(self):
# """
# Test 31: A heap with two elements, with a parent value greater than its left
# child's value obeys the max-heap property.
# """
# h = MaxHeap()
# h._data.append(10)
# h._data.append(5)
# self.assertTrue(h._obeys_heap_property_at_index(0))
# self.assertTrue(h._obeys_heap_property_at_index(1))
# def test_heap_property_three_violate(self):
# """
# Test 32: A heap with three elements, with a parent value less than its right
# child's value or its left child's value violates the max-heap property.
# Hint: Refine your logic. What are the possible states? No children,
# a left child, or both a left and right child.
# """
# h = MaxHeap()
# h._data.append(10)
# h._data.append(5)
# h._data.append(42)
# self.assertFalse(h._obeys_heap_property_at_index(0))
# self.assertTrue(h._obeys_heap_property_at_index(1))
# self.assertTrue(h._obeys_heap_property_at_index(2))
# h._data = []
# h._data.append(10)
# h._data.append(42)
# h._data.append(5)
# self.assertFalse(h._obeys_heap_property_at_index(0))
# self.assertTrue(h._obeys_heap_property_at_index(1))
# self.assertTrue(h._obeys_heap_property_at_index(2))
# def test_heap_property_three_obey(self):
# """
# Test 33: A heap with three elements, with a parent value greater than its left
# child's value and its right child's value obeys the max-heap property.
# Hint: Refine your logic. What are the possible states? No children,
# a left child, or both a left and right child.
# """
# h = MaxHeap()
# h._data.append(10)
# h._data.append(5)
# h._data.append(1)
# self.assertTrue(h._obeys_heap_property_at_index(0))
# self.assertTrue(h._obeys_heap_property_at_index(1))
# self.assertTrue(h._obeys_heap_property_at_index(2))
# h._data = []
# h._data.append(10)
# h._data.append(1)
# h._data.append(5)
# self.assertTrue(h._obeys_heap_property_at_index(0))
# self.assertTrue(h._obeys_heap_property_at_index(1))
# self.assertTrue(h._obeys_heap_property_at_index(2))
# """
# Swap. Given the indexes of two items, swap their positions in the data list.
# This swaps their position in the conceptual tree. We'll only ever use it to
# swap a child with its parent, or vice-versa. But, this method shouldn't care.
# """
# def test_swap(self):
# """
# Test 34: Given an index a and an index b, swapping a with b moves b's value to a
# and a's value to b.
# Hint: A classic algorithm. Three lines.
# """
# h = MaxHeap()
# h._data.append(10)
# h._data.append(5)
# h._data.append(1)
# h._swap(0, 1)
# self.assertEqual(5, h._data[0])
# self.assertEqual(10, h._data[1])
# h._swap(0, 2)
# self.assertEqual(1, h._data[0])
# self.assertEqual(5, h._data[2])
# # We'll never swap siblings, but let's make sure swap is simple.
# h._swap(1, 2)
# self.assertEqual(5, h._data[1])
# self.assertEqual(10, h._data[2])
# """
# Sift down. An important heap algorithm.
# When we remove an element from the heap, we always remove the root. We then
# take the last element in the heap and make it the new root. But that new
# root's value will most definitely be smaller than it's childrens' values.
# And we don't want to violate the max-heap property. We rectify this by
# 'sifting' or 'pushing' down the new root until it is in a location where it
# is greater than its two children.
# But, what direction do we push the node down: the left or the right?
# The algorithm for a max-heap is that we push down the branch owned by the
# larger child.
# """
# def test_sift_down_one(self):
# """
# Test 35: Sifting down the root of a single-element heap is easy.
# Hint: Be naive for now.
# """
# h = MaxHeap()
# h._data.append(1)
# h._sift_down(0)
# self.assertEqual(1, h._data[0])
# def test_sift_down_two_stable(self):
# """
# Test 36: Sifting down an element in a two-element heap, when the element is larger
# than its child, is easy.
# Hint: Be naive for now.
# """
# h = MaxHeap()
# h._data.append(5)
# h._data.append(1)
# # Sifting down the root of this tree doesn't change anything.
# h._sift_down(0)
# self.assertEqual(5, h._data[0])
# self.assertEqual(1, h._data[1])
# # Sifting down the last element of this tree doesn't change anything, either.
# h._sift_down(1)
# self.assertEqual(5, h._data[0])
# self.assertEqual(1, h._data[1])
# def test_sift_down_three_stable(self):
# """
# Test 37: Sifting down an element in a three-element heap, when the element is larger
# than its children, is easy.
# Hint: Be naive for now.
# """
# h = MaxHeap()
# h._data.append(10)
# h._data.append(5)
# h._data.append(1)
# # Sifting down the root of this tree doesn't change anything.
# h._sift_down(0)
# self.assertEqual(10, h._data[0])
# self.assertEqual(5, h._data[1])
# self.assertEqual(1, h._data[2])
# # Sifting down the second element of this tree doesn't change anything.
# h._sift_down(1)
# self.assertEqual(10, h._data[0])
# self.assertEqual(5, h._data[1])
# self.assertEqual(1, h._data[2])
# # Sifting down the third element of this tree doesn't change anything.
# h._sift_down(2)
# self.assertEqual(10, h._data[0])
# self.assertEqual(5, h._data[1])
# self.assertEqual(1, h._data[2])
# def test_sift_down_two_unstable(self):
# """
# Test 38: Sifting down an element in a two-element heap, when the element is smaller
# than its child swaps the element with its child.
# Hint: A little more genuine now. Use your abstractions!
# Hint 2: If it obeys the heap property at that index, there's no work to do.
# """
# h = MaxHeap()
# h._data.append(1)
# h._data.append(5)
# # Sifting down the root of this tree swaps it with its child.
# h._sift_down(0)
# self.assertEqual(5, h._data[0])
# self.assertEqual(1, h._data[1])
# def test_sift_down_three_unstable_left(self):
# """
# Test 39: Sifting down an element in a three-element heap, swaps it with the larger
# of its two children.
# """
# h = MaxHeap()
# h._data.append(1)
# h._data.append(10)
# h._data.append(5)
# # Sifting down the root of this tree swaps it with its left child.
# h._sift_down(0)
# self.assertEqual(10, h._data[0])
# self.assertEqual(1, h._data[1])
# self.assertEqual(5, h._data[2])
# def test_sift_down_three_unstable_right(self):
# """
# Test 40: Sifting down an element in a three-element heap, swaps it with the larger
# of its two children.
# Hint: Review your handy helper methods. And use them.
# """
# h = MaxHeap()
# h._data.append(1)
# h._data.append(5)
# h._data.append(10)
# # Sifting down the root of this tree swaps it with its right child.
# h._sift_down(0)
# self.assertEqual(10, h._data[0])
# self.assertEqual(5, h._data[1])
# self.assertEqual(1, h._data[2])
# # But, in a larger heap, an element may need to be sifted down further than
# # one level. Time to be recursive.
# def test_sift_down_left(self):
# """
# STest 41: ifting down an element swaps it with the larger of its two children,
# and continues to sift down that element in its new position and its new
# children, until it is in a position where it obeys the heap property.
# Hint: This might only require one more line of code, if expressed recursively.
# """
# h = MaxHeap()
# h._data.append(2)
# h._data.append(10)
# h._data.append(9)
# h._data.append(5)
# h._data.append(4)
# h._data.append(7)
# h._data.append(6)
# h._data.append(1)
# h._sift_down(0)
# self.assertEqual(10, h._data[0])
# self.assertEqual(5, h._data[1])
# self.assertEqual(9, h._data[2])
# self.assertEqual(2, h._data[3])
# self.assertEqual(4, h._data[4])
# self.assertEqual(7, h._data[5])
# self.assertEqual(6, h._data[6])
# self.assertEqual(1, h._data[7])
# def test_sift_down(self):
# """
# Test 42: Sifting down an element swaps it with the larger of its two children,
# and continues to sift down that element in its new position and its new
# children, until it is in a position where it obeys the heap property.
# Hint: This might only require one more line of code, if expressed recursively.
# """
# h = MaxHeap()
# h._data.append(2)
# h._data.append(9)
# h._data.append(10)
# h._data.append(5)
# h._data.append(4)
# h._data.append(7)
# h._data.append(6)
# h._data.append(1)
# h._sift_down(0)
# self.assertEqual(10, h._data[0])
# self.assertEqual(9, h._data[1])
# self.assertEqual(7, h._data[2])
# self.assertEqual(5, h._data[3])
# self.assertEqual(4, h._data[4])
# self.assertEqual(2, h._data[5])
# self.assertEqual(6, h._data[6])
# self.assertEqual(1, h._data[7])
# """
# Sift up. The second important heap algorithm.
# When we add a new element to a heap, we always add it as the last element
# in the list. Conceptually, we're adding elements to a tree from the top down,
# and left to right, one level of the tree at a time. Adding a new element to
# a heap adds a new leaf node to the tree. But, that leaf node's value might
# be greater than its parent, which violates the heap property.
# We rectify this by 'sifting up' the new element until it is in a location
# where it is less than its parent.
# """
# def test_sift_up_one(self):
# """
# Test 43: Sifting up the root is easy.
# Hint: Be naive for now.
# """
# h = MaxHeap()
# h._data.append(1)
# h._sift_up(0)
# self.assertEqual(1, h._data[0])
# def test_sift_up_two_stable(self):
# """
# Test 44: Sifting up an element in a two-element heap, when the element is smaller
# than its parent, is easy.
# Hint: Be naive for now.
# """
# h = MaxHeap()
# h._data.append(5)
# h._data.append(1)
# # Sifting up the root of this tree doesn't change anything.
# h._sift_up(0)
# self.assertEqual(5, h._data[0])
# self.assertEqual(1, h._data[1])
# # Sifting up the last element of this tree doesn't change anything, either.
# h._sift_up(1)
# self.assertEqual(5, h._data[0])
# self.assertEqual(1, h._data[1])
# def test_sift_up_three_stable(self):
# """
# Test 45: Sifting up an element in a three-element heap, when the element is smaller
# than its parent, is easy.
# Hint: Be naive for now.
# """
# h = MaxHeap()
# h._data.append(10)
# h._data.append(5)
# h._data.append(1)
# # Sifting up the root of this tree doesn't change anything.
# h._sift_up(0)
# self.assertEqual(10, h._data[0])
# self.assertEqual(5, h._data[1])
# self.assertEqual(1, h._data[2])
# # Sifting up the left leaf of this tree doesn't change anything.
# h._sift_up(1)
# self.assertEqual(10, h._data[0])
# self.assertEqual(5, h._data[1])
# self.assertEqual(1, h._data[2])
# # Sifting up the right leaft of this tree doesn't change anything.
# h._sift_up(2)
# self.assertEqual(10, h._data[0])
# self.assertEqual(5, h._data[1])
# self.assertEqual(1, h._data[2])
# def test_sift_up_two_unstable(self):
# """
# Test 46: Sifting up an element in a two-element heap, when the element is larger
# than its parent swaps the element with its parent.
# Hint: A little more genuine now. Refine your logic. Use your abstractions.
# """
# h = MaxHeap()
# h._data.append(1)
# h._data.append(5)
# # Sifting up the leaf of this tree swaps it with its parent.
# h._sift_up(1)
# self.assertEqual(5, h._data[0])
# self.assertEqual(1, h._data[1])
# def test_sift_up_three_unstable_left(self):
# """
# Test 47: Sifting up an element in a three-element heap, when the element is
# larger than its parent, swaps it with its parent.
# """
# h = MaxHeap()
# h._data.append(1)
# h._data.append(10)
# h._data.append(5)
# # Sifting up the left leaf of this tree swaps it with its parent.
# h._sift_up(1)
# self.assertEqual(10, h._data[0])
# self.assertEqual(1, h._data[1])
# self.assertEqual(5, h._data[2])
# def test_sift_up_three_unstable_right(self):
# """
# Test 48: Sifting up an element in a three-element heap, when the leaf is
# greater than its parent, swaps it with its parent.
# Hint: Refine your logic.
# """
# h = MaxHeap()
# h._data.append(1)
# h._data.append(5)
# h._data.append(10)
# # Sifting up the right leaf of this tree swaps it with its parent.
# h._sift_up(2)
# self.assertEqual(10, h._data[0])
# self.assertEqual(5, h._data[1])
# self.assertEqual(1, h._data[2])
# # But, in a larger heap, an element may need to be sifted up further than
# # one level. Time to be recursive.
# def test_sift_up_to_root(self):
# """
# Test 49: Sifting up an element swaps it with its parent when appropriate, and
# continues to sift up that element from its new position, until it either
# becomes the root or its parent's value is larger than its value.
# Hint: Think of the stopping condition first.
# """
# h = MaxHeap()
# h._data.append(10)
# h._data.append(9)
# h._data.append(8)
# h._data.append(7)
# h._data.append(6)
# h._data.append(5)
# h._data.append(4)
# h._data.append(3)
# h._data.append(2)
# h._data.append(1)
# h._data.append(42)
# h._sift_up(10)
# self.assertEqual(42, h._data[0])
# self.assertEqual(10, h._data[1])
# self.assertEqual(8, h._data[2])
# self.assertEqual(7, h._data[3])
# self.assertEqual(9, h._data[4])
# self.assertEqual(5, h._data[5])
# self.assertEqual(4, h._data[6])
# self.assertEqual(3, h._data[7])
# self.assertEqual(2, h._data[8])
# self.assertEqual(1, h._data[9])
# self.assertEqual(6, h._data[10])
# def test_sift_up(self):
# """
# Test 50: Sifting up an element swaps it with its parent when appropriate, and
# continues to sift up that element from its new position, until it either
# becomes the root or its parent's value is larger than its value.
# """
# h = MaxHeap()
# h._data.append(600)
# h._data.append(500)
# h._data.append(8)
# h._data.append(7)
# h._data.append(6)
# h._data.append(5)
# h._data.append(4)
# h._data.append(3)
# h._data.append(2)
# h._data.append(1)
# h._data.append(42)
# h._sift_up(10)
# self.assertEqual(600, h._data[0])
# self.assertEqual(500, h._data[1])
# self.assertEqual(8, h._data[2])
# self.assertEqual(7, h._data[3])
# self.assertEqual(42, h._data[4])
# self.assertEqual(5, h._data[5])
# self.assertEqual(4, h._data[6])
# self.assertEqual(3, h._data[7])
# self.assertEqual(2, h._data[8])
# self.assertEqual(1, h._data[9])
# self.assertEqual(6, h._data[10])
# """
# Inserting a value. It's easy now.
# Adding a value to a heap appends a new element to the end of its data list,
# which is also conceptually adding a new leaf node. After adding the new
# leaf node, the heap must 'sift up' the new leaf in case it has a value larger
# than its parent.
# Hint: Two steps. Add the new value, and sift up. That's it.
# """
# def test_insert_empty(self):
# """
# Test 51: An empty MaxHeap stores a new value as the root. No algorithms necessary.
# """
# h = MaxHeap()
# h.insert(10)
# self.assertEqual(10, h._data[0])
# def test_insert_smaller_one(self):
# """
# Test 52: An inserted value that is smaller than the root becomes the left child.
# """
# h = MaxHeap()
# h.insert(10)
# h.insert(5)
# self.assertEqual(10, h._data[0])
# self.assertEqual(5, h._data[1])
# def test_insert_larger_one(self):
# """
# Test 53: An inserted value that is larger than the root becomes the root, and the
# root becomes the left child.
# """
# h = MaxHeap()
# h.insert(10)
# h.insert(15)
# self.assertEqual(15, h._data[0])
# self.assertEqual(10, h._data[1])
# def test_insert_smaller_two(self):
# """
# Test 54: An inserted value that is smaller than the root of a two-element MaxHeap
# becomes the right child.
# 10 10
# / => / \
# 5 5 1
# """
# h = MaxHeap()
# h.insert(10)
# h.insert(5)
# h.insert(1)
# self.assertEqual(10, h._data[0])
# self.assertEqual(5, h._data[1])
# self.assertEqual(1, h._data[2])
# def test_insert_larger_two(self):
# """
# Test 55: An inserted value that is larger than the root becomes the new root, and
# the old root becomes the last element in the tree.
# 10 15
# / => / \
# 5 5 10
# Hint: Remember, insertion is just two steps. Append the new leaf to the
# end, and sift that new leaf up.
# """
# h = MaxHeap()
# h.insert(10)
# h.insert(5)
# h.insert(15)
# self.assertEqual(15, h._data[0])
# self.assertEqual(5, h._data[1])
# self.assertEqual(10, h._data[2])
# def test_insert_stable(self):
# """
# Test 56: An inserted value that is smaller than its parent will remain in the new
# leaf position.
# 10 10
# / \ => / \
# 8 4 8 4
# / \ / \
# 3 4 1 2
# """
# h = MaxHeap()