哈夫曼树及堆
一、哈夫曼树
最优二叉树,是一类带权路径长度最短的树
所谓树的带权路径长度,就是树中所有叶节点的权值乘上其到根节点的路径长度(若根节点为0层,叶节点到根节点的路径长度为叶节点的层数)。树的路径长度是从树根到每一节点的路径长度之和
权值自己设定
二、堆
最大(小)堆,其实就是最大(小)完全二叉树
三、最大堆
特性
1.是完全二叉树,满足完全二叉树的特性
2.堆中的任意一个节点的值都必须大于或等于其中最大的子节点的值
四、最大堆的实现
#pragma once
template<typename T>
class cmyheap
{
T* pbuff;
size_t len;
size_t maxsize;
public:
cmyheap();
~cmyheap();
void clear();
void appendnode(T const& scrdata);//从尾部追加数据
void deletenode();
void initheap(T arr[], size_t srclen);
public:
void printfheap()
{
for (size_t i = 0; i < len; i++)
{
printf("%d ", pbuff[i]);
}
printf("
");
}
};
template<typename T>
cmyheap<T>::cmyheap()
{
pbuff = nullptr;
len = maxsize = 0;
}
template<typename T>
cmyheap<T>::~cmyheap()
{
clear();
}
template<typename T>
void cmyheap<T>::clear()
{
if (pbuff)
delete[]pbuff;
pbuff = nullptr;
len = maxsize = 0;
}
template<typename T>
void cmyheap<T>::appendnode(T const & scrdata)
{
if (len >= maxsize)
{
maxsize = maxsize + ((maxsize >> 1) > 1 ? (maxsize >> 1) : 1);
T* ptemp = new T[maxsize];
memcpy(ptemp, pbuff, sizeof(T)*len);
if (pbuff)
{
delete[]pbuff;
pbuff = nullptr;
}
pbuff = ptemp;
}
pbuff[len++] = scrdata;
//这只是实现了一颗完全二叉树,但并不是最大堆
//需要通过最后添加的这个节点的位置,去找父节点
//进行插入排序这种方式的比较和交换
//插入排序:将待排序数,和已排好序的数据进行比较,看用升序还是降序排序
//就是说要用最后一个添加的节点,依次与他的父节点相比进行交换,一直比到根节点为止(纵向次序--就是从下到上)
int tempindex = len - 1;//当前节点下标的位置
T tempnum = scrdata;//保存最后要添加的那个数据
//循环判断,最好的可能性,和父节点比较发现比父节点小,不交换。最坏的可能性,一直比到根节点
while (tempindex)//当这个值为0的时候,表示判断到了根节点
{
int parentindex = (tempindex - 1) >> 1;//得到父节点的下标
if (tempnum > pbuff[parentindex])
{
pbuff[tempindex] = pbuff[parentindex];//将父节点的数据赋值给当前节点,就是数据从上往下赋值
}
else
break;
tempindex = parentindex;//把父节点的下标赋值成当前节点下标
}
//上面执行的都是赋值操作,还没有操作要添加的数据
//要添加的数据,是拷贝给最后一个比较完的当前节点
pbuff[tempindex] = tempnum;//记得最后要添加的数据拷贝回去
}
//删除规则
//1.每次只能删除根节点
//2.把最后一个节点移动到根节点
//3.调整根节点的位置,让这个二叉树重新满足堆的特性
template<typename T>
void cmyheap<T>::deletenode()
{
if (len == 0)
return;
//数组元素只能覆盖掉,不能删除
if(len>1)//如果堆中只有一个根节点,不需要把最后一个节点的值再赋值给根节点
pbuff[0] = pbuff[len - 1];
len--;
//下面要开始往下面进行比较了
int index = 0;
T tempnum = pbuff[0];//保存当前节点的值
while (true)
{
int left = 2 * index + 1;//这是左子树的下标
int right = 2 * index + 2;//这是右子树的下标
//如果左子树的下标比最后的节点的下标还要大,就直接退出
if (left > (int)len - 1)
break;
//到了这一步,证明当前节点下面至少有一个节点
bool isleft = true;//假设和左子树比
if (right<=(int)len-1)//证明右子树存在
{
if (pbuff[left] < pbuff[right])//且右边和左边大,那么就和右子树比较
isleft = false;
}
//到了这里,找到左右子树中最大的那个节点了
if (isleft)
{
//和左子树比较
if (tempnum < pbuff[left])//证明当前节点比左子树小
{
pbuff[index] = pbuff[left];//将左子树赋值给当前节点
index = left;//因为要一直比到条件不成立,所以要更新当前节点的下标
}
else//当前节点比左子树大,就不用交换了
break;
}
else
{
//和右子树比较
if (tempnum < pbuff[right])//证明当前节点比右子树小
{
pbuff[index] = pbuff[right];//将右子树赋值给当前节点
index = right;//因为要一直比到条件不成立,所以要更新当前节点的下标
}
else//当前节点比右子树大,就不用交换了
break;
}
}
pbuff[index] = tempnum;//将当前节点的值赋值过去
}
//堆的删除
//1.清空原来可能有的数据
//2.一次在堆中加入所有元素
//3.从最后一个有子节点的节点开始,依次调整次序满足最大堆的特性
template<typename T>
void cmyheap<T>::initheap(T arr[], size_t srclen)
{
clear();//先清除原来树中可能存在的数据
if (srclen == 0)
return;
maxsize = srclen;
len = srclen;
pbuff = new T[maxsize];
for (size_t i = 0; i < len; i++)
{
pbuff[i] = arr[i];//用数组给这个二叉树初始化
}
//上面只是将数据插入进来了,还没有构成最大堆
//(len-2)>>1----表示最后那个节点的父节点
for (int i = (len - 2) >> 1; i >= 0; i--)
{
int index = i;//当前节点的下标
T tempnum = pbuff[i];//保存当前节点的值
while (true)
{
int left = 2 * index + 1;//这是左子树的下标
int right = 2 * index + 2;//这是右子树的下标
//如果左子树的下标比最后的节点的下标还要大,就直接退出
if (left > (int)len - 1)
break;
//到了这一步,证明当前节点下面至少有一个节点
bool isleft = true;//假设和左子树比
if (right <= (int)len - 1)//证明右子树存在
{
if (pbuff[left] < pbuff[right])//且右边和左边大,那么就和右子树比较
isleft = false;
}
//到了这里,找到左右子树中最大的那个节点了
if (isleft)
{
//和左子树比较
if (tempnum < pbuff[left])//证明当前节点比左子树小
{
pbuff[index] = pbuff[left];//将左子树赋值给当前节点
index = left;//因为要一直比到条件不成立,所以要更新当前节点的下标
}
else//当前节点比左子树大,就不用交换了
break;
}
else
{
//和右子树比较
if (tempnum < pbuff[right])//证明当前节点比右子树小
{
pbuff[index] = pbuff[right];//将右子树赋值给当前节点
index = right;//因为要一直比到条件不成立,所以要更新当前节点的下标
}
else//当前节点比右子树大,就不用交换了
break;
}
}
pbuff[index] = tempnum;//将当前节点的值赋值过去
}
}