堆(heap)、堆排序(heapSort)、优先级队列

来源:互联网 发布:别墅装修设计软件 编辑:程序博客网 时间:2024/06/08 06:44

1. 堆数据结构: 堆是一种数据结构。分为大根堆和小根堆。  

         对堆数组,其分布与一个完全二叉树对应,其下标的几个关系:  

        当i的取值满足区间[1, n]时; 数组的小标从1开始;

          parent(i) = i  >> 1; 

          left(i) = 2*i;

          right(i) = 2*i +1;


          对于C语言的数组的小标从0开始,稍作调整如下:

           parent(i) = ( ( (i) - 1  ) >> 1);  // i >= 1;                               
           left(i)       = ( ( (i) << 1 ) + 1);   // i >= 0;                                  
           right(i)    = ( ( (i) << 1 ) + 2)    // i >= 0;                      

 

parent(i) = ( ( (i) - 1  ) >> 1);  // i >= 1;  left(i)   = ( ( (i) << 1 ) + 1);   // i >= 0;  right(i)  = ( ( (i) << 1 ) + 2)    // i >= 0;


堆的性质:

  对于大根堆数组a: 满足: a[parent(i)] >= a[i];

  对于小跟堆数组a :   满足: a[parent(i)] <= a[i];


2.堆的构建: 通过堆的调整,完成堆的构建。

 堆的构建的两种方式: 

 1)像插入排序的方式那样进行增量(incremental)求解, 即其满足循环不变式的性质:

总的描述: 数组a[0...j-1]是一个堆,将a[j]加入,通过堆调整,数组a[0...j]也是一个堆。

        满足循环不变式的3个条件:

          (1)初始化:只有一个a[0]数组a是一个堆。

          (2) 保持: 数组a[0...j-1]是一个堆,将a[j]加入,通过堆调整,数组a[0...j]也是一个堆。

          (3)终止: 当数组中所有的元素都加入完毕式,整个数组满足堆的性质,是一个堆。

  2)合并求解: 以a[i] 为根的拟堆,除a[i] 以外其他所有的元素都满足堆的性质,即除了a[i]以外是以它的两个孩子为根的两个堆。通过堆调整将a[i]加入,形成以a[i]为根的堆。

在数组a中,若数组的元素个数为n,则从a[n/2]、a[n/2 +1] ... a[n-1]满足堆的性质。

     满足循环不变式的三个性质:

        (1)初始化: 则从a[n/2]、a[n/2 +1] ... a[n-1]满足堆的性质。

        (2)保持: 将a[n/2-1] 加入,通过堆调整,a[n/2-1]、a[n/2]、a[n/2 +1] ... a[n-1]满足堆的性质。

        (3)终止:形成以a[0]为根的堆。

算法实现是基于第二种。



3. 堆排序算法: 采用大根堆。首先对数组a[0...n-1]通过2中的方法建立大根堆。

      方法: 将a[0...n-1]  中 根元素a[0] 与 a[n-1]交换,则最大元素a[n-1]确定了,

                  通过堆调整,形成新的堆数组a[0...n-2].  重复上述过程,最终完成排序。

     上述过程是一个循环不变式:

         (1)初始化: a[0...n-1] 是个大根堆。

         (2)保持:在大根堆a[0...i] 中,将a[i]与a[0]交换确定a[i], 通过调整a[0...i-1]形成新的大根堆。

         (3)终止:确定最小的元素a[0].

   算法的实现:

   

/** * \file    heapSort.c * * \brief  Heap Sort test file */#include <stdio.h>#include <stdlib.h>#define MaxLen  100#define parent(i) (((i)-1)>>1)#define left(i)   (((i)<<1) + 1)#define right(i)  (((i)<<1) + 2)/** * \brief      exchange the value of tow integers. * * \param  a   integer point. * \param  b   integer point. */void exchange(int* a, int* b);/** * \brief       print an integer array. * * \param  a    integer array point. * \param  len  total # of elements of the array. */void printArray(int* a, int len);/** * \brief       Max(big) root heap adjust without recurrence. * * \param  a    array point. * \param  i    element location. * \param  len  total # of elements of the array. */void maxHeapify(int* a, int i, int len);/** * \brief       Min(small) root heap adjust without recurrence. * * \param  a    array point. * \param  i    element location. * \param  len  total # of elements of the array. */void minHeapify(int* a, int i, int len);/** * \brief       Max(big) root heap adjust with recurrence. * * \param  a    array point. * \param  i    element location. * \param  len  total # of elements of the array. */void maxHeapify_r(int* a, int i, int len);/** * \brief       Min(small) root heap adjust with recurrence. * * \param  a    array point. * \param  i    element location. * \param  len  total # of elements of the array. */void minHeapify_r(int* a, int i, int len);/** * \brief       build max(big) root heap. * * \param  a    integer array point. * \param  len  total # of elements of the array. */void buildMaxHeap(int* a, int len);/** * \brief       build min(small) root heap. * * \param  a    integer array point. * \param  len  total # of elements of the array. */void buildMinHeap(int* a, int len);/** * \brief       heap sort in ascending order. * * \param  a    integer array point. * \param  len  total # of elements of the array. */void heapSort(int* a, int len);/** * \brief       heap sort in decending order. * * \param  a    integer array point. * \param  len  total # of elements of the array. */void heapSort_reverse(int* a, int len);int main(int argc, char* argv[]){    int a[] = {3, 4, 8, 6, 7, 1, 2, 5, 9};    int len = 9;    heapSort(a, len);    //heapSort_reverse(a, len);    printArray(a, len);    return 0;}void exchange(int* a, int* b) {    int tmp;    tmp = *a;    *a = *b;    *b = tmp;}void printArray(int* a, int len) {    int i;    for(i=0; i<len; i++) {        printf("%d\t", a[i]);    }    printf("\n");}void maxHeapify_r(int *a, int i, int len) {    int l;    int r;    int largest;    l = left(i);    r = right(i);    largest = i;    if(l<len && a[largest]<a[l]) largest = l;    if(r<len && a[largest]<a[r]) largest = r;    if(largest!=i) {        exchange(a+largest, a+i);        maxHeapify_r(a, largest, len);    }}void maxHeapify(int *a, int i, int len) {    int l;    int r;    int largest;    int tag;    //to indicate the changeable.    do{        tag = 0;        l = left(i);        r = right(i);        largest = i;        if(l<len && a[largest]<a[l]) largest = l;        if(r<len && a[largest]<a[r]) largest = r;        if(largest!=i) {            exchange(a+largest, a+i);            i = largest;            tag = 1;        }    }while(tag);}void minHeapify_r(int* a, int i, int len) {    int l;    int r;    int least;    l = left(i);    r = right(i);    least = i;    if(l<len && a[least]>a[l]) least = l;    if(r<len && a[least]>a[r]) least = r;    if(least!=i) {        exchange(a+least, a+i);        minHeapify_r(a, least, len);    }}void minHeapify(int *a, int i, int len) {    int l;    int r;    int least;    int tag;    //to indicate the changeable.    do{        tag = 0;        l = left(i);        r = right(i);        least = i;        if(l<len && a[least]>a[l]) least = l;        if(r<len && a[least]>a[r]) least = r;        if(least!=i) {            exchange(a+least, a+i);            i = least;            tag = 1;        }    }while(tag);}void buildMaxHeap(int* a,  int len) {    int i;    int half = (len>>1) -1;    for(i=half; i>=0; i--) {       maxHeapify(a, i, len);       //maxHeapify_r(a, i, len);    }}void buildMinHeap(int* a,  int len) {    int i;    int half = (len>>1) -1;    for(i=half; i>=0; i--) {        minHeapify(a, i, len);        //minHeapify_r(a,i, len);    }}void heapSort(int *a, int len) {    int i;    buildMaxHeap(a, len);    //printArray(a, len);    for(i=len-1; i>0; i--) {        exchange(&a[i], &a[0]);        len--;        maxHeapify(a, 0, len);    }}void heapSort_reverse(int *a, int len) {    int i;    buildMinHeap(a, len);    //printArray(a, len);    for(i=len-1; i>0; i--) {        exchange(&a[i], &a[0]);        len--;        minHeapify(a, 0, len);    }}// usage for priority queue.int maxHeapMaximum(int* a, int len) {    return a[0];}int minHeapMinimum(int* a, int len) {    return a[0];}int maxHeapExtractMax(int* a, int* len) {    int max;    if(*len<1) return -1;  //heap empty    max = a[0];    a[0] = a[*len-1];    *len--;    maxHeapify(a, 0, *len);    return max;}int minHeapExtractMin(int* a, int* len) {    int min;    if(*len<1) return -1;  //heap empty    min = a[0];    a[0] = a[*len-1];    *len--;    minHeapify(a, 0, *len);    return min;}int maxHeapIncreaseKey(int* a, int i, int len, int key){   if(i>len || key<=a[i]) return -1;   int p;   a[i] = key;   p = parent(i);   while(i>0 && a[i]>a[p]) {        exchange(a+i, a+p);        i = p;        p = parent(i);   }    return 0;}int minHeapDecreaseKey(int* a, int i, int len, int key){   if(i>len || key>=a[i]) return -1;   int p;   a[i] = key;   p = parent(i);   while(i>0 && a[i]<a[p]) {        exchange(a+i, a+p);        i = p;        p = parent(i);   }    return 0;}int  maxHeapInsert(int* a, int *len, int key) {    int i;    int p;    *len += 1;    if(*len > MaxLen) return -1;    i = *len - 1;    p = parent(i);    a[i] = key;    while(i>0 && a[i]>a[p]) {        exchange(a+i, a+p);        i = p;        p = parent(i);    }    return 0;}int  minHeapInsert(int* a, int *len, int key) {    int i;    int p;    *len += 1;    if(*len > MaxLen) return -1;    i = *len - 1;    p = parent(i);    a[i] = key;    while(i>0 && a[i]<a[p]) {        exchange(a+i, a+p);        i = p;        p = parent(i);    }    return 0;}int maxHeapDelete(int* a,  int i, int* len) {    int p;    if(i> *len ) return -1;    a[i] = a[*len -1];    *len --;    maxHeapify(a, i, *len);    p = parent(i);    while(i>0 && a[i]>a[p]) {        exchange(a+i, a+p);        i = p;        p = parent(i);    }    return 0;}int minHeapDelete(int* a,  int i, int* len) {    int p;    if(i> *len ) return -1;    a[i] = a[*len -1];    *len --;    minHeapify(a, i, *len);    p = parent(i);    while(i>0 && a[i]<a[p]) {        exchange(a+i, a+p);        i = p;        p = parent(i);    }    return 0;}


4.  用堆数据结构构造优先级队列。


5.用堆构造的优先级队列构造栈、和队列数据结构。


6. 算法分析

  堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,它们均是通过调用Heapify实现的。
  堆排序的最坏时间复杂度为O(nlogn)。堆序的平均性能较接近于最坏性能。
  由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
  堆排序是就地排序,辅助空间为O(1),
  它是不稳定的排序方法。


以上参见《算法导论》的第6章。。。