导航

堆排序

Posted on 2018-08-21 16:08  困或  阅读(221)  评论(0编辑  收藏  举报

1.堆定义

  堆的性质:

    [1]堆中某个节点的值总是不大于或不小于其父节点的值;

    [2]堆总是一棵完全二叉树。

    [3]将根节点最大的堆叫做大根堆,根节点最小的堆叫做小根堆。

  堆还是很简单的,父节点的值总是大于(或者总是小于)子节点的值,但是左右子节点没有大小顺序。由于是完全二叉树,所以可以用数组表示堆。

2.堆排序

  以下是从小到大排序的步骤:

  [1]把一个无序数组构造成一个大顶堆。(完成后根节点最大,但是一个节点的左右子节点是无序的)

  [2]将堆顶元素和末尾元素交换,然后调整剩下的元素使其继续成为一个大顶堆。(此时最后一个元素是最大的值,堆顶元素是除最后一个元素外最大的值(次大值),因此此时把倒数第二个元素作为末尾元素)

  [3]重复第2步。

  总结就是,第一步把数组构造成大顶堆后,此时根节点值最大。第二步,根和末尾交换,此时末尾元素最大,然后把除末尾的剩下元素继续调整成一个大根堆,此时根是次大元素。最后再重复第二步就可以了(因为最后的元素已排序好,所以重复时,末尾是前面未排序的末尾)。  

  另外如果是从大到小排序,则上面只要把大根堆改为构造小根堆就可以。

3.堆排序代码

#include <stdio.h>

void swap(int *array, int a, int b)
{
    int temp;

    temp = array[a];
    array[a] = array[b];
    array[b] = temp;
    return ;
}

static void print_array(int *array, int len)
{
    int i;

    for(i=0; i<len; i++){
        printf("%d ", array[i]);
    }

    printf("\n");
}

void adjust(int *array, int n, int len)
{
    int i;
    int temp = array[n];

    /*
        调整下标为n的元素,使n和n的子树成为大根堆。
        注意这里有个前提条件是n的子树本身就是大根堆,因为第一步构造时是从第一个非叶子节点开始调整的,所以会一直满足这个条件。

        流程就是,比较n的两个叶子节点,把其中大的和n交换(如果大于n的值),例如和右子节点交换,由于交换后n的右子树可能不再满足大跟堆,所以继续向下处理。
    */
    for(i=n*2 + 1; i < len; i = i*2 + 1){
        if(i+1 < len && array[i] < array[i + 1])
            i++;

        if(array[i] > temp){
            array[n] = array[i];
            n = i;
        }else
            break;
    }

    array[n] = temp;
}

//从小到大排序
void heap_sort(int *array, int len)
{
    int i;

    // 第一步,构造大顶堆
    for(i=len/2 - 1; i>=0; i--){
        adjust(array, i, len);
    }

    for(i=len-1; i>0; i--){
        swap(array, 0, i);        //根和末尾交换
        adjust(array, 0, i);      //然后把剩下的元素再调成大根堆,由于上面交换后,i个元素中,只有堆顶的元素不满足大根堆,所以传入的参数是0,也就是要调整堆顶元素
    }
}

int main()
{
    int i;
    int array[]={5,3,7,6,8,9};

    heap_sort(array, sizeof(array)/sizeof(int));
    print_array(array, sizeof(array)/sizeof(int));
    return 0;
}

  [1]代码中,和上面说明的步骤是对应的。唯一注意的就是adjust()函数流程:传入的参数n,就是要把n和n的子树调成一个大跟堆,并且要保证调用该函数之前,n的子树本来就是一个大跟堆。因为构造大跟堆是从倒数第一个非叶子节点开始的,所以这个条件是一直都会满足的。

  [2]继续用图示说明adjust()函数中循环的意义,例如在上面第一步构造大顶堆: