哈夫曼树及堆

哈夫曼树及堆

一、哈夫曼树

最优二叉树,是一类带权路径长度最短的树

所谓树的带权路径长度,就是树中所有叶节点的权值乘上其到根节点的路径长度(若根节点为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\t", pbuff[i]);
		}
		printf("\n");
	}
};

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;//将当前节点的值赋值过去
	}
 }

posted @ 2021-04-09 10:26  kisfly  阅读(201)  评论(0编辑  收藏  举报