User:Zelhar/Algorithms0 maman13

Q 4
First I define a function that calculates the real value of element i in a heap (as defined for this exercise) by summing up the elements along the path from i to the root

EXTRACT-VALUE(A,i) //extracts the original value of A[i] from the heap 01   x=0 02   for j=2 to i: //a telescopic sum that adds up to x=A[1]-A[i] 03       x=x+A[i] 04       i=PARENT(i) 05   x=A[1]-x 06   return x

This functions adds up the element in the path from node i up to the root. It has already been proved in the textbook that the height of a nearly complete binary tree of size i is lg(i), therefore The above function running time is θ(lg(i)).

HEAPIFY:
HEAPIFY(A, i)      //The original function 1 l = LEFT(i) 2 r = RIGHT(i) 3 if l ≤ heap-size[A] and A[l] > A[i]: 4   largest = l     5    else: largest = i     6 if r ≤ heap-size[A] and A[r] > A[largest]: 7   largest = r     8 if largest ≠ i:     9    exchange A[i] = A[largest] 10   HEAPIFY(A, largest)

The running time of the original function was shown to be θ(lg(n)) in the textbook.

HEAPIFY-M(A, i)    //The modified version 01   l=RIGHT(i) 02   r=LEFT(i) 03   if l ≤ heap-size[A] and A[l] > A[i]: 04       largest=l 05   else: 06       largest=i 07   if r ≤ heap-size[A] and A[r] > A[largest]: 08       largest=r 09   if largest ≠ i:    10        if LEFT(largest)<=heap-size[A]: 11           A[LEFT(larget)]=A[largest] - A[LEFT(larget)] //turn it into //the root of its sub-heap 12       if RIGHT(largest)<=heap-size[A]:                 //turn it into //the root of its sub-heap 13           A[RIGHT(larget)]=A[largest] - A[RIGHT(larget)] 14       exchange A[i] ↔ A[largest] 15       HEAPIFY-M(A, largest) 16   else:           //make A[i]'s sons contain the subtraction value. 17       A[l]=A[i]-A[l] 18       A[r]=A[i]-A[r] The modified function, HEPIFY-M has the same recursion pattern as the original, (they only differ in a θ(1) element of the non-recursive part. That is T(n) ≤ T(2n/3) + θ(1) = θ(lg(n))

BUILD-HEAP:
BUILD-HEAP(A)      //The Original 1 heap-size[A] = length[A] 2 for i = floor(length[A]/2) downto 1: 3      HEAPIFY(A, i)

BUILD-HEAP-M(A) //The modification 1  heap-size[A] = length[A] 2  for i = floor(length[A]/2) downto 1 : 3      HEAPIFY-M(A,I)

The modified function has asymptotically the same running time as the original since it only differ by calling to HEAPIFY-M instead of HEAPIFY but both functions has the same asymptotic running time as shown above.

HEAPSORT:
HEAPSORT(A)    //The Original 1 BUILD-HEAP(A) 2 for i = length[A] downto 2: 3   exchange A[1] ↔ A[i] 4   heap-size[A] ← heap-size[A] - 1 5   HEAPIFY(A, 1)

HEAPSORT-M(A) 1  BUILD-HEAP-M(A) 2  for i=length[A] downto 2: 3      r=RIGHT(1) 4      l=LEFT(1) 5      if r<=heap-size(A): //turn the right and left sons into roots of their respective sub-heaps 6          A[l]=A[1]-A[l] 7          A[r]=A[1]-A[r] 8      else-if l<=heap-size(A): 9          A[l]=A[1]-A[l] 10     A[i]=EXTRACT-VALUE(A,i) //extract the value of A[i] as defined above. 11     exchange(A[i],A[1]) 12     heap-size(A)=heap-size(A)-1 13     HEAPIFY-M(A,1)

There is a call to EXTRACT-VALUE inside the loop but its running time is asymptotically the same as that of HEAPIFY (and HEAPIFY-M) that is called inside the loop of the original HEAPSORT. The running time of HEAPSORT-M is therefor the same as the original, θ(nlg(n)).

HEAP-EXTRACT-MAX:
HEAP-EXTRACT-MAX(A)    //The Original 1 if heap-size[A] < 1: 2  error "heap underflow" 3 max = A[1] 4 A[1] = A[heap-size[A]] 5 heap-size[A] = heap-size[A] - 1 6 HEAPIFY(A, 1) 7 return max

HEAP-EXTRACT-M(A)      //the modification 1  if heap-size[A]<1: 2      "error underflow" 3  max=A[1] 4  A[1]=EXTRACT-VALUE(A,heap-size(A)) 5  heap-size[A] = heap-size[A] - 1 6  HEAPIFY-M(A,1) 7  return max

Since HEAPIFY, HEAPIFY-M and EXTRACT-VALUE all have running time of θ(lg(n)), the running time of the modified function is also (θ(lg(n))), same as the original.

HEAP-INSERT:
HEAP-INSERT(A,key)     //Original 1  heap-size(A)=heap-size(A)+1 2  i=heap-size(A) 3  while i>1 and A[PARENT(i)]1 and y<0: 06       A[i]=A[PARENT(i)] 07       k=RIGHT(i)+LEFT(i)-j 08       if k<=heap-size(i):  //k needs to be adjusted since its //grandfather become its father 09           A[k]=A[k]+A[j] 10       j=i 11       i=PARENT(i) 12       x=A(i)-x  //Extracts the value of A[PARENT(i)] 13       y=x-key 14   A[i]=y 15   A[RIGHT(i)]=key - EXTRACT-VALUE(A,RIGHT(i)) 16   A[LEFT(i)]=key - EXTRACT-VALUE(A,LEFT(i))

The while loop iterated along the path from the new leaf up to the root θ(lg(n)) cycles. Each iteration costs a constant time (its bound is independent of i and of the specific data). Outside the loop there is a call for the function EXTRACT-VALUE which takes time O(lg(n)). Therefore The running time for HEAP-INSERT is θ(lg(n)) asymptotically the same as the original.