[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

 

转载于:https://www.cnblogs.com/lz87/p/7006853.html

### 使用 `heapq.heapify` 的方法及其实现 在 Python 中,`heapq` 是一个用于处理堆数据结构的标准库模块。其中的 `heapify` 函数可以将普通的列表转换成最小堆(min-heap)。以下是关于该函数的具体说明: #### 什么是 `heapq.heapify` `heapq.heapify` 将任意列表原地调整为堆结构[^1]。具体来说,它会重新排列列表中的元素,使得父节点始终小于其子节点(对于最小堆而言),从而满足二叉堆性质。 调用方式如下: ```python import heapq data = [5, 7, 9, 1, 3] heapq.heapify(data) print(data) # 输出: [1, 3, 9, 7, 5] ``` 上述代码展示了如何通过 `heapq.heapify` 方法将原始列表 `[5, 7, 9, 1, 3]` 转换成最小堆形式。 #### 实现原理 `heapq.heapify` 的核心思想基于自底向上的堆化过程。算法从最后一个非叶子节点开始向前遍历,并对每个节点执行下沉操作(sift-down),直到整个数组被调整为有效的堆结构[^4]。 下面是手动模拟 `heapq.heapify` 功能的一个简单版本: ```python def heapify(arr): n = len(arr) # 自底向上构建堆 for i in range(n // 2 - 1, -1, -1): sift_down(arr, i, n) def sift_down(heap, startpos, endpos): root = startpos while (child := 2 * root + 1) < endpos: child_to_swap = child if child + 1 < endpos and heap[child] > heap[child + 1]: child_to_swap += 1 if heap[root] <= heap[child_to_swap]: break heap[root], heap[child_to_swap] = heap[child_to_swap], heap[root] root = child_to_swap # 测试 arr = [5, 7, 9, 1, 3] heapify(arr) print(arr) # 输出: [1, 3, 9, 7, 5] ``` 此代码片段定义了一个名为 `heapify` 的辅助函数以及内部使用的 `sift_down` 下沉逻辑来完成堆化的功能。 #### 应用场景 `heapq.heapify` 常见的应用包括但不限于以下几种情况: - 构建优先级队列; - 执行高效的选择排序变体——堆排序; - 寻找前 N 大或者前 N 小的数据项。 例如,在寻找最大的三个数时可利用 `heapq.nlargest` 方法,这实际上依赖于先创建一个小顶堆再逐步提取最大值的过程。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值