[LintCode] Heapify

Given an integer array, heapify it into a min-heap array.

For a heap array A, A[0] is the root of heap, and for each A[i], A[i * 2 + 1] is the left child of A[i] and A[i * 2 + 2] is the right child of A[i].
 
Clarification

What is heap? 

  • Heap is a data structure, which usually have three methods: push, pop and top. where "push" add a new element to the heap, "pop" delete the minimum/maximum element in the heap, "top" return the minimum/maximum element.
What is heapify?
  • Convert an unordered integer array into a heap array. If it is min-heap, for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. 
What if there is a lot of solutions?
  • Return any of them.
Example

Given [3,2,1,4,5], return [1,2,3,4,5] or any legal heap array.

Challenge 

O(n) time complexity

 

This problem is about implementing a ascending priority queue ADT using min binary heap.

It should support O(log n) insertion, O(log n) delete min, O(1) get min. 

Min Heap is a tree with the following two special properties.

1. The key of a node must be <= the keys of its children nodes

2. It must form a complete binary tree. 

 

Heapify an array, O(n)

Leaf nodes always satisfy the min heap property so we only need to heapify the non-leaf nodes;

For a complete binary tree, if there are an odd number of nodes of 2 * n + 1, then there are 

n + 1 leaf nodes and n non-leaf nodes; If there are an even number of nodes of 2 * n, then there 

are n leaf nodes and n non-leaf nodes. So the biggest index of non-leaf nodes is array.length / 2 - 1 = n - 1. 

Starting from the last non-leaf node, all the way back to root, we percolate down each of these nodes.

 

Insert, O(log n)

1. Increase the heap size by 1

2. Add the new element at the end of the heap

3. Heapify this element from bottom to top(Percolate up)

    if the new element's index is k, its parent's index is (k - 1) / 2. 

 

Delete Min, O(log n)

1. Save the root element's value 

2. Copy the last element's value into root and delete the last element

3. Percolate Down the new root element.

 

Delete Arbitrary , O(log n) if we don't need to find the arbitrary element first; O(n) if we need to find it before deletion.

If we delete an arbitrary element that is in the middle of the heap(neither root nor leaf nodes), this element has both 

parent node and children nodes, so we still swap its value with the last element then delete the last element, but instead of just precolate down the last element in its new position, we need to heapify the whole heap, iterating all the non-leaf nodes.

1. Swap value with the last element 

2. Delete the last element

3. Percolate Down on all non-leaf nodes

 

The above arbitrary deletion algorithm takes O(n) time even if the location of the to-be deleted element is given. In fact, there is no need to percolate down on all non-leaf nodes. 

The following algorithm takes O(logn) time.

1. Swap the last node's value with the deletion node's value, then remove this last node. This step maintains the "complete binary tree" invariant.

2. Fix the invariant where for each node, its key must be <= both of its children nodes' keys.

  a. If there is no parent node for the replacement node or key of replacement node >= its parent node's key:  percolate down this replacement node;

  b. else we have key of replacement node < its parent node's key: percolate up this replacement node. 

 

 1 public class Solution {
 2     //heapify an array
 3     public void heapify(ArrayList<Integer> A) {
 4         if(A == null || A.size() <= 1)
 5         {
 6             return;
 7         }
 8         for(int i = A.size() / 2 - 1; i >= 0; i--)
 9         {
10             percolateDown(A, i);
11         }
12     }
13     private void percolateDown(ArrayList<Integer> A, int idx)
14     {
15         int minIdx = idx;
16         int leftChildIdx = 2 * idx + 1;
17         int rightChildIdx = 2 * idx + 2;
18         
19         if(leftChildIdx < A.size() && A.get(leftChildIdx) < A.get(minIdx))
20         {
21             minIdx = leftChildIdx;
22         }
23         if(rightChildIdx < A.size() && A.get(rightChildIdx) < A.get(minIdx))
24         {
25             minIdx = rightChildIdx;
26         }
27         
28         if(minIdx != idx)
29         {
30             int temp = A.get(idx);
31             A.set(idx, A.get(minIdx));
32             A.set(minIdx, temp);
33             percolateDown(A, minIdx);
34         }
35     }
36     //insert
37     public void insert(ArrayList<Integer> A, int val)
38     {
39         A.add(val);
40         //precolate up the new added element
41         int idx = A.size() - 1;
42         while(idx > 0 && val < A.get((idx - 1) / 2)){
43             A.set(idx, A.get(idx - 1) / 2);
44             idx = (idx - 1) / 2;
45         }
46         A.set(idx, val);
47     }
48     //delete min
49     public int deleteMin(ArrayList<Integer> A)
50     {
51         //swap the root element with the last element in A
52         int min = A.get(0);
53         A.set(0, A.get(A.size() - 1));
54         A.remove(A.size() - 1);
55         //precolate down the new root
56         percolateDown(A, 0);
57         return min;
58     }
59     
60     //delete arbitrary, assuming we know the idx of to be deleted element
61     public int delete(ArrayList<Integer> A, int deleteIdx)
62     {
63         int del = ArrayList.get(deleteIdx);
64         //swap the to be deleted element with the last element in A
65         //then remove the last element
66         A.set(deleteIdx, A.get(A.size() - 1));
67         A.remove(A.size() - 1);
68         
69         //need to heapify all the non-leaf nodes as the deleted element 
70         //has both parent and children nodes
71         for(int i = A.size() / 2 - 1; i >= 0; i++)
72         {
73             percolateDown(A, i);
74         }
75         return del;
76     }
77 }

 

Optimized O(logn) aribtrary deletion

 1 private void percolateUp(ArrayList<Integer> A, int idx) {
 2     if(idx < 0) {
 3         return;
 4     }
 5     int val = A.get(idx);
 6     while(idx > 0 && val < A.get((idx - 1) / 2)) {
 7         A.set(idx, A.get((idx - 1) / 2));
 8         idx = (idx - 1) / 2;
 9     }
10     A.set(idx, val);
11 }
12 public int delete(ArrayList<Integer> A, int deleteIdx) {
13     int del = ArrayList.get(deleteIdx);
14     A.set(deleteIdx, A.get(A.size() - 1));
15     A.remove(A.size() - 1);
16     int parentIdx = (deleteIdx - 1) / 2;
17     if(deleteIdx == 0 || A.get(deleteIdx) >= A.get(parentIdx)) {
18         percolateDown(A, deleteIdx);
19     }
20     else {
21         percolateUp(A, deleteIdx);
22     }
23     return del;
24 }

 

Related Problems

HeapSort

 

posted @ 2017-11-08 14:43  Review->Improve  阅读(388)  评论(0编辑  收藏  举报