-1

I am reading the book, "Python from Novice to Expert" by Magnus Lie Hetland (Third Edition) and came across Heaps. There he discusses the sorting order of a heap list as "the order of the elements is important (even though it may look a bit haphazard.."

According to him the heap algorithm has 2 rules of ordering the elements:

1) Element at i is greater than element at position i//2

If one is not made then:

2) Element at position i is lower than elements at positions 2*i and 2*i+1

I ran a code checking these rules to see if they work all the time,

from heapq import *
from random import shuffle

data = list(range(10))
heap = []
shuffle(data)
for i in data:
    heappush(heap, i)
print(heap)
temp = False

#From p.240 
#The order of the elements isn’t as arbitrary as it seems. They aren’t in 
#strictly sorted order, but there is one
#guarantee made: the element at position i is always greater than the one 
#in position i // 2 (or, conversely,
#it’s smaller than the elements at positions 2 * i and 2 * i + 1). This is 
#the basis for the underlying heap
#algorithm. This is called the heap property.

for i in heap:
    print('___________')
    if heap[i] > heap[i//2]:
        print('First if: {}>{}'.format(heap[i],heap[i//2]))
        temp = True
        try:    
            if heap[i] < heap[2*i]:
                print('Second if: {}<{}'.format(heap[i],heap[i*2]))
                temp = True
        except IndexError:
                pass
        try:
            if heap[i] < heap[2*i+1]:
                print('Third if: {}<{}'.format(heap[i],heap[i*2+1]))
                temp = True
        except IndexError:
                pass
    else:
        try:    
            if heap[i] < heap[2*i]:
                print('Second if: {}<{}'.format(heap[i],heap[i*2]))
                temp = True
        except IndexError:
                pass
        try:
            if heap[i] < heap[2*i+1]:
                print('Third if: {}<{}'.format(heap[i],heap[i*2+1]))
                temp = True
        except IndexError:
                pass
    if not temp:
        print('No requirement was made')
    temp = False
    print('___________')

As expected there were inputs that achieved the goal and some not, such as:

[0, 1, 2, 3, 5, 8, 7, 9, 4, 6]
[0, 3, 1, 5, 4, 6, 2, 7, 8, 9]

My question is are there more rules for sorting when none of these rules apply?

sahasrara62
  • 10,069
  • 3
  • 29
  • 44
eladgl
  • 69
  • 8
  • I don't see where the requirements (the heap property) aren't met. – Michael Butscher Oct 03 '19 at 07:20
  • 1
    I don't understand the question. There are not two requirements, just one (expressed in two different ways). The heap linearly encodes a binary tree, where every parent is smaller than its children (or every child greater than its parent). This is true for both of your examples. Ex 1: 0 < 1, 0 < 2, 1 < 3, 1 < 5, 2 < 8, 2 < 7, 3 < 9, 3 < 4, 5 < 6. Ex 2: 0 < 3, 0 < 1, 3 < 5, 3 < 4, 1 < 6, 1 < 2, 5 < 7, 5 < 8, 4 < 9. This is much easier to see when you draw the tree. When the rule does not apply, you don't have a heap. What exactly are you asking? – Amadan Oct 03 '19 at 07:28
  • @MichaelButscher for the input [0, 3, 1, 5, 4, 6, 2, 7, 8, 9] index 6, is greater than index 3 and then the second rquiement isn't made – eladgl Oct 03 '19 at 07:35
  • @Amadan according to the author those are the rules and not parent is smaller than children and so on. The question is if those are the rules for sorting it and if not are there end rules for situations like this – eladgl Oct 03 '19 at 07:36
  • 1
    You are not understanding me. The way that heap encodes a binary tree is as follows: for any index `i`, `heap[i]` is a child of `heap[i//2]` (except when `i` is zero, which is the root node, without a parent). "My" rule is just a rewrite of the same rule into plain English, not a different rule: each child (`heap[i]`) is greater than its parent (`heap[i//2]`). This is _the only rule_ for a heap. – Amadan Oct 03 '19 at 07:40
  • 2
    Oh, I see where the confusion is. The rule is stated for mathematical arrays, where indices start with 1. In Python lists indices start at 0. The children of `heap[0]` is `heap[1]` and `heap[2]`, not `heap[0]` and `heap[1]`. Thus, if a child is at `heap[i]`, in Python heap the parent is at `heap[(i - 1) // 2]`, not at `heap[i // 2]`. Conversely, if a parent is at `heap[j]`, then its children are at `heap[j * 2 + 1]` and `heap[j * 2 + 2]`. – Amadan Oct 03 '19 at 07:47
  • @Amadan The last one solves this issue for me, all this time I thought how can 2 be greater than 5. Thank you – eladgl Oct 03 '19 at 07:55

1 Answers1

1

As mentioned in the comments, the rule you had is stated in the framework of arrays with 1-based indices. Python lists are 0-based, and thus

if a child is at heap[i], in Python heap the parent is at heap[(i - 1) // 2], not at heap[i // 2]. Conversely, if a parent is at heap[j], then its children are at heap[j * 2 + 1] and heap[j * 2 + 2]

This is easy to see if you actually take the time to draw the heap:

   Example 1          Example 2         Python Index      1-based Index
       0                  0                  0                  1
   1       2          3       1          1       2          2       3
 3   5   8   7      5   4   6   2      3   4   5   6      4   5   6   7
9 4 6              7 8 9              7 8 9              8 9 A
Amadan
  • 191,408
  • 23
  • 240
  • 301