算法の堆排序

来源:互联网 发布:http。默认端口 编辑:程序博客网 时间:2024/05/22 02:42

堆数据结构一个堆数据结构由一个数组(类型可以任意),一个代表最大值的整数和当前堆的大小组成。
堆排序时间复杂度 O(N*logN) 同快速排序、归并排序一样的时间复杂度。
步骤首先以线性时间建立一个堆。然后通过将堆中的最后一个元素与第一个元素交换,缩减堆的大小进行下虑,来执行N-1次deleteMax操作。
二叉堆 二叉堆是一个完全二叉树或近似一个完全二叉树。满足两个特性:

  1. 父节点的键值总是大于或等于任何一个子节点的键值。
  2. 每个节点的左子树和右子树都是一个二叉堆。
    最大堆父节点的键值大于或等于任何一个子节点的键值。
    最小堆父节点的键值小于或等于任何一个子节点的键值。
    最小堆
    一般用数组来表示堆,对于i节点,其左右子节点为2*i和2*i+1;父节点为:i-1/2.
    这里写图片描述

这里写图片描述
堆插入操作 void insert(elementType x,priorityQueue h)
每次插入将新元素放在数组之后。即在下一个空闲位置创建一个空穴,如果新元素x可以放在空穴中而不影响堆序性质则插入完成,否则,将空穴父节点元素移入到该空穴中。继续该过程直到x可以放在空穴。这里写图片描述

//插入操作结构体实现一:void insetr(elementType x, priorityQueue h){    int i;    if(isFull(h)){        error("fatal error");        return;        }    for(i=++h->size;h->element[i/2]>x;i/=2){        h->element[i]=h->element[i/2];    h->element[i]=x;    }    //另一种直接数组实现二:    //  新加入i结点  其父结点为(i - 1) / 2void MinHeapFixup(int a[], int i){    int j, temp;    temp = a[i];    j = (i - 1) / 2;      //父结点    while (j >= 0 && i != 0)    {        if (a[j] <= temp)            break;        a[i] = a[j];     //把较大的子结点往下移动,替换它的子结点        i = j;        j = (i - 1) / 2;    }    a[i] = temp;}//在最小堆中加入新的数据nNumvoid MinHeapAddNumber(int a[], int n, int nNum){    a[n] = nNum;    MinHeapFixup(a, n);}

**删除最小元**deleteMin:堆中每次都只能删除第0个数据。这样在根节点出=处产生一个空穴,因此必须将最后一个元素移动到该堆的某个地方;为了便于重建堆,实际的操作是将最后一个数据与空穴的两个子节点的较小着者进行比较,如果其大于两者的较小者则将较小者移入空穴,这样空穴下移一层,继续上述步骤,直至满足条件;
如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程。下面给出代码:

elemenType deleteMin(PriorityQueue H){    int i,child;    elemenType MinElement,lastElement;    if (isempty (H))    {        EEROR("Priority is empty");        return H->element[0];    }    MinElement= H->element[1];//获取最小元素    LastElement= H->element[H->size--];//空穴产生    for(i=1;i*2<H->size;i=child){//下虑操作        child=i*2;        if (child!=H->size&&H->element[child+1]<H->element[child])//比较两字节点的较小者,child为较小子节点            child++;        if(lastElement>H->element[child])            H->element[i]=H->element[child];        else break;    }    H->element[i]=lastElement;    return minElement;}

这里写图片描述

第二种实现方法:

//  从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2void MinHeapFixdown(int a[], int i, int n){    int j, temp;    temp = a[i];    j = 2 * i + 1;    while (j < n)    {        if (j + 1 < n && a[j + 1] < a[j]) //在左右孩子中找最小的            j++;        if (a[j] >= temp)            break;        a[i] = a[j];     //把较小的子结点往上移动,替换它的父结点        i = j;        j = 2 * i + 1;    }    a[i] = temp;}//在最小堆中删除数void MinHeapDeleteNumber(int a[], int n){    Swap(a[0], a[n - 1]);    MinHeapFixdown(a, 0, n - 1);}

堆序化数组即build heap 建堆过程
这里写图片描述
这里写图片描述
实现代码:

//下虑操作void percDown(elementType a[],int i, int n){    int child;    elementType tmp;    for(tmp=a[i];2*i+1<n;i=child)    {        child=2*i+1;        if(child!=n-1&&a[child+1]<a[child])//在左右节点中找较小的            child++;        if(a[child]<tmp)//较小节点向上移动替换父节点            a[i]=a[child]            else                 break;                }            a[i]=tmp;

堆排序

viod heapSort(element a[],int n){    int i;    for(i=N/2;i>=0;i--)    {        percDown(a,i,n);//必须从下开始即从父节点下标最大的位置处进行依次向上调整成堆,不能直接从根节点调整(竟然被这个问题困扰了还!!)}//建堆    for(i=n-1;i>0;i--)     {         swap(&a[0],&a[n-1]);         percDown(a,0,i);//不断交换根节点与最后一个元素并在交换之后进行调整使之成堆}//

注意使用最小堆排序后是递减数组,要得到递增数组,可以使用最大堆。

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

0 0