堆排序算法-数据结构与算法

堆排序

分为两类:
1.大顶堆:
父的顶点比孩子结点都大。
2.小顶堆:
父的顶点比孩子结点都小。

举例大顶堆:
1.我们使用的是顺序存储【数组】,按层次存储(从上至下,从左至右),

那么我们的编号的关系是:若父节点下标为key,那么左孩子结点的下标为2key,右孩子则为2key+1。 (因为右孩子在左孩子右一个位置,我们又按照层次顺序存储,所以左孩子+1等于右孩子)。

2.我们拿到的也是一个数组,但是初始状态并不一定满足大根堆【不是大根堆】,所以我们要维护这个数组,对他进行调整。对初始数组的调整的过程叫做,建立大根堆。
当我们build_sort后我们就获得了一个大根堆。【定义在顶部】
类似于:
在这里插入图片描述

我们的真正所需要的是一个降序的序列,而现在是看不出来的。
关键操作来了:
当我们通过build_heap 获得了一个大顶堆,【注意当前的长度为length】我们还要对他进行 输出a[1]堆顶元素,然后堆顶元素a[1]和数组的最后一个元素交换。

在这里插入图片描述

这时我们认为最后的这个结点(值为4)已经不属于大顶堆。因此长度变为length-1
这个时候我们的大顶堆相当于变成了下图,只有3个顶点,现在已经不满足定义,因为堆顶元素破坏了大顶堆的结构(破坏了定义)。

在这里插入图片描述

因此我们只需要把堆顶元素进行调整 adjust_heap
我们就会得到新的大顶堆(也不能说是新的,就是我们需要的结果)
在这里插入图片描述
现在是不是又回到了我们刚开始第一步的操作的样子,,这样就可以永远保证堆顶元素是剩下的顶点构成的集合中的最大值。输出堆顶元素a[1]
因此,一个循环直到堆顶元素,我们就会按照降序输出了。

代码如下:

void heap_sort(int length){
	//建立大根堆后 才获得一个大根堆  才能用来输出
	build_heap();
	for(int i=length;i>=1;i--){
	//注意这里是输出堆顶元素 a[1] 不要写成 a[i] ,今天写的时候就是因为这个问题搞了好久,最后才发现,吐了。
		cout<<a[1]<<" ";
		//这里的i就是长度
		adjust_sort(a[1],i--);	
	}
}

4.build_heap:
我们只需要对非叶结点的最后一个有孩子结点的 位置开始 直到 数组存储数据的第一个位置 1 ,对这个区间的元素按倒序进行调整。
在这里插入图片描述

上图就是 从值为5的结点开始一直往前 到值为3的结点为止

那个开始的位置是 length/2, 在这里就不推了。

//建立堆的代码
void build_heap(){
	for(int i=length/2;i>=1;i--){
		adjust_heap(a[i],length);
	}
}

5.无论是在建堆的过程中还是,输出的过程中都要调整堆adjust

  • 为什么要调整堆,我们建立的是大顶堆,一旦不符合大顶堆的定义就需要调整
  • 定义:父结点要比孩子结点都大
  • 从定义中可以看出两个东西:
  • ①如果有孩子结点(非叶结点),父结点要比孩子结点都大
  • ②如果没有孩子结点(叶结点) 则不需要进行操作

思路:

  • 传了一个需要调整的结点下标key,我们先保存这个元素的值到a[0]
  • 我们通过他与他的孩子们的关系判断他是否符合大顶堆的定义
  • 若符合那么不做操作
  • 若不符合则 当前key的位置替换成最大的孩子,而key需要移动到那个孩子的位置,因为替换了后,原来的位置已经满足定义,而我们这个key的位置也算是新加进来的。
  • 到这个时候也是回到了第一步,所以迭代操作。

迭代的时候采用*2的递增速度,因为每次都是找其孩子。

代码:

void adjust_heap(int key, int length){
	a[0]=a[key];
	for(int i=key*2;i<=length;i*=2){
		if(i<length && a[i+1]>a[i])	i++;
		if(a[0]>a[i]){
			break;
		}else{
			a[key]=a[i];
			key=i;
		}
	}
	a[key]=a[0];	
}

最后附上完整源代码:
其他排序源码传送门

#include <iostream>
using namespace std;


int a[11] = {0,14,9,6,13,21,10,16,17,2,12};

void swap(int *a,int *b) {
    int *t = a;
    a = b;
    b =t;
}

void adjustDown(int key, int length) {
    a[0]= a[key];
    int  j;
        for (j =key*2; j <=length; j*=2)
        {
            if (j<length && a[j]<a[j+1]) {
                j++;
            }
            if (a[0] >=a[j]) {
                break;
            }
            else {
                a[key] = a[j];
                key = j;
            }
           
        }
        a[key] = a[0];
}

void my_build_heap(int length) {
    for (int i = length/2; i >0; i--)
    {
        adjustDown(i,length);
    }
}

void my_heap_sort(int length) {
    my_build_heap(length);
    for (int i =length; i>0; i--)
    {
        cout << a[1] << " ";
        swap(a[1], a[i]);
        adjustDown(1,i-1);
    }
}

int main()
{
    my_heap_sort(10);
    
   return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值