首页 > 代码库 > 堆排序-学习笔记

堆排序-学习笔记

在学习堆排序之前首先了解一下二叉堆的特性:

1、二叉堆的父节点的值总是大于等于(或小于等于)其左右孩子的值;

2、每个节点的左右子树都是一棵这样的二叉堆。

如果该二叉堆的父节点总是大于孩子节点,则叫做最大堆,如果父节点小于孩子节点,则叫做最小堆。

在堆排序的应用中,如果递增排序,则应该使用最大堆,反之,使用最小堆。

堆排序是不稳定的

堆排序主要有两个步骤来完成堆排序:

1、把一个无序数列构造成一个最大堆或最小堆;

2、去掉根节点(堆顶元素),把剩下的元素重新构造一个二叉堆。

二叉堆的存储结构为一维数组,逻辑结构为二叉树。

在一个索引以0为开头的数组中,i为二叉堆的非叶子节点,它的左孩子为2*i+1,右孩子为2*i+2 ,他的父节点为(i-1)/2。

构造二叉堆:

1、选取最后一个非叶子节点,和他的左右孩子比较,如果有大于(小于)父节点的,则把左右孩子节点中较大的一个与父节点对换。

2、重复上述步骤,直到所有的节点全部扫描一遍,这样,二叉堆就构造好了。

调整二叉堆:

1、把堆顶元素和最后一个元素互换,构成了一个新的数组 a[0...n-1];

2、把新构成二叉堆重新扫描,因为只是更换了堆顶元素,所以只要扫描堆顶元素就可以了,完成后,构成了一个新的二叉堆。

3、重复上述步骤,直到二叉堆内的元素为1;

4、最后,新构成的数组就是排好序的了。

代码如下

#include<cstdio>

void HeapadjustDown(int a[],int _begin,int _end){ //二叉堆构造函数
    int temp=a[_begin];    //把节点保存在临时temp里
    int lchild=2*_begin+1; //lchild为左孩子
    while(lchild<_end){ //循环控制条件
        if(lchild+1<_end&&a[lchild+1]>a[lchild]){ //从左右孩子中选取最大的
            lchild++;
        }
        if(a[lchild]<temp){ //如果左右孩子节点都比父节点小,则退出
            break;
        }
        a[_begin]=a[lchild];//父节点被子节点替换
        _begin=lchild;//把当前父节点设置为替换的孩子节点
        lchild=2*_begin+1;//重新设置左孩子节点
    }
    a[_begin]=temp;//把最开始的父节点赋值到,孩子节点中。

}

void HeapSort(int a[],int n){
    for(int i=(n-1)/2;i>=0;i--){//这个循环是构造二叉堆
        HeapadjustDown(a,i,n);
    }
    for(int i=n-1;i>=0;i--){//这个循环是构造有序数组
        int temp=a[i];
        a[i]=a[0];
        a[0]=temp;
        HeapadjustDown(a,0,i);//去掉堆顶元素后,重新构造二叉堆
    }
}
int main(){
    int n,a[20];
    scanf("%d",&n);
    for(int i=0;i<n;i++){

        scanf("%d",&a[i]);
    }
    HeapSort(a,n);
    for(int i=0;i<n;i++){
        printf("%d ",a[i]);
    }
}

 

堆排序-学习笔记