《算法导论》学习笔记(1)——堆与堆排序

来源:互联网 发布:什么是node服务器 编辑:程序博客网 时间:2024/06/06 18:53

         堆排序( Heapsort )是指利用“堆”这种数据结构所设计的一种排序算法

         堆是一种数据结构,是一个数组。它可以被看成一个近似的完全二叉树,树上的每一个结点对应数组中的一个元素。除了最底层外,即叶子结点,该树是完全充满的,而且是从左到右填充。

         最大堆的每个结点都要满足堆的性质,此外还有其他的约束:堆中的最大元素存放在根结点中,并且在任一子树中,该子树所包含的所有结点的值都不大于该子树根结点的值。

         堆排序就是利用最大堆的性质来排序。大致步骤如下:

         ①建最大堆

         ②将根结点(最大元素)与最后一个结点的元素互换,并将剩下的结点维护最大堆的性质。

         ③重复步骤②,直到将每个元素排完序。

下面就讲解几个重要过程:

         维护最大堆的性质:MaxHeapify

         传入参数:待排序数组、需要维护的下标、当前堆的大小。返回值:空

         每次在传入参数的下标、该下标的左、右孩子结点三者中选出最大的元素。如果最大元素是该下标,则满足最大堆性质,结束。否则,需要将父亲与最大的孩子交换位置,使得当前下标及其左右孩子满足最大堆的性质。之后递归调用,使得整个子树都满足最大堆的性质。

         特别需要注意,数组的大小和堆的大小是不一样的概念。堆的大小表示有多少个堆元素存储在数组中。在进行堆排序的过程中,每次需要把根结点元素与最后一个元素交换,这样堆的大小每次就会减一。而数组大小是不会变的。也就是:

0 ≤ 堆的大小 ≤ 数组的大小

                                                              

         建最大堆:BuildMaxHeap

         传入参数:待排序数组、当前堆的大小。返回值:空

         只需保证所有的非叶子结点满足最大堆的性质即可。非叶子结点的下标为从0到堆大小 / 2(向下取整)。

                 

         堆排序:HeapSort

         传入参数:待排序数组、当前堆的大小。返回值:空

         首先建堆,然后每次去除根结点元素(最大元素),然后维护最大堆的性质。如此循环,直到所有元素排序完毕。


         实现代码如下:

#include <iostream>using namespace std;typedef int ElemType;int getLeftChild( int i ) //返回左孩子下标。注:数组从位置0开始{return 2 * i + 1;}int getRightChild( int i ) //返回右孩子下标。注:数组从位置0开始{return 2 * i + 2;}void swap( ElemType *a, ElemType *b ) //交换两个元素{ElemType temp = *a;*a = *b;*b = temp;}/* 维护最大堆的性质。 * 参数:待排序数组、需要维护的下标、当前堆的大小 * 返回值:空 */void MaxHeapify( ElemType *A, int i, int num ){int left = getLeftChild( i );int right = getRightChild( i );int largest; // 存放父结点、左右子节点三者的最大值的下标位置if( left <= num && A[left] > A[i] )largest = left;elselargest = i;if( right <= num && A[right] > A[largest] )largest = right;if( largest != i ){swap( &A[i], &A[largest] );MaxHeapify( A, largest, num );}}/* 建最大堆 * 传入参数:待排序数组、当前堆的大小 * 返回值:空 */void buildMaxHeap( ElemType *A, int num ){for( int i=num/2; i>=0; i-- )MaxHeapify( A, i, num );}/* 堆排序 * 待排序数组、当前堆的大小 * 返回值:空 */void heapSort( ElemType *A, int num ){buildMaxHeap( A, num-1 );int HeapSize = num - 1; //当前堆的大小for( int i=num-1; i>0; i-- ){swap( &A[0], &A[i] );HeapSize--;MaxHeapify( A, 0, HeapSize );}}int main(){ElemType A[10] = {1,3,6,2,4,8,0,9,7,5};heapSort( A, 10 );for( int j=0; j<10; j++ )cout << A[j] << " ";cout << endl;return 0;}

时空复杂度:

    由于每次重新恢复堆的时间复杂度为O(logN),共N - 1次重新恢复堆操作,再加上前面建立堆时N / 2次向下调整,每次调整时间复杂度也为O(logN)。二次操作时间相加还是O(N * logN)。故堆排序的时间复杂度为O(N * logN)。

    由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。

    堆排序是就地排序,辅助空间为O(1), 它是不稳定的排序方法。



0 0
原创粉丝点击