数据结构与算法之六堆排序

本文详细介绍了堆排序的基本概念,包括大顶堆和小顶堆的定义,以及如何通过堆排序算法对数组进行升序排列。同时,文章还提供了具体的实例步骤,并给出了完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:

  Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]

  即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。

  堆分为大顶堆和小顶堆,满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。

堆排序的思想

       其基本思想为(大顶堆):

    1)将初始待排序关键字序列[R0,R2....R(n-1)]构建成大顶堆,此堆为初始的无序区;

    2)将堆顶元素R[0]与最后一个元素R[n-1]交换,此时得到新的无序区[R0,R2,......R(n-2)]和新的有序区[R(n-1)],且满足R[0,1...n-2]<=R[n-1]; 

    3)由于交换后新的堆顶R[0]可能违反堆的性质,因此需要对当前无序区(R0,R1,......Rn-2)调整为新堆,然后再次将R[0]与无序区最后一个元素交换,得到新的无序区(R0,R1....Rn-3)和新的有序区(Rn-2,Rn-1)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

    操作过程如下:

     1)初始化堆:将R[0..n-1]构造为堆;

要将初始文件R[0..n-1]调整为一个大根堆,就必须将它所对应的完全二叉树中以每一结点为根的子树都调整为堆。显然只有一个结点的树是堆,而在完全二叉树中,所有序号 i>[n/2]-1的结点都是叶子,因此以这些结点为根的子树均已是堆。这样,我们只需依次将以序号为 -1,…,0的结点作为根的子树都调整为堆即可。

     2)将当前无序区的堆顶元素R[0]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。

    因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。

    下面举例说明:

     给定一个整形数组a[]={16,7,3,20,17,8},对其进行堆排序。

    首先根据该数组元素构建一个完全二叉树,得到

 
 然后需要构造初始堆,则从最后一个非叶节点开始调整,调整过程如下:

20和16交换后导致16不满足堆的性质,因此需重新调整

这样就得到了初始堆。

即每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)。有了初始堆之后就可以进行排序了。

此时3位于堆顶不满堆的性质,则需调整继续调整

整体程序如下:
#include<iostream>
using namespace std;
void Build_heap(int *a,int size);
void Heap_adjust(int *a,int i,int size);
void Heap_sort(int *a,int size);
int main()
{
         int a[100],i,size;
        cout<<"Please enter the length of array:";
        cin>>size;
        cout<<"Please input "<<size<<" numbers: ";
        for(i=0;i<size;i++)
               cin>>a[i];
        Heap_sort(a,size);
        cout<<"The resorted array: ";
        for(i=0;i<size;i++)
              cout<<a[i]<<' ';
}
void Heap_adjust(int *a,int i,int size)
{
       int max=i,temp;
       int left_child=2*i+1;  //i的左子节点序号
       int right_child=2*i+2;//i的右子节点序号
       if(i<size/2)//如果i是叶子就不需要进行调整
       {
            if(left_child<size&&a[left_child]>a[max]) max=left_child;
            if(right_child<size&&a[right_child]>a[max]) max=right_child;
        }
      if(max!=i)
        {
            temp=a[max];
           a[max]=a[i];
           a[i]=temp;
           Heap_adjust(a,max,size);//避免调整之后以max为父节点的子树不是堆
        }
}
void Build_heap(int *a,int size)//建立堆
{
        int i;
        for(i=size/2-1;i>=0;i--)//非叶节点最大序号值为size/2 -1
             Heap_adjust(a,i,size);
}
void Heap_sort(int *a,int size)//堆排序
{
        int i,temp;
        Build_heap(a,size);
        for(i=size-1;i>=0;i--)
        {
           temp=a[i];//交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面 
           a[i]=a[0];
           a[0]=temp;
           Heap_adjust(a,0,i);//重新调整堆顶节点成为大顶堆
        }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值