数据结构学习第十七课(堆)

这篇博客介绍了小顶堆的实现,包括插入节点、遍历、删除堆顶元素(堆排序)等操作。通过模板类`heap<T>`展示了C++代码实现,并给出了一个整型数组的例子来演示堆的使用。堆是一种特殊的完全二叉树,用于实现优先队列等数据结构,具有高效插入和删除特性。
摘要由CSDN通过智能技术生成

1,头文件
#pragma once
#include<iostream>
using std::cout;
using std::endl;
//小顶堆
template<typename T>
class heap
{
public:
	heap():pRoot(NULL),len(0),maxLen(0){}
	~heap()
	{
		pRoot = NULL;
		len = 0;
		maxLen = 0;
	}
	void insertNode(const T& data);//插入
	void insertNode1(int*arr);
	void travel();//遍历
	T pop();//删除堆顶
private:
	T* pRoot;//指向根结点的指针 数组首地址
	int len;//元素个数 数组长度
	int maxLen;//容量
private:
	void _clear();//堆的删除(堆排序)
};
/*
堆的插入:(小顶堆)
1 直接放堆中
2 临时保存新插入元素
3 往上检查是否冲突(注意是用父结点和新插入元素比较)
如果不冲突,或者不能再往上了,插入完毕:
冲突:父结点覆盖子节点
4 新插入元素覆盖当前位置
*/

template<typename T>
inline void heap<T>::travel()
{
	cout << "heap:";
	for (int i = 0; i < 10; i++)
	{
		cout << pRoot[i] << " ";
	}
	cout << endl;
}

/*
堆的删除:(堆排序)
1 删堆顶 先输出堆顶
2 最后一个元素覆盖堆顶元素
3 从堆顶开始,往下循环(不要越界,如果比孩子中最小的还小,就停止
3.1 找出左右孩子中较小的那一个
3.2 用孩子覆盖父结点
*/
template<typename T>
T heap<T>::pop()
{
	if (0 == len)return (T)0;
	if (1 == len) 
	{
		cout << "删掉了:" << pRoot[0] << endl;
		len--;
		return pRoot[0];
	}
	//1 删堆顶 先输出堆顶
	T temp = pRoot[0];
	//2 最后一个元素覆盖堆顶元素
	pRoot[0] = pRoot[len - 1];
	//3 从堆顶开始, 往下循环(不要越界, 如果比孩子中最小的还小, 就停止
	int CurrentIdx = 0;
	int MinChildIdx;
	while (1)
	{
		if (CurrentIdx >= len/2-1)break;
		MinChildIdx = CurrentIdx * 2 + 1;
		//确定孩子中最小的那一个
		if (pRoot[MinChildIdx] > pRoot[MinChildIdx + 1])
		{
			MinChildIdx += 1;
		}
		if (pRoot[len - 1] < pRoot[MinChildIdx])
		{
			break;
		}
		// 用孩子覆盖父结点
		pRoot[CurrentIdx] = pRoot[MinChildIdx];
		//当前结点变化
		CurrentIdx = MinChildIdx;
	}
	//覆盖回来
	pRoot[CurrentIdx] = pRoot[len - 1];
	len--;
	cout << "删掉了:" << temp << endl;

	return temp;

	
}

template<typename T>
inline void heap<T>::_clear()
{

}
template<typename T>
inline void heap<T>::insertNode(const T& data)
{
	if (maxLen <= len)
	{
		maxLen = maxLen + (((maxLen >> 1) > 1) ? (maxLen >> 1) : 1);
		T* pNew = new T[maxLen];
		memcpy(pNew, pRoot, sizeof(T) * len);
		delete[] pRoot;
		pRoot = pNew;
	}
	//如果父为0
	if (0 == len)
	{
		pRoot[len++] = data;
		return;
	}
	//1 直接放进来
	int CurrentIdx = len;//插入数据的下标
	pRoot[CurrentIdx] = data;
	int ParentIdx = (CurrentIdx - 1) / 2;
	//2 临时存储新插入数据

	//3 循环 没有父或者不冲突就结束
	//while(CurrentIdx>0&&pRoot[CurrentIdx]<pRoot[ParentIdx])
	while (1)
	{
		if (CurrentIdx <= 0)break;
		ParentIdx = (CurrentIdx - 1) / 2;
		if (data > pRoot[ParentIdx])break;
		//冲突,父结点覆盖子节点
		pRoot[CurrentIdx] = pRoot[ParentIdx];
		//上升
		CurrentIdx = ParentIdx;
	}
	//4 新插入数据覆盖到当前位置
	pRoot[CurrentIdx] = data;
	len++;
}
/*

template<typename T>
inline void heap<T>::insertNode1(int* arr)
{
	//1 先直接开内存, 整个数组入堆
	len = 10;
	pRoot = new T[10];
	for (int i = 0; i < 10; i++)
	{
		pRoot[i] = arr[i];
	}
	/*int a=pRoot[CurrentIdx];
	int b = pRoot[ParentIdx];*/
	//2 从当前无序堆的最后一个元素开始循环 下标为n(len - 1 到1)
	//while (LastIdx)
	//{
	//	while ((pRoot[LastIdx] < pRoot[ParentIdx])&& LastIdx !=0)
	//	{
	//		int t = pRoot[LastIdx];
	//		pRoot[LastIdx] = pRoot[ParentIdx];
	//		pRoot[ParentIdx] = t;
	//	}
	//	LastIdx = ParentIdx;
	//	LastIdx--;
	//}
	//3 循环中循环拿当前元素pRoot[n]和它的父节点比较, 
	//一直到下标为0 如果冲突就交换
	int t;
	for (int i = 0; i <= len - 2; i++)//轮数
	{
		for (int j = 0; j <= len - 2 - i; j++)//每一轮j交换范围
		{
			if (pRoot[j] > pRoot[j + 1])
			{
				t = pRoot[j];
				pRoot[j] = pRoot[j + 1];
				pRoot[j + 1] = t;
			}
		}
	}
}

2,源文件
/*
满二叉树:同时满足以下两个特征的二叉树
         特征一:节点要么有两个孩子,要么没孩子;
		 特征二:叶子节点在同一层(高度相同);
完全二叉树:一个满二叉树 从最底层(叶子节点)从右往左删;
           满二叉树一定是完全二叉树;
		   完全二叉树不一定是满二叉树;

		   已知父结点下标为n:
		   左孩子下标为:2*n+1
		   右孩子下标为:2*n+2

		   已知左孩子m:
		   父结点为:(m-1)/2
		   已知右孩子m:
		   父结点为:(m-2)/2
		   由于右孩子下标一定是偶数,所以:
		   已知孩子下标为m,其父结点下标(m-1)/2;

堆:有序的完全二叉树;
堆的有序和有序二叉树的有序不同,堆的有序只是父子之间;
分类:
大顶堆(最小堆):父大于子
小顶堆(最大堆):子大于父
兄弟之间无序;
 */

#include "heap.h"
#include<stdio.h>
int main()
{
	int arr[10] = { 89,45,66,78,93,24,454,61,20,17 };
	heap<int> h;


		h.insertNode1(arr);
		h.travel();
	/*for (int i = 0; i < 10; i++)
	{
		h.pop();
		h.travel();
	}*/
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值