索引堆
- 普通堆的问题
- Heapify的过程中改变了原数组元素的位置,性能消耗较高
- 原有元素改变位置后难以被索引找到(如原数组中保存着系统任务,Heapify后想提高原来id=6任务的优先级)
- 索引堆的引入
- 数据和索引分开表示
- 建堆过程:比较数据,交换索引,效率高
- 便于堆中数据的操作,如将进程号为7的任务优先级由28改为38
1 template<typename Item> 2 class IndexMaxHeap{ 3 private: 4 Item *data; //数据 5 int *indexes; //索引 6 int count; 7 int capacity; 8 9 // 比较新加入的子节点和父节点大小并交换,直到最顶 10 // O(logn) 11 void shiftUp(int k){ 12 while( k > 1 && data[indexes[k/2]] < data[indexes[k]] ){ 13 swap(data[k/2],data[k]); 14 k /= 2; 15 } 16 } 17 18 // 比较两个子节点,和大的交换,直到最底 19 // 复杂度O(logn) 20 void shiftDown(int k){ 21 // 是否有左孩子 22 while( 2*k <= count ){ 23 int j = 2*k; 24 // 是否有右孩子&&右孩子是否比左孩子大 25 if( j+1 <= count && data[indexes[j+1]] > data[indexes[j]]) j++; 26 if( data[indexes[k]] >= data[indexes[j]]) break; 27 swap(indexes[k] , indexes[j]); 28 k = j; 29 } 30 } 31 public: 32 // 构造一个空堆,可容纳capacity个元素 33 // 时间复杂度O(nlogn) 34 IndexMaxHeap(int capacity){ 35 data = new Item[capacity+1]; 36 indexes = new int[capacity +1]; 37 count = 0; 38 this -> capacity = capacity; 39 } 40 41 ~MaxHeap(){ 42 delete[] data; 43 delete[] indexes; 44 } 45 46 // 返回堆中元素个数 47 int size(){ 48 return count; 49 } 50 51 // 是否为空堆 52 bool isEmpty(){ 53 return count == 0; 54 } 55 56 // 插入新元素,shiftUp 57 // i为索引,传入的i对用户而言是从0开始的 58 void insert(int i, Item item){ 59 assert( count +1 <= capacity ); 60 assert( i + 1 >= 1 && i + 1 <= capacity); 61 62 i += 1; 63 data[i] = item; 64 indexes[count+1] = i; 65 66 count ++; 67 shiftUp(count+1); 68 } 69 70 // 从最大堆取出堆顶元素,shiftDown 71 Item extractMax(){ 72 assert( count > 0 ); 73 Item ret = data[indexes[1]]; 74 swap( indexes[1] , indexes[count] ); 75 count --; 76 shiftDown(1); 77 return ret; 78 } 79 80 // 获取最大堆的堆顶元素 81 Item getMax(){ 82 assert( count > 0 ); 83 return data[1]; 84 } 85 86 // 给定索引获得数据 87 Item getItem( int i ){ 88 assert( i + 1 >= 1 && i + 1 <= capacity ); 89 return data[i+1]; 90 } 91 92 //返回最大元素索引 93 Item extractMaxIndex(){ 94 assert( count > 0 ); 95 int ret = indexes[1] - 1; 96 swap( indexes[1] , indexes[count] ); 97 count --; 98 shiftDown(1); 99 return ret; 100 } 101 102 // 将最大索引堆中索引为i的元素修改为newItem 103 // 应用场景:修改优先队列中某个任务的优先级 104 // O(n+logn) 即 O(n) 105 void change( int i , Item newItem ){ 106 i += 1; 107 data[i] = newItem; 108 // 找到indexes[j] = i, j表示data[i]在堆中的位置 109 // 之后shiftUp(j), 再shiftDown(j) 110 for( int j = 1 ; j <= count ; j ++ ) 111 if( indexes[j] == i ){ 112 shiftUp(j); 113 shiftDown(j); 114 return; 115 } 116 } 117 };
- 为什么先shiftUp()再shiftDown():不知道是改堆头还是堆尾的元素