一、堆的初始化
1、堆的概念
堆实际上是一种完全二叉树,堆可分为大堆和小堆。大堆就是左右孩子的值都小于其父节点的值,根节点的值最大;小堆就是左右孩子的值都小于其父节点的值,根节点的值最小。
2、建堆
第一步:先给出一个数组
第二步:根据该数组,将其构建成完全二叉树的结构
第三步:以这个完全二叉树为基础构造初始堆,从最后一个非叶子节点开始向上调整,这里以小堆为例
这里对以上调整过程加以解释说明:
(1)调整的时候从最后一个非叶子节点向上调整,设最后一个非叶子节点的位置为index,数组的大小为size,则index=size/2-1。
如上图:最后一个非叶子节点的位置为4,数组的大小为10,则4=10/2-1。
(2)向最后一个非叶子节点开始调整的过程是:
先比较左右孩子的大小,找出左右孩子中较小的那一个;
然后将左右孩子中较小的那一个与其父节点进行比较,如果父节点较大就进行交换,否则就去调整下一棵子树。
第四步:代码实现
template <class T>
class Heap
{
public:
//缺省的构造函数
Heap()
{}
Heap( T* a, size_t size)
{
//将数组中的数据放到顺序表里面
for (size_t i = 0; i < size; i++)
{
_a.reserve(size);
_a.push_back(a[i]);
}
//进行调整
for (int i = _a.size() / 2 - 1; i >= 0; --i)
{
_AdjustDown(i)
}
}
protected:
void _AdjustDown(int root)
{
int left = root * 2 + 1;
int right = left + 1;
//如果左孩子不存在,则root为叶子节点,已不需再调整
while (left < _a.size())
{
//找出左右孩子中较小的那一个
int min = left;
//注意判断右孩子有没有越界
if (right<_a.size() &&
_a[left]>_a[right])
{
min = right;
}
//找出左右孩子中较小的那一个与父节点进行比较
if (_a[min] < _a[root])
{
swap(_a[min], _a[root]);
root = min;
left = root * 2 + 1;
right = left + 1;
}
else
break;
}
}
private:
vector<T> _a;
};
二、相关操作
1、向堆中插入元素,并保证堆的性质
(1)、步骤
步骤一:向存储堆的顺序表中push进去一个数据x;
步骤二:从堆的最后一个叶子节点开始向上调整
(2)、举例说明
(3)、代码实现
//向堆尾插入元素
void Push(int x)
{
_a.push_back(x);
_AdjustUp(_a.size() - 1);
}
//从最后一个叶子节点开始往上调整
void _AdjustUp(int root)
{
int child=root;
while (child > 0)
{
int parent = (child - 1) / 2;
if (_a[child] < _a[parent])
{
swap(_a[child], _a[parent]);
child = parent;
}
else
{
break;
}
}
}
2、删除堆顶元素
(1)、步骤
步骤一:将堆顶元素和最后一个非叶子节点进行交换;
步骤二:将交换后的最后一个非叶子节点删除;
步骤三:从堆顶开始往下进行调整
(2)、举例说明
(3)、代码实现
//删除堆顶元素
void Pop()
{
swap(_a[0], _a[_a.size() - 1]);
_a.pop_back();
_AdjustDown(0);
}
三、堆排序
1、算法描述
堆排序实际上是一种选择排序,如果想要降序排列,则先构建一个小堆,设小堆的大小为size
步骤一:先将堆顶元素和最后一个元素进行交换;
步骤二:将size减1,缩小堆的大小,即不包括最后一个元素;
步骤三:将size缩减后再进行调整,即除过最后一个元素之外,将剩下的元素调整成小堆结构。
2、举例说明
3、代码实现
void HeapSort(int n)
{
//先调成小堆结构
for (int i = _a.size() / 2 - 1; i >= 0; --i)
{
_AdjustDownSort(i, n);
}
int end = n - 1;
while (end)
{
swap(_a[0], _a[end]);
_AdjustDownSort(0, end);
--end;
}
}
//从最后一个非叶子节点开始调整(排序用)
void _AdjustDownSort(int root,int n)
{
int left = root * 2 + 1;
int right = left + 1;
//如果左孩子不存在,则root为叶子节点,已不需再调整
while (left < n)
{
//找出左右孩子中较小的那一个
int min = left;
//注意判断右孩子有没有越界
if (right<n && _a[left]>_a[right])
{
min = right;
}
//找出左右孩子中较小的那一个与父节点进行比较
if (_a[min] < _a[root])
{
swap(_a[min], _a[root]);
root = min;
left = root * 2 + 1;
right = left + 1;
}
else
break;
}
}
说明:由于每一调整的范围不同,因此_AdjustDownSort()函数需要两个形参,所以该函数与上面的调整函数不太相同。