堆排序
特点
堆是非线性数据结构,相当于一维数组,有两个直接后继。堆通常是一个可以被看做一棵树的数组对象,堆总是满足两个条件堆中某个结点的值总是不大于或不小于其父结点的值
和堆总是一棵完全二叉树
。
堆排序的最典型应用就是优先队列,根结点最大
的堆叫做大顶堆
,根结点最小
的堆叫做小顶堆
。因此,二叉堆排序是在插入元素
和删除元素
的时候进行的。堆是一个一维数组,同时也是一棵完全二叉树。因此,可以将堆按照从上到下从左到右的顺序将数组的索引号与树的各个节点进行映射,如下所示:
具体映射关系是任意父节点左边的索引号是自身的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;
}
};