目录
hello,各位baby🤗们,好久不见,甚是想念!好了,不多说废话了,接下来进入我们今天的学习
今天我们要学的是堆,使用自定义的方式实现堆。
在实现堆之前,我们先来了解一下什么是堆
堆是什么
先上一波概念,在计算机科学中,堆是一种基于数的数据结构,通常用完全二叉树来实现。(完全二叉树是除最后一层外,其他层都是填满的,当然最后一层填满的也是完全二叉树。完全二叉树添加新节点时必须要靠左添加!)
有可能概念不是那么好懂,接下来看一张图就能明白了
到这有可能有些小伙伴会疑惑了,为什么上面的这个堆叫大顶堆,别急,接下来就来讲讲什么是大顶堆和小顶堆
大顶堆和小顶堆
大顶堆符合任意节点与它的父节点符合父节点值大于等于子节点值
小顶堆符合任意节点与它的父节点符合父节点值小于等于子节点值
堆了解到这,还远远不够,为了能实现堆,这里还在多补充点知识
补充知识
1.最顶层的节点(没有父亲)的称之为root根节点
2.堆(底层是完全二叉树实现)是一种非线性结构,但存储时可以采用线性的数组结构来存储数据
3.用数组存储,如果从索引0开始存储节点数据
1.节点i的父节点为(i-1)/2
2.节点i的左子节点为2*i+1,右子节点为2*i+2
了解以上概念,自己实现堆就没什么大问题了,在这里我要实现的一个类既能实现大顶堆也能实现小顶堆(是不是有点高级(嘻嘻嘻))
堆实现
创建类
当为真(true)时为大顶堆,为假(false)时为小顶堆
class Heap
{
public:
Heap(vector<int>& v1,bool max)
{
this->_V = v1;
_size = v1.size();
_max = max;
heapify();
}
public:
vector<int> _V;
int _size;
bool _max;
};
在这申明一下,为了简单起见和方便再在力扣刷题,这里就不用泛型实现堆了,用int类型实现
细心的小伙伴可能会发现在构造方法中为什么多了一行代码,其实我们要自己实现堆功能的第一步就是要建堆,多的那行代码就是用来建堆的
建堆heapify
主流的建堆算法有两种,一种是威廉姆斯建堆算法,另一种是佛洛依德建堆算法。
威廉姆斯建堆算法就是从堆底添加节点不断上浮,这里就不细讲了(时间复杂度没有佛洛依德算法的好),时间复杂度为O(n*log(n))
佛洛依德建堆算法思路:
1.找到最后一个非叶子节点
2.从后向前,对每个节点执行下潜
时间复杂度为O(n)
void heapify()//建堆
{
int index = (_size >> 1) - 1;//找到最后一个非叶子节点
for (int i = index; i >= 0; i--)
{
down(i);//下潜
}
}
上面讲两种建堆算法中有提到上浮和下潜,接下来就来实现上浮和下潜代码,可以说上浮和下潜是堆中最重要的,所有功能都是基于这两个代码上实现的
上浮up&下潜down
下潜:(以大顶堆为例)将parent索引处的元素下潜,与两个孩子较大者交换,直至没有孩子或孩子都没它大
void Heap::down(int parent)
{
int left = 2 * parent + 1;
int right = left + 1;
int MaxorMin = parent;
if (left<_size && (_max?_V[left]>_V[MaxorMin]:_V[left]<_V[MaxorMin]))
{
MaxorMin = left;
}
if (right<_size && (_max ? _V[right]>_V[MaxorMin]:_V[right] < _V[MaxorMin]))
{
MaxorMin = right;
}
if (MaxorMin != parent)
{
swap(_V[parent], _V[MaxorMin]);
down(MaxorMin);//递归
}
}
上浮:(以大顶堆为例)将插入的元素上浮,直至value小于父亲或到堆顶
上浮代码思想和插入排序思想相似
void Heap::up(int value)
{
_V.push_back(-1);
int child = _size;
while (child > 0)
{
int parent = (child - 1) / 2;
//三目操作符
bool cmp = _max ? value > _V[parent]:value < _V[parent];
if (cmp)
{
_V[child] = _V[parent];
}
else
{
break;
}
child = parent;
}
_V[child] = value;
}
实现完上浮和下潜接下来的代码就是易如反掌😏
堆的增删替换
增是往堆底加入节点
void myPush(int value)
{//增
up(value);
_size++;
}
删是删除堆顶节点并返回堆顶节点的值
int myPop()
{
int deleted = _V[0];
swap(_V[0], _V[_size - 1]);
_V.pop_back();
_size--;
down(0);
return deleted;
}
替换是替换堆顶节点
void myReplace(int replaced)
{
_V[0] = replaced;
down(0);
}
到这为止,堆的功能基本已经完全实现了🎉
测试案例
void test01()
{
vector<int> v1 = { 2,4,6,5,1,3,7 };
Heap minheap(v1, false);
for (vector<int>::iterator it = minheap._V.begin(); it != minheap._V.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
Heap maxheap(v1, true);
for (vector<int>::iterator it = maxheap._V.begin(); it != maxheap._V.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
学习的时间总是短暂的,这篇blog到这里就结束了~
创作不易,还望各位小可爱🤗多多支持🌹🌹🌹
想要点赞收藏关注❤️❤️❤️
如有错,还望各位大佬指点一下😘