数据结构与算法——堆 特点与算法实现(二)
1. 建立堆算法实现
堆数据结构的定义
1 #define DEFAULT_CAPCITY 128 2 3 typedef struct _Heap 4 { 5 int *arr; //存储堆元素的数组 6 int size; //当前已存储的元素个数 7 int capacity; //当前存储的容量 8 }Heap;
建(最大)堆
1 bool initHeap(Heap &heap, int *orginal, int size); 2 static void buildHeap(Heap &heap); 3 static void adjustDown(Heap &heap, int index); 4 5 /*初始化堆*/ 6 bool initHeap(Heap &heap, int *orginal, int size) 7 { 8 int capacity = DEFAULT_CAPCITY>size? DEFAULT_CAPCITY:size; 9 10 heap.arr = new int[capacity]; 11 if(!heap.arr) return false; 12 13 heap.capacity = capacity; 14 heap.size = 0; 15 16 //如果存在原始数据则构建堆 17 if(size>0) 18 { 19 memcpy(heap.arr, orginal, size*sizeof(int)); 20 heap.size = size; 21 buildHeap(heap); 22 } 23 else 24 { 25 heap.size = 0; 26 } 27 return true; 28 } 29 30 /*将当前的节点和子节点调整成最大堆*/ 31 void adjustDown(Heap &heap, int index) 32 { 33 int cur=heap.arr[index]; //当前待调整的节点 34 int parent,child; 35 36 /*判断否存在大于当前节点子节点,如果不存在 ,则堆本身是平衡的,不需要调整; 37 如果存在,则将最大的子节点与之交换,交换后,如果这个子节点还有子节点, 38 则要继续按照同样的步骤对这个子节点进行调整*/ 39 for(parent=index; (parent*2+1)<heap.size; parent=child) 40 { 41 child=parent*2+1; 42 //取两个子节点中的最大的节点 43 if(((child+1)<heap.size)&&(heap.arr[child]<heap.arr[child+1])) 44 { 45 child++; 46 } 47 //判断最大的节点是否大于当前的父节点 48 if(cur>=heap.arr[child]) 49 { 50 //不大于,则不需要调整,跳出循环 51 break; 52 } 53 else 54 { 55 //大于当前的父节点,进行交换,然后从子节点位置继续向下调整 56 heap.arr[parent]=heap.arr[child]; 57 heap.arr[child]=cur; 58 } 59 } 60 } 61 62 /* 从最后一个父节点(size/2-1 的位置)逐个往前调整所有父节点(直到根节点), 63 确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆 */ 64 void buildHeap(Heap &heap) 65 { 66 int i; 67 for(i=heap.size/2-1; i>=0; i--) 68 { 69 adjustDown(heap, i); 70 } 71 }
2. 插入新元素
将数字 99 插入到上面大顶堆中的过程如下:
1) 原始的堆,如图 a,对应的数组:{95, 93, 87, 92, 86, 82}
2) 将新进的元素插入到大顶堆的尾部,如下图 b 所示:
对应的数组:{95, 93, 87, 92, 86, 82, 99}
3) 此时最大堆已经被破坏,需要重新调整, 因加入的节点比父节点大,则新节点跟父节点调换即可,如图 c 所示;调整后,新节点如果比新的父节点小,则已经调整到位,如果比新的父节点大,则需要和父节点重新进 行交换,如图 d, 至此,最大堆调整完成。
3. 插入元素代码实现
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #define DEFAULT_CAPCITY 128 6 7 typedef struct _Heap 8 { 9 int *arr; //存储堆元素的数组 10 int size; //当前已存储的元素个数 11 int capacity; //当前存储的容量 12 }Heap; 13 14 bool initHeap(Heap &heap, int *orginal, int size); 15 bool insert(Heap &heap, int value); 16 static void buildHeap(Heap &heap); 17 static void adjustDown(Heap &heap, int index); 18 static void adjustUp(Heap &heap, int index); 19 20 /*初始化堆*/ 21 bool initHeap(Heap &heap, int *orginal, int size) 22 { 23 int capacity = DEFAULT_CAPCITY>size ? DEFAULT_CAPCITY : size; 24 heap.arr = new int[capacity]; 25 if (!heap.arr) return false; 26 heap.capacity = capacity; 27 heap.size = 0; 28 29 //如果存在原始数据则构建堆 30 if(size > 0) 31 { 32 /*方式一: 直接调整所有元素 33 memcpy(heap.arr, orginal, size*sizeof(int)); 34 heap.size = size; 35 //建堆 36 buildHeap(heap); 37 */ 38 //方式二: 一次插入一个 39 for(int i=0; i<size; i++) 40 { 41 insert(heap, orginal[i]); 42 } 43 } 44 return true; 45 } 46 47 /* 从最后一个父节点(size/2-1 的位置)逐个往前调整所有父节点(直到根节 48 点),确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆*/ 49 void buildHeap(Heap &heap) 50 { 51 int i; 52 for (i = heap.size / 2 - 1; i >= 0; i--) 53 { 54 adjustDown(heap, i); 55 } 56 } 57 58 /*将当前的节点和子节点调整成最大堆*/ 59 void adjustDown(Heap &heap, int index) 60 { 61 int cur = heap.arr[index];//当前待调整的节点 62 int parent, child; 63 64 /*判断否存在大于当前节点子节点,如果不存在 ,则堆本身是平衡的,不需要调整; 65 如果存在,则将最大的子节点与之交换,交换后,如果这个子节点还有子节点,则要继续 66 按照同样的步骤对这个子节点进行调整*/ 67 for (parent = index; (parent * 2 + 1)<heap.size; parent = child) 68 { 69 child = parent * 2 + 1; 70 //取两个子节点中的最大的节点 71 if (((child + 1)<heap.size) && (heap.arr[child]<heap.arr[child + 1])) 72 { 73 child++; 74 } 75 //判断最大的节点是否大于当前的父节点 76 if (cur >= heap.arr[child]) 77 { 78 //不大于,则不需要调整,跳出循环 79 break; 80 } 81 else 82 { 83 //大于当前的父节点,进行交换,然后从子节点位置继续向下调整 84 heap.arr[parent] = heap.arr[child]; 85 heap.arr[child] = cur; 86 } 87 } 88 } 89 90 /*将当前的节点和父节点调整成最大堆*/ 91 void adjustUp(Heap &heap, int index) 92 { 93 if(index<0 || index >= heap.size) 94 { 95 //大于堆的最大值直接 return 96 return; 97 } 98 while(index>0) 99 { 100 int temp = heap.arr[index]; 101 int parent = (index - 1) / 2; 102 if(parent >= 0) 103 { 104 //如果索引没有出界就执行想要的操作 105 if(temp > heap.arr[parent]) 106 { 107 heap.arr[index] = heap.arr[parent]; 108 heap.arr[parent] = temp; 109 index = parent; 110 } 111 else 112 { 113 //如果已经比父亲小 直接结束循环 114 break; 115 } 116 } 117 else 118 { 119 //越界结束循环 120 break; 121 } 122 } 123 } 124 125 /*最大堆尾部插入节点,同时保证最大堆的特性*/ 126 bool insert(Heap &heap, int value) 127 { 128 if (heap.size == heap.capacity) 129 { 130 fprintf(stderr, "栈空间耗尽!\n"); 131 return false; 132 } 133 int index = heap.size; 134 heap.arr[heap.size++] = value; 135 adjustUp(heap, index); 136 return true; 137 } 138 139 int main(void) 140 { 141 Heap hp; 142 int origVals[] = { 1, 2, 3, 87, 93, 82, 92, 86, 95 }; 143 int i = 0; 144 if(!initHeap(hp, origVals, sizeof(origVals)/sizeof(origVals[0]))) 145 { 146 fprintf(stderr, "初始化堆失败!\n"); 147 exit(-1); 148 } 149 for (i = 0; i<hp.size; i++) 150 { 151 printf("the %dth element:%d\n", i, hp.arr[i]); 152 } 153 insert(hp, 99); 154 printf("在堆中插入新的元素 99,插入结果:\n"); 155 for (i = 0; i<hp.size; i++) 156 { 157 printf("the %dth element:%d\n", i, hp.arr[i]); 158 } 159 160 system("pause"); 161 return 0; 162 }
4. 堆顶元素出列
如果我们将堆顶的元素删除,那么顶部有一个空的节点,怎么处理?
当插入节点的时候,我们将新的值插入数组的尾部。现在我们来做相反的事情:我们取出数组中的最后一个元 素,将它放到堆的顶部,然后再修复堆属性。
5. 堆顶元素出列算法实现
1 bool initHeap(Heap &heap, int *orginal, int size); 2 static void buildHeap(Heap &heap); 3 static void adjustDown(Heap &heap, int index); 4 5 /*初始化堆*/ 6 bool initHeap(Heap &heap, int *orginal, int size) 7 { 8 int capacity = DEFAULT_CAPCITY>size? DEFAULT_CAPCITY:size; 9 heap.arr = new int[capacity]; 10 if(!heap.arr) return false; 11 heap.capacity = capacity; 12 heap.size = 0; 13 14 //如果存在原始数据则构建堆 15 if(size>0) 16 { 17 memcpy(heap.arr, orginal, size*sizeof(int)); 18 heap.size = size; 19 buildHeap(heap); 20 } 21 else 22 { 23 heap.size = 0; 24 } 25 return true; 26 } 27 28 /*将当前的节点和子节点调整成最大堆*/ 29 void adjustDown(Heap &heap, int index) 30 { 31 int cur=heap.arr[index]; //当前待调整的节点 32 int parent,child; 33 /*判断否存在大于当前节点子节点,如果不存在 ,则堆本身是平衡的,不需要调整; 34 如果存在,则将最大的子节点与之交换,交换后,如果这个子节点还有子节点, 35 则要继续按照同样的步骤对这个子节点进行调整*/ 36 for(parent=index; (parent*2+1)<heap.size; parent=child) 37 { 38 child=parent*2+1; 39 //取两个子节点中的最大的节点 40 if(((child+1)<heap.size)&&(heap.arr[child]<heap.arr[child+1])) 41 { 42 child++; 43 } 44 //判断最大的节点是否大于当前的父节点 45 if(cur>=heap.arr[child]) 46 { 47 //不大于,则不需要调整,跳出循环 48 break; 49 } 50 else 51 { 52 //大于当前的父节点,进行交换,然后从子节点位置继续向下调整 53 heap.arr[parent]=heap.arr[child]; 54 heap.arr[child]=cur; 55 } 56 } 57 } 58 /* 从最后一个父节点(size/2-1 的位置)逐个往前调整所有父节点(直到根节点),确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆 */ 59 void buildHeap(Heap &heap) 60 { 61 int i; 62 for(i=heap.size/2-1; i>=0; i--) 63 { 64 adjustDown(heap, i); 65 } 66 }