学习《算法导论》第六章 堆排序 总结

来源:互联网 发布:暴雪降至 知乎 编辑:程序博客网 时间:2024/05/16 17:40

学习《算法导论》第六章 堆排序 总结

完全二叉树的概述

一颗完全二叉树:树的每一层都是填满的,最后一层可能除外(最后一层是从一个结点的左子树开始填的).

树的结点表示

对于完全二叉树,给定一个结点的下标i,其父结点PARENT(i),左儿子LEFT(i),右二子RIGHT(i)的下标可通过下面简单计算出来:

PARENT(i)    return i / 2;LEFT(i)    return 2 * i;RIGHT(i)    return 2 * i + 1;

这三个过程通常是用宏或内联来实现的.

树的高度

结点的高度:该结点到叶子结点的最长简单下降路径上的边的数目.
树的高度:树的根结点到叶子结点的最长简单下降路径上的边的数目.
因此, 对于有n 个结点的完全二叉树的高度为: O(lg n)

堆可以看作是一颗二叉树. 这样, 表示堆的数组A有两个属性:length[A]是数组A中的元素. heapsize[A]是存放在A中的堆的元素个数. 也就是说A[heapsize[A]]之后的元素都不属于这个堆. 堆有两种:最大堆和最小堆.

最大堆

最大堆特性是指除了根以外的每个结点i, 有A[PARENT(i)] >= A[i], 即每个结点的值至多和其父结点的值一样大. 这样, 堆中最大的元素就是根结点; 且在以某一个结点为根的子树中, 各结点的值都不大于该子树根结点的值.

最小堆

最小堆的元素组织方式正好相反; 它的特性是指除了根以外的每个结点i, 有A[PARENT(i)] =< A[i]. 最小堆的最小元素就是根结点.

堆的高度

堆的高度即树的高度, 其高度也是O(lgn)

堆排序

堆排序有两个优点:1. 堆排序的运行时间为O(nlg n), 比插入排序要快; 2. 堆排序是一种原地排序算法. 所谓原地排序是指在任何时候,数组中只有常数个元素存储在数组之外. 下面将从三个方面介绍堆排序(这里用最大堆来实现).

MAX-HEAPIFY过程(保持堆的性质)

这里就是保持最大堆的性质, 即每个结点的值都不能小于它的子女. 下面直接给出它的算法描述, 先只考虑单个结点i和它的左右孩子比较, 判断它们之间是否满足最大堆的性质. 该算法的输入有两个参数: 数组A和结点下标i.

MAX-HEAPIFY(A, i)1    l <--- LEFT(i)2    r <--- RIGHT(i)3    if l <= heapsize[A] and A[l] > A[i]4        then largest <--- l5        else largest <--- i 6    if r <= heapsize[A] and A[r] > A[i]7        then largest <--- r8    if largest != i 9        then exchange A[i] <-> A[largest]10            MAX-HEAPIFY(A, largest)

MAX-HEAPIFY作用在单个结点上时, 其运行时间为调整A[i], A[LEFT[i]], A[RIGHT[i]]的关系所用的时间O(1), 再加上对以i的某个结点为根的子树递归调用MAX-HEAPIFY所用的时间. MAX-HEAPIFY的运行时间为O(lg n).

BUILD-HEAP过程(建堆)

对于一个数组A[1…n], 我们知道子数组A[(n/2+1)…n]中的元素都是树中的叶子. 所以建堆过程只要对树中的其他结点都调用一次MAX-HEAPIFY过程. 具体算法如下:

BUILD-MAX-HEAP(A)1    heapsize[A] <--- length[A]2    for i <--- heapsize[A] / 2 downto 13        do MAX-HEAPIFY(A, i)

注意:算法中的步骤2, i是一直递减到1的. 也就是建堆是从下面往上面建的, 就和建房子类似. 它的运行时间为O(n)(具体见算法导论书).
直到这一步, 给定一个数组A, 已经可以建立数组A对应的堆. 那么如何对这个最大堆进行排序呢?我们直到最大堆的特点就是最大的元素是堆的根结点A[1]. 那么我们只要将根结点和最后一个结点互换, 来达到”正确的位置”. 现在, 如果将A[n]从堆中”去掉”, 可以发现A[1…n-1]破坏了堆的性质. 这时要调用3.1MAX-HEAPIFY过程将A[1…n-1]保持堆的性质, 然后最大元素又是根结点,将根结点和最后一个结点互换, 不断重复这个过程. 堆的大小也从n-1一直降到2. 这就是下面的堆排序算法.

HEAPSORT过程(堆排序)

下面直接给出它的算法和运行时间:

HEAPSORT(A)1    BUILD-MAX-HEAP(A)2    for i <--- length[A] downto 23        do exchange A[1] <-> A[i]4           heapsize--5           MAX-HEAPIFY(A, 1)

注意:每次都是根结点和最后一个结点互换. 所以算法第5步每次都是在根结点上开始保持堆的性质
时间代价为:O(lgn).

算法实现和程序代码

直接根据上面的算法就可以写程序验证了.

#include <stdio.h>#include <stdlib.h>// 待测试的数组的长度#define ARRAY_LENGTH 100// 用宏和位运算来提高运算速度#define LEFT(i) (i<<1)#define RIGHT(i) (2*(i)+1) // 这个还不知道怎么用位运算实现????????????????????????????????inline void swap(int* a, int* b){    int temp = *a;    *a = *b;    *b = temp;    return;}// MAX-HEAPIFY过程(保持堆的性质)// 入参有:数组HeapArray、数组长度、以及结点下标ParentNodeIdvoid MaxHeapify (int* Heap, int HeapLength, int ParentNodeId){    int LeftNodeId = LEFT(ParentNodeId);    int RightNodeId = RIGHT(ParentNodeId);    // 用于标记父结点和子结点中值最大的结点下标    int LargestNodeId = ParentNodeId;    if ((LeftNodeId <= HeapLength) && (Heap[LeftNodeId -1] > Heap[ParentNodeId - 1]))    {        LargestNodeId = LeftNodeId;    }    // 注意:RightNodeId的值是和LargestNodeId的值进行比较. 而不是ParentNodeId!!!!    if ((RightNodeId <= HeapLength) && (Heap[RightNodeId - 1] > Heap[LargestNodeId - 1]))    {        LargestNodeId = RightNodeId;    }    // 当子结点的值比父结点的值大时, 互换他们的位置,     // 互换后, 以largestnodeid为结点的根的子树又可能违反最大堆的性质    // 所以递归调用MAX-HEAPIFY过程    if (LargestNodeId != ParentNodeId)    {        swap(Heap + ParentNodeId - 1, Heap + LargestNodeId - 1);        MaxHeapify(Heap, HeapLength, LargestNodeId);    }}// BUILD-HEAP过程(建堆)// 入参为:数组HeapArray和数组长度ArrayLengthvoid BuildMaxHeap (int* HeapArray, int HeapLength){    int HeapSize = HeapLength;    int LoopNodeId = 0;    for (LoopNodeId = HeapSize / 2; LoopNodeId > 0; LoopNodeId--)    {        MaxHeapify (HeapArray, HeapSize, LoopNodeId);    }}// HEAPSORT过程(堆排序)int HeapSort (int* Array, int ArrayLength){    // 合法性检查,这里要求数组至少为3, 否则排序无意义    if ((NULL == Array) || (ArrayLength < 3))    {        return -1;    }    int LoopId = 0;    int HeapSize = ArrayLength;    BuildMaxHeap(Array, ArrayLength);    for (LoopId = ArrayLength; LoopId > 1; LoopId--)    {        swap(Array, Array + LoopId - 1);        HeapSize--;        MaxHeapify (Array, HeapSize, 1);    }    return 0;}int main(){    int number[ARRAY_LENGTH] = {0};    for (int i = 0; i < ARRAY_LENGTH; i++)    {        number[i] = i + 1;    }    HeapSort (number, ARRAY_LENGTH);    for (i = 0; i < ARRAY_LENGTH; i++)    {        printf ("The %d th value is %d.\n", i + 1, number[i]);    }    return 0;}
0 0
原创粉丝点击