本博客的代码的思想和图片参考:好大学慕课浙江大学陈越老师、何钦铭老师的《数据结构》
堆(Heap)
1:引子
1.1 需求
在一个操作系统中,CPU如何处理进程。如果按照时间先后排序,那么
对优先权较高的线程来说,就会出现问题。我们必须建立一种组织方式
假设我们每次从"队列"里面取出的元素,是按照优先权大小,而不是如"队列"
的时间顺序,如何进行组织?
1.2比较我们之前使用过的几种数据结构
1.2.1 数组
插入:总是在最后,时间复杂度为O(1)
删除:
查找最大或者最小元素,时间复杂度为O(n)
删除找到的最大最小元素,时间复杂度为O(n)
1.2.2 链表
插入:总是插入在链表的头部 时间复杂度O(1)
删除:
查找到最大或者最小元素,时间复杂度O(n)
删除元素,时间复杂度O(1)
1.2.3 有序数组
插入:
找到合适位置,时间复杂度O(n)或者O(log2 N)
移动元素位置:O(n)
删除:删去最后一个元素
1.2.4 有序链表
插入:找到合适的位置,时间复杂度O(n)
删除:删除首元素或者尾元素,时间复杂度O(1)
上面的数据结构至少都需要O(n)的时间复杂度,不是我们所需要的
我们使用二叉搜索树看看怎么样。
1.2.5 二叉搜索树
插入:找到元素的位置 时间复制度O(log2 N)
删除:最大最小元素在二叉搜索树的最左边或者最右边,时间复杂度O(log2 N)
这么一看,感觉还是可以的,但是我们发现,我们每次删除的都是最大(最小)元素
那么如果一直这么操作,这棵树会歪掉。我们得重新定义树的结构要求。
2 引入堆的概念
2.1 堆的特性
结构性:使用数组来表示完全二叉树的特性
有序性:任意节点的关键字是其子树所有节点的最大值(最小值)
最大堆(MaxHeap),也称“大顶堆”:最大值
最小堆(MinHeap),也称“小顶堆”:最小值
2.2 堆的演示图
堆的示意图:
不是堆的示意图:
2.3 堆的要求是
从根节点到任意节点的路径上节点序列都有序
2.4 堆的数据结构的定义
1 typedef int elementType; 2 3 4 5 typedef struct heapStruct *maxHeap; 6 7 struct heapStruct{ 8 9 elementType *elementArray;/*定义存储元素的数组*/ 10 11 int size;/*堆当前元素的个数*/ 12 13 int capacity;/*堆的最大容量*/ 14 15 16 17 };
2.5 堆的操作集合
类型名称: 最大堆( MaxHeap)
数据对象集: 完全二叉树,每个结点的元素值不小于其子结点的元素值
操作集:最大堆H Î MaxHeap,元素item Î ElementType,主要操作有:
2.5.1 创建一个空的最大堆
MaxHeap Create( int MaxSize );
算法思想:
a.分配一个内存空间给 heapStruct
b.在分配 (MaxSize+1) * elementType 的数组空间给 elementArray
c.设置 size=0
d.设置capacity=MaxSize
e.我们设置了MaxSize+1个元素,我们让元素的角标从1开始,那么在第0号角标,我们设置一个MaxValue作为哨兵。MaxValue要求大于所有的堆元素
2.5.2判断最大堆H是否已满
Boolean IsFull( MaxHeap H );
算法思想:如果size==capacity,则堆满了
2.5.3 将元素item插入最大堆H
Insert( MaxHeap H, ElementType item );
算法思想:
不论什么元素,先把元素插入到数组的末尾,在和根元素进行比较,比根元素大就进行交换,否则不进行调整。调整以后,在比较,是否还是比根元素大,若还是大,在进行条,一直到比MaxValue小为止。下面使用图解来说明
2.5.4 判断最大堆H是否为空
Boolean IsEmpty( MaxHeap H )
算法思想:如果size==0,表示堆为空
2.5.5 删除堆中最大元素,并返回
ElementType DeleteMax( MaxHeap H )
算法思想:算法思想:先使用数组最末尾的元素替换第一个元素(根元素),然后判断当前根元素的左右孩子是否比根元素大,如果是,进行调整。一直这样进行递归,直到当前根元素比左右孩子都大,这样,就得到了堆。下面看演示图:
2.5.6 建立一个堆
需求:给你N个数据,如何在快速的建立一个堆
方法1:我们可以按照堆插入算法,把这些元素,一个个的插入堆中。一个元素插入堆的时间复杂度为O(log2 N),N个元素插入堆中的时间复杂度为O(N * log2 N)
方法2:我们依次把这些元素放入数组中(构建完全二叉树),然后从倒数第一个有孩子的节点的元素开始,按照删除元素的思路,对该节点和其孩子进行调整,使该节点和它对于的孩子变成一个堆。然后在往前推一个元素,在进行如此操作,就完成了快速建立一个堆。
时间复杂度的计算:
结点数 最多交换次数
n/4 1
n/8 2
n/16 3
……
n/2^k=1 log2n-1 (k-1)
所有时间复杂度为N,比第一种方法要好得多。
下面是具体的演示过程:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #define MAXDATA 65536 4 5 typedef int elementType; 6 7 typedef struct heapStruct *maxHeap; 8 struct heapStruct{ 9 elementType *elementArray;/*定义存储元素的数组*/ 10 int size;/*堆当前元素的个数*/ 11 int capacity;/*堆的最大容量*/ 12 13 }; 14 15 /* 16 create a heap appointed maxsize 17 @param maxSize The max size of the max heap 18 @return a initialized empty heap. 19 */ 20 maxHeap createEmptyHeap(int maxSize){ 21 maxHeap heap = (maxHeap)malloc(sizeof(struct heapStruct)); 22 /*把数组元素空间分配出来*/ 23 heap->elementArray = (elementType *)malloc(sizeof((maxSize+1)*sizeof(elementType))); 24 heap->size=0; 25 heap->capacity = maxSize; 26 /*define the max value as 哨兵, we will make update easy*/ 27 heap->elementArray[0]=MAXDATA; 28 return heap; 29 } 30 31 /* 32 Judge the heap whether is full. 33 @param heap The heap need to judge 34 @return 1 will be returned if the heap is full, otherwise will return 0 35 */ 36 int isHeapFull(maxHeap heap){ 37 return (heap->size==heap->capacity); 38 } 39 40 /* 41 Judge the heap whether is emppty. 42 @param heap The heap need to judge 43 @return 1 will be returned if the heap is empty, otherwise will return 0 44 */ 45 int isHeapEmpty(maxHeap heap){ 46 return (heap->size==0); 47 } 48 49 /*insert element to heap,we will update it if necessary 50 @param heap The heap exsit 51 @param element The element need to insert 52 */ 53 void insertHeap(maxHeap heap,elementType element){ 54 int i; 55 if(isHeapFull(heap)){ 56 printf("the heap is full "); 57 return; 58 } 59 i = ++heap->size; 60 heap->elementArray[i]=element; 61 for( ; heap->elementArray[i/2]<element;i=i/2){ 62 heap->elementArray[i]=heap->elementArray[i/2]; 63 } 64 heap->elementArray[i]= element; 65 } 66 67 elementType deleteMax(maxHeap heap){ 68 int parent,child; 69 elementType maxElement,temp; 70 if(isHeapEmpty(heap)){ 71 printf("the heap has bean empty "); 72 return; 73 } 74 /*取出最大元素*/ 75 maxElement = heap->elementArray[1]; 76 temp=heap->elementArray[heap->size--]; 77 for(parent=1;parent*2<heap->size;parent=child){ 78 child = parent*2; 79 /* 80 child!=heap->size:indicate the heap has a right child 81 heap->elementArray[child]<heap->elementArray[child+1]:the value of right greater than the value of left 82 the code of IF is to find the index of the bigger value of left child and right child 83 */ 84 if((child!=heap->size)&& heap->elementArray[child]<heap->elementArray[child+1]){ 85 child++;/*get the right child index*/ 86 } 87 if(temp>=heap->elementArray[child]){ 88 break;/*temp find a suitable station*/ 89 }else{ 90 /*move bigger element to parent station*/ 91 heap->elementArray[parent]=heap->elementArray[child]; 92 } 93 } 94 95 heap->elementArray[parent]=temp; 96 return maxElement; 97 } 98 99 100 /* 101 print the heap 102 @param heap The heap nend to print to the controal 103 */ 104 void toString(maxHeap heap){ 105 maxHeap p = heap; 106 printf("toString:"); 107 int i=1; 108 while(i<=p->size){ 109 printf("%d ",p->elementArray[i]); 110 i++; 111 } 112 printf(" "); 113 } 114 115 116 /* 117 construct the max heap,the algorithem thought is same with delete method 118 @param heap The binary tree who want to construct a max heap 119 @param p The parent index of the tree 120 */ 121 void percDown(maxHeap heap,int p){ 122 elementType temp=heap->elementArray[p]; 123 int parent,child; 124 for(parent=p;parent*2<=heap->size;parent=child){ 125 child = parent *2; 126 if((child!=heap->size) && heap->elementArray[child]<heap->elementArray[child+1]){ 127 child++; 128 } 129 if(temp>=heap->elementArray[child]){ 130 break; 131 }else{ 132 heap->elementArray[parent]=heap->elementArray[child]; 133 } 134 } 135 heap->elementArray[parent]=temp; 136 } 137 138 /* 139 make a binary tree to be a heap 140 @param heap The binary tree need to transfer into heap 141 */ 142 void buildMaxHeap(maxHeap heap){ 143 int i; 144 /* 145 maxHeap heap = createEmptyHeap(10); 146 maxHeap heap = createEmptyHeap(10); 147 printf("lala,%d ",length); 148 for(i=1;i<=length;i++){ 149 heap->elementArray[++heap->size]=a[i-1]; 150 } 151 */ 152 for(i=heap->size/2;i>0;i--){ 153 percDown(heap,i); 154 } 155 156 } 157 158 /* 159 void testBuildHeap(){ 160 int i; 161 maxHeap heap2 = createEmptyHeap(10); 162 for(i=1;i<=10;i++){ 163 heap2->elementArray[++heap2->size]=i; 164 } 165 toString(heap2); 166 buildMaxHeap(heap2); 167 toString(heap2); 168 } 169 */ 170 int main(){ 171 172 int i,arr[10]; 173 maxHeap heap = createEmptyHeap(30); 174 175 for(i=1;i<=10;i++){ 176 heap->elementArray[++heap->size]=i; 177 } 178 printf("build a bianray tree,not heap "); 179 toString(heap); 180 buildMaxHeap(heap); 181 printf("constructor a binary into a heap "); 182 toString(heap); 183 for(;i<=20;i++){ 184 insertHeap(heap,i); 185 } 186 printf("test insert method "); 187 toString(heap); 188 printf("delete max element%d ",deleteMax(heap)); 189 toString(heap); 190 printf("delete max element%d ",deleteMax(heap)); 191 toString(heap); 192 printf("delete max element%d ",deleteMax(heap)); 193 toString(heap); 194 printf("delete max element%d ",deleteMax(heap)); 195 toString(heap); 196 //testBuildHeap(); 197 printf("just test"); 198 return 0; 199 }
下面是结果演示: