为了账号安全,请及时绑定邮箱和手机立即绑定

堆排序(Heapsort)

标签:
Java

1.排序问题

  现有一个含有N个数字的数组S,如何通过程序把这个数组变成有序的数组?

  例如:

  排序前:S:5,3,7,5,9,4,1,100,50

  排序后:S:1,3,4,5,5,7,9,50,100

2.二叉堆(binary heaps)

  进行堆排序前,需要先把数组排成二叉堆,故这里先介绍二叉堆。

什么是二叉堆?

  定义:二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。

  如果我们要用堆排序把数组排成递增有序的数组,则需要先排成最大堆;如果要把数组排成递减有序的数组,则需要先排成最小堆。

  最大堆示意图:

  

  上图为按照字母排序(如T>S>P)排成的最大堆。a[2]和a[3]为a[1]的子节点,且a[1]>=a[2], a[1]>=a[3];a[4]和a[5]为a[2]的子节点,且a[2]>=a[4], a[2]>=a[5]。

  即如果2k+1项元素在a[]数组中存在,则a[2k]和a[2k+1]为a[k]的子节点。

  子节点间不一定是a[2k]>=a[2k+1]。

  注意:最大堆的数组a[]是从a[1]开始的!

如何把数组做出二叉堆?  

  这里介绍用从下而上的制作最大堆的方法:  

  令a[]数组是从a[1]开始的N项数组。为方便解释,假设N=11,a[]数组如下图:

  

  先从int k = N/2(即k=5)开始讨论 :

   

  1.首先确认int j = 2k; j+1项元素是否在数组中。在此例子中,j+1=11,j+1项元素在数组中。

  2.然后比较 j 项元素和 j+1 项元素大小:如果a[j+1] >= a[j] , 则 j++;反之则不进行操作。

  3.然后比较 j 项元素和 k 项元素大小:如果 a[j] >= a[k] , 则交换 j 项元素和 k 项元素,将j的值赋给k, k = j;否则不进行操作。

  4.检查k下面还有没节点:if(2 * k <= N), 如果有,则继续 1,2,3,4步骤,直到下面没节点为止。

  在这里,k下面没节点了,检查k=4时的情况:

  

  执行1,2,3,4步骤后,检查k=3时的情况:

  

  执行1,2,3,4步骤后,检查k=2时的情况:

   

  执行1,2,3,4步骤后,检查k=1时的情况:

  

  这样,最大堆便形成了。

  实现代码:(注意:heapsort是基于数组首项元素index为1的,而我们的数组首项元素index为0,因此,比较元素和交互元素时要减1。)

复制代码

.hpublic:    
//下沉:针对一项元素,确保它大于等于子节点。
    void Sink(int k, int N);//开始排序
    void Sort();//i项元素比j项元素小?
    bool Less(int i, int j);//交换i项元素与j项元素
    void Exchange(int i, int j);private:
     TArray<int> MyIntArray;

.cpp 
void AMyHeapSort::Sort()
{    int N = MyIntArray.Num();    //排成最大堆
    for (int k = N / 2; k >= 1; k--) Sink(k, N);
}void AMyHeapSort::Sink(int k,int N)
{    //注意:heapsort是基于数组首项元素index为1的,而我们的数组首项元素index为0,故k-1项元素才是sink的目标元素;    //序号k应该在数组范围内
    if (k > MyIntArray.Num()) return;    //2 * k是子元素的序号
    while (2 * k <= N)
    {        int j = 2 * k;        //如果隔壁的元素比较大,则用隔壁的
        if (j < N && Less(j, j + 1)) j++;        //如果k项元素比子元素大,返回,下沉结束
        if (!Less(k, j)) break;        //如果k项元素小于等于子元素,交换它们        Exchange(k, j);        //继续往下检查
        k = j;
    }
}bool AMyHeapSort::Less(int i, int j)
{    //注意:heapsort是基于数组首项元素index为1的,而我们的数组首项元素index为0,故比较时减1;    //序号i,j应该在数组范围内
    if (i > MyIntArray.Num() || j > MyIntArray.Num()) return false;    //比较大小
    return MyIntArray[i-1] < MyIntArray[j-1];
}void AMyHeapSort::Exchange(int i, int j)
{    //注意:heapsort是基于数组首项元素index为1的,而我们的数组首项元素index为0,故交换时减1;    //序号i,j应该在数组范围内
    if (i > MyIntArray.Num() || j > MyIntArray.Num()) return;
    MyIntArray.Swap(i - 1, j - 1);
}

复制代码

 

3.堆排序

  数组形成最大堆后,只需简单几步就可以把完成堆排序:

  1.把数组的第一个元素和最后一个元素交换,这样,数组中最大的元素就排到了数组的尾端。

  2.把前N-1个元素看成一个数组。由于除了第一个元素,其它元素已经满足了最大堆的要求,所以对第一个元素执行Sink(),这个N-1个元素的数组就形成了最大堆。

  3.重复1,2步骤,直到最后新形成的数组只有一个元素为止。

  这样,数组就变成了有序递增的数组了,堆排序完成。

4.堆排序实现完整代码

  

复制代码

.h

UCLASS()class ALGORITHM_API AMyHeapSort : public AActor
{
    GENERATED_BODY()    
public:    
    // Sets default values for this actor's properties    AMyHeapSort();    // Called every frame
    virtual void Tick(float DeltaTime) override;    //生成数组
    void InitArray(int N);    //下沉:针对一项元素,确保它大于等于子节点。
    void Sink(int k, int N);    //i项元素比j项元素小?
    bool Less(int i, int j);    //交换i项元素与j项元素
    void Exchange(int i, int j);    //开始排序
    void Sort();protected:    // Called when the game starts or when spawned
    virtual void BeginPlay() override;public:    
    

private:
    TArray<int> MyIntArray;
    
};

.cpp// Sets default valuesAMyHeapSort::AMyHeapSort()
{     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

}// Called when the game starts or when spawnedvoid AMyHeapSort::BeginPlay()
{
    Super::BeginPlay();    //测试    //生成数组
    InitArray(10000);    //排序前
    for (int i = 0; i < MyIntArray.Num(); i++)
    {
        UKismetSystemLibrary::PrintString(this, "Before: " + FString::FromInt(i) + " : " + FString::FromInt(MyIntArray[i]));
    }
    Sort();    //排序后
    for (int i = 0; i < MyIntArray.Num(); i++)
    {
        UKismetSystemLibrary::PrintString(this, "After: " + FString::FromInt(i) + " : " + FString::FromInt(MyIntArray[i]));
    }
}// Called every framevoid AMyHeapSort::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}void AMyHeapSort::InitArray(int N)
{
    FRandomStream Stream;
    Stream.GenerateNewSeed();    for (int i = 0; i < N; i++)
    {
        MyIntArray.Add(Stream.RandRange(0, 100));
    }
}void AMyHeapSort::Sink(int k,int N)
{    //注意:heapsort是基于数组首项元素index为1的,而我们的数组首项元素index为0,故k-1项元素才是sink的目标元素;    //序号k应该在数组范围内
    if (k > MyIntArray.Num()) return;    //2 * k是子元素的序号
    while (2 * k <= N)
    {        int j = 2 * k;        //如果隔壁的元素比较大,则用隔壁的
        if (j < N && Less(j, j + 1)) j++;        //如果k项元素比子元素大,返回,下沉结束
        if (!Less(k, j)) break;        //如果k项元素小于等于子元素,交换它们        Exchange(k, j);        //继续往下检查
        k = j;
    }
}bool AMyHeapSort::Less(int i, int j)
{    //注意:heapsort是基于数组首项元素index为1的,而我们的数组首项元素index为0,故比较时减1;    //序号i,j应该在数组范围内
    if (i > MyIntArray.Num() || j > MyIntArray.Num()) return false;    //比较大小
    return MyIntArray[i-1] < MyIntArray[j-1];
}void AMyHeapSort::Exchange(int i, int j)
{    //注意:heapsort是基于数组首项元素index为1的,而我们的数组首项元素index为0,故交换时减1;    //序号i,j应该在数组范围内
    if (i > MyIntArray.Num() || j > MyIntArray.Num()) return;
    MyIntArray.Swap(i - 1, j - 1);
}void AMyHeapSort::Sort()
{    //注意:heapsort是基于数组首项元素index为1的,而我们的数组首项元素index为0,故k-1项元素才是sink的目标元素;
    int N = MyIntArray.Num();    //排成最大堆
    for (int k = N / 2; k >= 1; k--) Sink(k, N);    //不断地把最大的元素排在数组的尾端,形成有序递增数组
    while (N > 1)
    {
        Exchange(1, N);
        Sink(1,--N);
    }
}

复制代码

原文出处:https://www.cnblogs.com/mcomco/p/10148529.html  

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消