堆
优先队列
-
普通队列:先进先出;后进后出
-
优先队列:出队顺序和入队顺序无关,与优先级有关
-
适用于动态数据维护的情况
-
N个元素中选前M个,使用普通排序算法的时间复杂度为O(NlogN),而使用堆排序的时间复杂度为O(NlogM)
-
基本实现
-
经典实现:二叉堆(Binary Heap)
-
-
二叉堆的特点:
- 任何一个节点都小于等于其父节点——最大堆
- 是一棵完全二叉树:除了最后一层外,其它层必须满节点。最后一层节点都在最左侧。
-
数组实现二叉堆
-
-
数组实现的特点:若将根节点序号设为1,则左节点的序号是其父节点的二倍。右节点的序号是其父节点的二倍+1;下面的实现都将根节点序号设为1.
Shift Up(向堆中插入一个元素)
-
当数组末尾新插入一个元素后,需要将其安排在二叉堆中的合适的位置,这个过程叫做Shift Up
-
方法:不断判断该元素与其父节点的值的大小,若大于其父节点的值,则两者交换位置,直到其不再大于其父节点为止
-
void shiftUp(int data[], int count, int k){ while(k > 1 && data[k/2] < data[k]){ swap(data[k/2], data[k]); k/2; } }
Shift Down(从堆中取出一个元素)
-
当取出数组首元素之后,重新排列数组元素使其符合二叉堆的定义的过程叫做Shift Down
-
方法:将取出首元素后,将末尾元素放到首元素的位置,然后不断与其子节点中值较大的节点进行交换位置,直到其不再小于其任意一个子节点为止
-
// swap(data[1], data[count]); // count--; void shiftDown(int data[], int count, int k){ while(2*k <= count){ int j=2*k;// 在此轮循环中,data[k]和data[j]交换位置 if(j+1 <= count && data[j+1] > data[j]) j+=1; if(data[k]>=data[j]) break; swap(data[k], data[j]); k=j; } }
-
不断重复该操作相当于对该数组排序,称为堆排序
Heapify
-
将一个无序的数组重新排列,成为一个二叉堆的过程称为Heapify
-
-
方法:找到该数组的第一个非叶子节点(对于根节点索引为1的二叉堆来说,索引为count/2的节点即为第一个非叶子节点),即上图中的第5号节点22,不断对其及之前的节点(22、13、19、17、15)进行Shift Down操作
-
将N个元素逐一插入到一个空队中的时间复杂对为O(NlogN);Heapify的时间复杂度为O(N)
堆排序
-
步骤:使用Heapify将一个无序数组构建成堆,然后不断取出首元素,并使用ShiftDown使剩余元素仍然维持一个堆。
-
void heapSort(int arr[], int n) { // 重新申请一块内存 data = new int[n + 1]; for (int i = 0; i < n; i++) data[i + 1] = arr[i]; count = n; // Heapify for (int i = count / 2; i >= 1; i--) shiftDown(data,count, i); // 不断取出首元素 for (int i = n - 1; i >= 0; i--){ arr[i] = data[1]; swap(data[1], data[count]); count--; shiftDown(data, count, 1); } }
索引堆
-
使用索引堆的原因:
- 避免了大量的赋值操作,尤其是对于元素复杂的数据,会消耗较多时间
- 由于重新排序后原数据元素的位置会发生改变,因此很难通过原索引找到某个元素。
-
解决方法:
-
存储数据的时候多存储一份每个数据的索引,即另开辟一块跟原数组同等大小的数组,存储原数据的索引。由于只是整型数组,并不占很大内存。
-
-
在构建堆的时候,元素进行比较的还是data中的值,而交换的却是对应的index。这样构建完后,原数组data并没有改变,改变的只是index。这样就避免了对原数据的复制,只需要交换index整型数组。
-
-
对于该最大索引堆,首元素为index[1]对应的元素62,原数据的第一个元素仍然为15,方便索引。
-
-
反向索引:
-
除了要知道原数组第i个元素对应的值是多少外,还要知道该元素在堆中的索引,即排在堆中的位置。比如,想知道原数组第8个元素是30之外,还想要知道它在堆中的位置。
-
-
解决方法:另开辟一个数组,存放该元素在堆中的索引即可。
-
堆相关的其他问题
- 多路归并排序
- 其他堆:二项堆、斐波那契堆