堆
对于堆 这个名词 ,相信大家都不会很陌生吧!!!!!为什么呢????
因为我们 ,经常总是说什么 堆栈 、、、 以及什么堆排序的。。。。
今天我们讲的这个堆,跟上面是不沾边的,我们在这说的这个堆,,,,指的是堆 的数据结构 。。。。。
那到底什么是 堆呢???? 堆到底是一种什么样的数据结构 。。。。。
堆,所谓堆,,,,其实就是一种完全二叉树 。。。。。但是这棵树是有一些特点的。。。。
堆 又分为 大堆 还有小堆 。。。。
大堆 -------每个父节点的都大于孩子节点。
小堆 -------每个父节点的都小于孩子节点。
来看下面这个图 吧!!!!来理解一下堆的 树形结构
这棵树就是一个所谓的小堆,,,,,,每个父节点的权值 比 孩子节点的权值小;;;;;
对于一个堆,其实就是用一个顺序表来 ,,,,存储这些节点 。。。。。
如果要建立一个大堆 的话 ,我们可 现将左右 子树设置为大堆,,,然后再进行向下调整算法。。。。
所以,想建立一个完整的大堆 ,,,,就必须现将跟节点的左右子树看成是一个 大堆 。。。。
如果将这个 问题 看成是一个子问题的话 ,,,,,那么我们 ,就需要先从最底部开始调整了 ,直到根节点
下面是我写的是 一个 向下调整算法。。。。
void AdjustDown(int k )
{
int parent = k ;//表示父节点
int child = 2* k+1;//表示节点的左孩子
Compare compare;//比较函数(为了区分建立的是大堆还是小堆)
while(child < _a.size())//当左孩子存在时
{
if(child+1 <_a.size() && compare(_a[child+1] ,_a[child]))//如果右孩子存在 且右孩子的权值大于左孩子
{
child++;//child 就表示的右孩子的下标
}
if(compare(_a[child] ,_a[parent]))//比较子节点的权值 与 父节点的权值
{
swap(_a[child],_a[parent]);
parent= child;//继续向下调整
child = parent*2+1;
}
else//如果 子节点的权值 与 父节点的权值 符合条件 ,那么从他之后的树都符合条件
{
break;
}
}
}
建堆函数
如果要建成一个堆 ,,,,,就要 从第一个非叶子节点开始 ,,,,将该子树调整为大堆
代码 :::
template<class T , class Compare = Greater<T>>
class Heap
{
public:
Heap()
{}
Heap(T * a,size_t n)
:_a(a,a+n)
{
// 最后一个节点的下标为 n-1 ,,则该节点的父节点 就为第一个 非叶子节点 下标就是 (n-1-1)/2;
for(int i = (n-2)/2;i >= 0;i--)//循环直到根节点
{
AdjustDown(i);
}
}
protected:
vector<T> _a;
};
建成一个堆 之后,,,,,,就要进行堆的基本操作 了 。。。。
插入函数 Push
在结尾插上一个 节点 ,之后我们 就要对 从这个节点 开始直接到根节点的 进行向上调整了
向上 调整算法
void AdjustUp(int k)
{
int child = k;//向上调整的节点为孩子
int parent = (k-1)/2;//找到父节点 ,,,
Compare compare;
while(parent>= 0)
{
//判断此树是否符合 堆的性质
if(compare(_a[child] ,_a[parent]) )//不符合的话
{
swap(_a[child],_a[parent]);//交换
child = parent;//继续向上调整
parent = (child-1)/2;
}
else//如果符合,,那么上面的肯定都符合了 。。。
{
break;
}
}
}
删除一个节点Pop;;;(删除根节点 )
如果我们 ,,,,直接把第一个节点删除 , 。。那么 整个堆就会被破坏 。。。
所以我们 可 以现将 最后一个节点与根节点 交换 ,,,,然后删除最后一个节点,,,在从根节点开始对于整棵树进行向下调整算法。。。
void Pop()
{
assert(_a.size()!= 0);
swap(_a[0],_a[_a.size()-1]);
_a.pop_back();
AdjustDown(0);
}
这样就差不多可以完成一棵树了 。。。。。
下面是完整的代码 ::::
#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
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 = Greater<T>>//使用Compare来表示是建立大堆还是小堆
class Heap
{
public:
Heap()
{}
Heap(T * a,size_t n)
:_a(a,a+n)
{
// 最后一个节点的下标为 n-1 ,,则该节点的父节点 就为第一个 非叶子节点 下标就是 (n-1-1)/2;
for(int i = (n-2)/2;i >= 0;i--)//循环直到根节点
{
AdjustDown(i);
}
}
void Push(const T& x)
{
_a.push_back(x);
AdjustUp(_a.size()-1);
}
void Pop()
{
assert(Empty()!= true);
swap(_a[0],_a[_a.size()-1]);
_a.pop_back();
AdjustDown(0);
}
T& Top()
{
assert(_a.size());
return _a[0];
}
bool Empty()
{
return _a.size() == 0;
}
protected:
void AdjustDown(int k )
{
int parent = k ;//表示父节点
int child = 2* k+1;//表示节点的左孩子
Compare compare;//比较函数(为了区分建立的是大堆还是小堆)
while(child < _a.size())//当左孩子存在时
{
if(child+1 <_a.size() && compare(_a[child+1] ,_a[child]))//如果右孩子存在 且右孩子的权值大于左孩子
{
child++;//child 就表示的右孩子的下标
}
if(compare(_a[child] ,_a[parent]))//比较子节点的权值 与 父节点的权值
{
swap(_a[child],_a[parent]);
parent= child;//继续向下调整
child = parent*2+1;
}
else//如果 子节点的权值 与 父节点的权值 符合条件 ,那么从他之后的树都符合条件
{
break;
}
}
}
void AdjustUp(int k)
{
int child = k;//向上调整的节点为孩子
int parent = (k-1)/2;//找到父节点 ,,,
Compare compare;
while(parent>= 0)
{
//判断此树是否符合 堆的性质
if(compare(_a[child] ,_a[parent]) )//不符合的话
{
swap(_a[child],_a[parent]);//交换
child = parent;//继续向上调整
parent = (child-1)/2;
}
else//如果符合,,那么上面的肯定都符合了 。。。
{
break;
}
}
}
protected:
vector<T> _a;
};
void TestHeap()
{
int a [] = {10,11, 13, 12, 16, 18, 15, 17, 14, 19};
Heap<int,Less<int>> h(a,10);
h.Push(20);
h.Pop();
}