【堆排序】小范围排序算法

堆排序算法使用

堆排序原理及实现

关于最大堆

什么是最大堆最小堆?最大(小)堆是指在树中,存在一个结点而且该结点有儿子结点,该结点的data域值都不小于(大于)其儿子结点的data域值,并且它是一个完全二叉树(不是满二叉树)。注意区分选择树,因为选择树(selection tree)概念和最小堆有些类似,他们都有一个特点是“树中的根结点都表示树中的最小元素结点”。同理最大堆的根结点是树中元素最大的。那么来看具体的看一下它长什么样?(最小堆这里省略)
在这里插入图片描述

最大堆ADT的创建

(1)创建一个最大堆
(2)最大堆的插入
(3)最大堆的删除
创建最大堆就需要考虑它的内存表达形式。在二叉树进行遍历的方法分为:先序遍历、中序遍历、后序遍历和层序遍历。我们可以通过层序遍历的方式将二叉树结点存储在数组中,由于最大堆是完全二叉树不会存在数组的空间浪费。
在这里插入图片描述

那么对于数组我们怎么操作父结点和左右子结点呢?对于完全二叉树采用顺序存储表示,那么对于任意一个下标为i(1 ≤ i ≤ n)的结点:
(1)、父结点为:i / 2(i ≠ 1),若i = 1,则i是根节点。
(2)、左子结点:2i(2i ≤ n), 若不满足则无左子结点。
(3)、右子结点:2i + 1(2i + 1 ≤ n),若不满足则无右子结点。
在这里插入图片描述
故选取数组作为最大堆的内存表达形式

基本定义:
struct Heap{
    int size ;
    int *array;
    Heap(int n)
    {
        size = 0;
        array = new int [n+1];
    }
    Heap()
    {
        delete array;
    }
    };
最大堆的插入

插入操作需要保证满足完全二叉树的标准,所以插入位置一定为末尾结点,那么就是数组中的array[++size]。并且要满足最大堆的父结点关键字值大于子结点,那么就需要移动相对位置。可以参考下图:
在这里插入图片描述

void insert (int value)//插入
    {
        array[++size] = value;
        int index = size;
        while (index > 1)
        {
            if (array[index] < array[index/2]) swap (array[index],array[index/2]);
            index /= 2;
        }
    }
最大堆的删除

最大堆的删除操作,就是从根结点删除元素。同样删除后该树仍然是一颗完全二叉树,这里我们需要通过移动尾结点,让其满足完全二叉树的定义。如图在下面最大堆执行删除操作:
在这里插入图片描述
在这里插入图片描述

最大堆的创建

创建最大堆的思路就是先创建一个空的最大堆,然后将N个数值一个个插入最大堆中,便创建好了一个元素为N的最大堆。
堆排序,便是将根结点一个个删除、弹出,便降序排序好了。(若是需要升序排序,便构建最小堆

题目

好了,终于到了题目部分:已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。

排序算法最差时间分析平均时间复杂度
冒泡排序O(N^2)O(N^2)
快速排序O(N^2)O(N*log2N)
插入排序O(N^2)O(N^2)
归并排序O(N*log2N)O(N*log2N)
堆排序O(N*log2N)O(N*log2N)
计数排序O(N)O(N)
基数排序O(N)O(N)
希尔排序O(N^2)O(N*log2N)

考虑到时间复杂度为O(N)的算法:计数排序与基数排序,因为不知道数字范围,所以不考虑。
其次是时间复杂度为O(N^2)的排序算法:冒泡与选择,但是冒泡与选择是与数组原始顺序没有关系的。
如果是插入排序,插入排序也是我看到这个题的第一个思路,因为插入的范围不和超过k。所以在这个题的时间复杂度应该是O(NK)。但应该还不是最好的。
那么接下来继续考虑时间复杂度为(N
log2N)的排序算法:归并排序,也与数组原始顺序无关。
最终解决思路是,构建了一个大小为K的最小堆,先将前k个元素放入堆中,因为元素的移动距离不超过k,故而最小值必然在堆中,然后弹出根结点,将第k+1个元素插入堆中,弹出根结点。重复此操作,便排序完成。
时间复杂度为O(N*log2k)。

struct Heap{
    int size ;
    int *array;
    Heap(int n){
        size = 0;
        array = new int [n+1];
    }
    ~Heap()
    {
        delete array;
    }
    bool empty()//判断是否为空
    {
        if (size != 0) return false;
        return true;
    }
    void insert (int value)//插入
    {
        array[++size] = value;
        int index = size;
        while (index > 1)
        {
            if (array[index] < array[index/2]) swap (array[index],array[index/2]);
            index /= 2;
        }
    }
    void del()//删除
    {
        if(empty()) return;
        swap(array[1],array[size--]);
        int index = 1;
        while (2*index <= size)
        {
            int next = 2*index;
            if (next < size && array[next+1] < array[next]) next++;
            if (array[index]>array[next])
            {
                swap(array[index],array[next]);
                index = next;
            }
            else break;
        }
    }
    int max (){
        if (empty()) return -1;
        return array[1];
    }
};
class ScaleSort {
public:
    vector<int> sortElement(vector<int> A, int n, int k) {
        Heap heap(k);
        int a[n];
        for (int i=0;i<k;i++)
        {
            heap.insert(A[i]);
        }
        for (int i=k;i<n;i++)
        {
            a[i-k]=heap.max();
            heap.del();
            heap.insert(A[i]);
        }
        for (int i=n-k;i<n;i++)
        {
            a[i]=heap.max();
            heap.del();
        }
        for (int i=0;i<n;i++)
            A[i]=a[i];
        return A;
    }
};

原理部分参考作者:凌云壮志几多愁
链接:https://www.jianshu.com/p/21bef3fc3030
来源:简书

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值