C_C++算法_堆排序

堆排序

特点

堆是非线性数据结构,相当于一维数组,有两个直接后继。堆通常是一个可以被看做一棵树的数组对象,堆总是满足两个条件堆中某个结点的值总是不大于或不小于其父结点的值堆总是一棵完全二叉树

堆排序的最典型应用就是优先队列,根结点最大的堆叫做大顶堆根结点最小的堆叫做小顶堆。因此,二叉堆排序是在插入元素删除元素的时候进行的。堆是一个一维数组,同时也是一棵完全二叉树。因此,可以将堆按照从上到下从左到右的顺序将数组的索引号与树的各个节点进行映射,如下所示:

二叉堆数组索引映射

具体映射关系是任意父节点左边的索引号是自身的2倍,而任意父节点右边的索引号是自身索引号的2倍 + 1

添加数据思路

基于堆的特点当新的元素加入到队尾时,该元素将与自身父节点进行比较,大于父节点则进行交换。

添加代码

template<typename Item>
class MaxHeap {
private:
    Item *  item_heaps_;
    int     count_;

private:
    void shiftUp(int _index) {
        while (_index > 1 && item_heaps_[_index / 2] < item_heaps_[_index]) {
            // item_heaps_[_index / 2] 表示当前节点的父节点位置
            std::swap(item_heaps_[_index / 2], item_heaps_[_index]);
            _index /= 2;
        }
    }
public:
    MaxHeap(int _capacity) {
        item_heaps_ = new Item[_capacity + 1];
        count_ = 0;
    }
    ~MaxHeap() {
        delete [] item_heaps_;
    }
    void insert(Item _item) {
        item_heaps_[++count_] = _item;
        shiftUp(count_);
    }
};

删除数据思路

首先队列只能从第一个元素开始删除,在删除时现将第一个元素与最后一个元素进行交换,用交换后的第一个元素与子节点进行比较,左右两边的节点哪个节点元素大就与哪边的元素进行交换,交换后继续用此元素进行比较,直到均大于子节点为止。

删除数据代码

template<typename Item>
class MaxHeap {
private:
    Item *  item_heaps_;
    int     count_;
private:
    void shiftDown(int _index) {
        while (_index * 2 <= count_) {
            int swap_index = _index * 2;
            if (swap_index + 1 <= count_ && (item_heaps_[swap_index + 1] > item_heaps_[swap_index])) {
                // 左边的节点大于右边的节点,则准备与左边的换
                swap_index += 1;
            }

            if (item_heaps_[swap_index] < item_heaps_[_index]) {
                // 当前的节点大于准备要交换的节点,则不再循环
                break;
            }

            std::swap(item_heaps_[swap_index], item_heaps_[_index]);
            _index = swap_index;
        }
    }

public:
    MaxHeap(int _capacity) {
        item_heaps_ = new Item[_capacity + 1];
        count_ = 0;
    }
    ~MaxHeap() {
        delete [] item_heaps_;
    }
    Item extract() {
        Item max_item = item_heaps_[1];
        std::swap(item_heaps_[1], item_heaps_[count_--]);
        shiftDown(1);
        return max_item;
    }
};

索引堆

排序的稳定性

排序的稳定性体现在当数组中存在数值相等时,排序前后的相对位置不发生变换,如下所示,排序前后12这个元素的位置先后顺序没有发生改变。
排序稳定性
为了解决堆排序时数据的不稳定性可以使用索引堆,增加一个数据的索引映射。

template<typename Item>
class IndexHeap {
private:
    Item *  item_heaps_;
    int *   indexes_;
    int     count_;
private:
    void shiftUp(int _index) {
        while (_index > 1 && item_heaps_[indexes_[_index / 2]] < item_heaps_[indexes_[_index]]) {
            // item_heaps_[_index / 2] 表示当前节点的父节点位置
            std::swap(indexes_[_index / 2], indexes_[_index]);
            _index /= 2;
        }
    }
    void shiftDown(int _index) {
        while (_index * 2 <= count_) {
            int swap_index = _index * 2;
            if (swap_index + 1 <= count_ && (item_heaps_[indexes_[swap_index + 1]] > item_heaps_[indexes_[swap_index]])) {
                // 左边的节点大于右边的节点,则准备与左边的换
                ++swap_index;
            }

            if (item_heaps_[indexes_[swap_index]] < item_heaps_[indexes_[_index]]) {
                // 当前的节点大于准备要交换的节点,则不再循环
                break;
            }

            std::swap(indexes_[swap_index], indexes_[_index]);
            _index = swap_index;
        }
    }
public:
    IndexHeap(int _capacity) {
        item_heaps_ = new Item[_capacity + 1];
        indexes_ = new int [_capacity + 1];
        count_ = 0;
    }
    ~IndexHeap() {
        delete [] item_heaps_;
        delete [] indexes_;
    }
    bool isEmpty() {
        return count_ == 0;
    }
    int size() {
        return count_;
    }
    void insert(Item _item) {
        item_heaps_[++count_] = _item;
        indexes_[count_] = count_;
        shiftUp(count_);
    }
    Item extract() {
        Item max_item = item_heaps_[indexes_[1]];
        std::swap(indexes_[1], indexes_[count_--]);
        shiftDown(1);
        return max_item;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值