堆排序算法以及代码实现

堆的一些知识点回顾
堆是一个完全二叉树
完全二叉树即是:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
堆满足两个性质: 堆的每一个父节点数值都大于(或小于)其子节点,堆的每个左子树和右子树也是一个堆。
堆分为最小堆和最大堆。最大堆就是每个父节点的数值要大于孩子节点,最小堆就是每个父节点的数值要小于孩子节点。排序要求从小到大的话,我们需要建立最大堆,反之建立最小堆。
堆的存储一般用数组来实现。假如父节点的数组下标为i的话,那么其左右节点的下标分别为:(2i+1)和 (2i+2)。如果孩子节点的下标为j的话,那么其父节点的下标为(j-1)/2。
完全二叉树中,假如有n个元素,那么在堆中最后一个父节点位置为(n/2-1)。

算法思想
建立堆(最大堆,or最小堆)
调整堆
堆排序:交换堆顶元素和堆的最后一个元素

如何创建堆
由于堆是使用数组保存的,所有我的理解是,建堆的过程就是堆化数组,即使给定的数组具有堆的性质。堆化的过程其实就是调整堆的过程,我们把调整堆的过程定义成一个单独的函数
void AdjustHeap(int heap[], int root_index, int heap_length),
第一个参数:代表堆的数组,
第二个参数:要调整的堆的根节点在数组中的下标,注意是下标。
第三个参数是堆的长度,也就是待建堆的数组长度,或者说 待调的数组元素个数。

调整堆的过程如下

1.根据根节点下标,确定左右子节点的下标。

2.找出根节点和左右子节点中最小值的下标,注意该过程要判断边界,即左右子节点的下标是否超出堆的长度。

3.如果最小值的下标等于根节点的下标,函数返回。

4.否则,交换把根节点与最小值交换,由于交换可能破坏了最小值所在子树的对性质,所以递归调用AdjustHeap函数。

堆化的时候,从哪个节点开始调整堆呢?
这个也是建堆的关键,我们可以这样想,子节点已经是堆了,无需在调整了,所以我们没必要从最后一个节点开始调整,一般是从(heap_length/2)开始调节,一直到下标1为止(堆的下标一般从1开始,0位置不用),从(heap_length/2)开始调节的原因是完全二叉树的性质决定的。这样遍历一遍,对每个节点调用堆调整函数就完成建堆了。

所以从(heap_length/2)处,开始自底向上遍历,遍历一次,调整一次。

堆排序:
取出堆的第一个元素,调整堆,循环下去,知道取完为止。
实现的过程就是
1.先把堆顶元素和堆的最后一个元素互换,
2.然后堆长度减1,再调整堆,注意此时只用从上面0这个位置,调整就行,且只调一次。
原因:
1.为什么从头部调:此时下面的都是大顶堆,只有头部不满足要求,所以从头调,即使调了之后使得下面的顺序不满足了,也因为一次调堆的函数,包含了向下的递归调堆。
2.为什么只调一次:上面建堆时是自下往上调,而这个是自顶往下,首先顶部的上面没有元素了,其次一次调堆函数的调用包含了自顶往下的过程

这样就使堆排序的空间复杂度为O(1)了,注意的是大顶堆排完序之后数组中元素的顺序是从小到大,小顶堆是从大到小。

代码实现:

1.建堆:

void make_heap(int *a, int len)
 {
    for(int i =  (len-1)/2; i >= 0; --i)    //遍历每个 非叶子节点
                  adjust_heap(a, i, len);//不用考虑那么多, 用面向对象的思乡去考虑,   
 }                                    //这个函数的作用就是用来使 当前节点的子树   符合   堆的规律
 

2… 要使某节点的当前节点的字数符合 堆规律,需要以下操作:

void adjust_heap(int* a, int node, int size)
{
        int left = 2*node + 1;
        int right = 2*node + 2;  
        int max = node;
        if( left < size && a[left] > a[max])    
                max = left;
        if( right < size && a[right] > a[max])
                max = right;
        if(max != node)
        {   
                swap( a[max], a[node]);    //交换节点
                adjust_heap(a, max, size);     很关于这里递归的解释请看下面
        }                     
}

————————————————
版权声明:本文为CSDN博主「pursue_my_life」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

在这里插入图片描述
排序:

for(int i = len - 1; i >= 0; i--)
 {
         swap(a[0], a[i]);           // 将当前最大的放置到数组末尾
         adjust_heap(a, 0 , i);              // 将未完成排序的部分继续进行堆排序
 }

完整代码:

#include <iostream>
using namespace std;
 
void adjust_heap(int* a, int node, int size)
{
        int left = 2*node + 1;
        int right = 2*node + 2;
        int max = node;
        if( left < size && a[left] > a[max])
                max = left;
        if( right < size && a[right] > a[max])
                max = right;
        if(max != node)
        {
                swap( a[max], a[node]);
                adjust_heap(a, max, size);
        }
}
 
void heap_sort(int* a, int len)//传入的len是长度,元素个数
{
        for(int i = len/2 -1; i >= 0; --i)
                adjust_heap(a, i, len);
 		//调堆,传入的i是开始调的下标
 		//第一个for代表一共调这么多次
        for(int i = len - 1; i >= 0; i--)
        {
        //这个for代表取len次,每次只调一次堆
                swap(a[0], a[i]);           // 将当前最大的放置到数组末尾
                adjust_heap(a, 0 , i);              // 将未完成排序的部分继续进行堆排序
        }
}
 
int main()
{
 
        int a[10] = {3, 2, 7, 4, 2, -999, -21, 99, 0, 9  };
        int len= sizeof(a) / sizeof(int);
 
        for(int i = 0; i < len; ++i)
                cout << a[i] << ' ';
        cout << endl;
 
        heap_sort(a, len);
 
        for(int i = 0; i < len; ++i)
                cout << a[i] << ' ';
        cout << endl;
 
        return 0;
}

 

参考链接

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值