堆
堆的概念:如果有一个关键码的集合K={k0, k1, k2, ······,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:ki <= K2*i +1 且ki<=K2*i + 2; i=0,1,2···,则称这个堆为最小堆(或者最大堆);其实这么多干干的概念还是很不好理解的;先来说一下什么是完全二叉树: 如果一颗具有N个结点的二叉树的结构与满二叉树的前N个结点的结构相同,称为完全二叉树; 满二叉树: 在一颗二叉树中,如果所有分支结点都存在左子树和右子树,并且所有的叶子结点都在同一层上。
从上图,我们可以很清楚的分别最小堆和最大堆的特点:
最小堆:任一结点的关键码均小于等于它的左右孩子的关键码,位于堆顶结点的关键码最小;
最大堆:任一结点的关键码均大于等于它的左右孩子的关键码,位于堆顶结点的关键码最大;
堆的创建
其实堆的创建就是把所提供给自己的层序遍历的二叉树的结果转化为具有堆特性的二叉树:
例如:给到这样一个数组:int arr[] = {53, 17, 78, 9, 45, 65, 87, 23};转化为小堆:
如上图所示,先从下向上找到第一个并不为叶子结点的结点(i = (sizeof(a) -2)/2),然后以他为根结点向下调整在左右孩子中找到最小的与之交换,在第①步中,不需要交换; 然后在向前走(i–),继续以他为根结点,找左右孩子中最小的交换,第②步中,我们就发生了交换;在第③步中,我们以17为根结点交换,但是要保证以他为根结点的所有子树都满足最小堆,所以它里面的所有结点都必须做出调整;最后一步就是把整个树都调整为最小堆:
这是向下调整的代码:
void _AdjustDown(size_t parent)
{
size_t child = parent * 2 + 1;
while (child < _heap.size())
{
if (child + 1 < _heap.size() && Compare()(_heap[child + 1], _heap[child]))
{
child += 1;
}
if (Compare()(_heap[child], _heap[parent]))
{
std::swap(_heap[parent], _heap[child]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
堆的插入
堆的插入其实就是每次在已经创建好的最小堆或者最大堆的后面插入但是插入之后,有可能会破坏堆的结构,就需要我们对堆进行重新调整以满足堆的特性:
如上图所示,最后要达到的结果就是这样, 我简单分析一下:其实就是用插入的元素和它的双亲进行比较如果是要构建小堆,那么,如果它比双亲小,就用它代替双亲,否则就已经是最小堆了(PS:这是基于堆原来就是小堆的基础上)
我们把这种调整的方法简单称为向上调整法:(插入的结点一直在向上走啊):
void _AdjustUp(size_t child)
{
while (child != 0)
{
int parent = (child - 1) >> 1;
if (Compare()(_heap[child], _heap[parent]))
{
std::swap(_heap[parent], _heap[child]);
child = parent;
//parent = (child - 1) >> 1;
}
else
return;
}
}
堆的删除
堆的删除是:偶从堆中删除堆顶元素。移除堆顶元素后,用堆的最后一个结点填补取走的堆顶元素,并将堆的实现元素个数减1,但用最后一个元素去掉堆顶元素之后有可能破坏堆,因此需要 将堆自顶向下调整,使其满足最大或最小堆。
如上图,在调整过后的树中,并不满足最小堆,所以需要自顶向下进行调整;其实和创建堆的向下调整是一样的;
附 堆的完整代码:
#pragma once
#include<vector>
#include<assert.h>
#include<iostream>
using namespace std;
//模板参数
//仿函数
template<class T>
struct Less
{
bool operator()(const T& left, const T& right)
{
return left < right;
}
};
template<class T>
struct Greater
{
bool operator()(const T& left, const T& right)
{
return left > right;
}
};
template<class T, class Compare = Less<T>>
class Heap
{
public:
//创建一个堆
Heap()
{}
Heap(const T array[], size_t size)
{
_heap.resize(size);
for (size_t idx = 0; idx < size; idx++)
{
_heap[idx] = array[idx];
}
int root = (size - 2) >> 1;
for (; root >= 0; --root)
{
_AdjustDown(root);
}
}
size_t Size()const
{
return _heap.size();
}
bool Empty()const
{
return _heap.empty();
}
void Insert(const T&data)
{
_heap.push_back(data);
size_t size = _heap.size();
size_t child = size - 1;
_AdjustUp(child);
}
void Remove()
{
assert(!Empty());
std::swap(_heap[0], _heap[_heap.size() - 1]);
_heap.pop_back();
_AdjustDown(0);
}
const T& Top()const
{
assert(!Empty());
return _heap[0];
}
private:
void _AdjustDown(size_t parent)
{
size_t child = parent * 2 + 1;
while (child < _heap.size())
{
if (child + 1 < _heap.size() && Compare()(_heap[child + 1], _heap[child]))
{
child += 1;
}
if (Compare()(_heap[child], _heap[parent]))
{
std::swap(_heap[parent], _heap[child]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
void _AdjustUp(size_t child)
{
while (child != 0)
{
int parent = (child - 1) >> 1;
if (Compare()(_heap[child], _heap[parent]))
{
std::swap(_heap[parent], _heap[child]);
child = parent;
//parent = (child - 1) >> 1;
}
else
return;
}
}
private:
std::vector<T> _heap;
};