堆
堆是一个完全二叉树的数组对象。树每一层都是满的,最后一层可能除外(从一个节点的左子树开始填)。
给定节点 i ,可以很容易计算父节点和子节点的位置。
Parent(i) =floor(i/2) :i/2再向下取整
LeftChild(i) =2*(i+1)-1 :因为i从0开始,这和c语言的数组从0开始相对应。可以用左移运算代替*,即LeftChild(i)=(i+1)<<1 - 1;
RightChild(i) = 2*(i+1) :RightChild(i) = (i+1)<<1;
堆排序
堆排序的时间复杂度是O(nlgn),是比较有效率的一种。其使用的是最大堆。最大堆的意思是父节点的值>=孩子的值。那么第0个节点必定是该堆的最大值。所以,堆排序的思想就是每次循环把最大值移走,然后从剩下的节点重新建立最大堆。
第一步:建立堆的结构体。
typedef struct heap_t{
int *arr; //point for an array to store heap value.
int heapMaxIndex; //heap element max index number
int arrLength; //array length of arr
}Heap;
其中arr指针指向的是存放堆数据的数组。
heapMaxIndex是数组最大的序号。如数组定义为a[10],那么heapMaxIndex的值应该为9.
第二步:保持堆的性质。
这一步是堆排序的基础。这里将功能写成一个函数名为void maxHeapify(Heap *hp, unsigned int nodei),这个函数用于让一个数组变成一个符合堆性质的数组。时间复杂度为O(h),h是堆所属二叉树树的高度=lgn(n是节点个数)。
思想是:从一个节点i,和他的孩子leftchild(i),rightChild(i)中找到最大的,然后其索引存放在largest中。如果i是最大的。那么i为根的子树已经是最大堆,程序结束。
否则i的某个子节点有最大元素,那么i的值和largest的值交换。下标为largest的节点在交换后作为父节点,那么他可能又违反堆性质,因此递归调用该函数。
void maxHeapify(Heap *hp, unsigned int nodei)
{
unsigned int l = (nodei+1) << 1 - 1; //left child = 2i-1, -1 ?:arr[0..n-1]
unsigned int r = (nodei+1) << 1 ; // right child = 2i
unsigned int largest = 0;
int heapMaxI = hp->heapMaxIndex;
if(l <= heapMaxI && hp->arr[l] > hp->arr[nodei])
largest = l ;
else
largest = nodei ;
if(r <= heapMaxI && hp->arr[r] > hp->arr[largest])
largest = r ;
if(largest!=nodei)
{
//exchange
int tmp ;
tmp = hp->arr[largest];
hp->arr[largest] = hp->arr[nodei];
hp->arr[nodei] = tmp;
maxHeapify(hp,largest);
}else{
return ;
}
}
第三步 利用maxHeapify函数创建堆
对于1个个数为n的堆,从上面堆的图中可以分析得到,n/2-1之前的都是父节点。之后的都是叶子节点,我们只需要对父节点进行maxHeapify就可以了。
n/2可以用右移运算n>>1。
Heap *createHeap(int *arrp, int arrLength,Heap *heap)
{
int i;
heap->arr = arrp;
heap->heapMaxIndex = arrLength-1;
heap->arrLength = arrLength;
//for an heap a[0..n-1]; a[(n/2)..n-1] all are leaves
for(i = arrLength>>1-1; i >=0; i--)
maxHeapify(heap,i);
return heap;
}
第四步:堆排序
设堆的数组为A[0..n-1],调用maxHeapify函数就可以得到最大值,然后将最大值和n-1互换,把堆的大小heapMaxIndex减1,再次调用maxHeapify,又得到最大值,存放在A[0],再和A[n-2]互换,把堆的大小再减一,这样循环下去,知道堆的大小为0。那么我们就得到了由小到大的排好序的数组。
void heapSort(Heap *hp)
{
int tmp;
int last;
while(hp->heapMaxIndex>0)
{
last = hp->heapMaxIndex ;
//exchange
tmp = hp->arr[last];
hp->arr[last] = hp->arr[0];
hp->arr[0] = tmp;
hp->heapMaxIndex -= 1;
maxHeapify(hp,0); //make heap from root node 0 to heapMaxIndex
}
}
测试堆排序
void printArr(int *p, int size)
{
int i;
for(i=0; i<size;i++)
{
printf("%d ",p[i]);
}
}
int main()
{
int a[]={15,25,32,23,1,-4,35,2,-85,42,0,12,26,56,45,12,145,17,25,21};
printArr(a,20);
printf("\n");
Heap hpa,*phpa;
phpa = createHeap(a,20,&hpa);
heapSort(phpa);
printArr(a,20);
putchar('\n');
return 0;
}
结果为:-85 -4 0 1 2 12 12 15 17 21 23 25 25 26 32 35 42 45 56 145
堆排序应用:
优先级队列,事件驱动模式响应队列等。