在我们上篇文章就提到了,实现完全二叉树或者是满二叉树,用以数组为底层结构的顺序表更加方便。
堆的概念:
1.堆的初始化
typedef int innt;
typedef struct list
{
innt* arr;
int sz;
int capcity;
}dui;//顺序表的底层结构数组,建立出来再初始化。
void init(dui* ps)
{
assert(ps);
ps->arr = NULL;
ps->capcity = ps->sz = 0;
}
2.往堆里面插入数据
我们堆的实现有大根堆有小根堆,其实逻辑上就是相反的,我们今天都以小根堆为例进行实现堆。
小根堆的跟是数据最小的,然后第二层,左边是的结点时比右边结点还小的结点,接着第三层,第四层...,我们的底层物理结构还是这样的数组,并不是逻辑结构那样的二叉树,所以我们每次插入的话是我们进行尾插,再进行向上调整或者向下调整。
我们今天来实现一下向上调整:
void push(dui* ps,innt x)
{
assert(ps);
if (ps->capcity == ps->sz)
{
int d = ps->capcity == 0 ? 4 : 2 * ps->capcity;
innt* ret = (innt*)realloc(ps->arr, sizeof(innt) * d);
if (ret == NULL)
{
perror("realloc false");
exit(1);
}
ps->arr = ret;
ps->capcity = d;
}
ps->arr[ps->sz] = x;
ps->sz++;
adjustup(ps->arr, ps->sz - 1);
}
先写一下顺序表一样的尾插
尾插结束之后,我们理一下向上调整的思路,向上调整(建立小堆)那底下的一层的结点比自己的父亲结点小就要和父亲结点交换,然后child的下标变成了父亲的下标,再通过child的下标找父亲结点,再比较,直到不再进行交换或者不再父结点到了堆顶。
值得一说的是父结点和子节点的关系是 parent=(child-1)/2 原理是取整原则 child=parent*2+1;
void Swap(innt* x, innt* y)
{
innt ret = *x;
*x = *y;
*y = ret;
}
void adjustup(innt* arr, int child)//时间复杂度是O(NlogN)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (arr[child] < arr[parent])
{
Swap(&arr[child], &arr[parent]);
child = parent;
parent = (parent - 1) / 2;
}
else
{
break;
}
}
}
3.获得堆顶数据
就和顺序表一样没什么好说的
int HpTop(dui* ps)
{
assert(ps);
return ps->arr[0];
}
4.删除堆顶数据
这是顺序表的结构实现的堆的功能,堆顶就是顺序表的表头,如果我们删除掉,按照顺序表的方式再进行把数据挪一遍,那时间复杂度就会是O(N),我们既然实现了向上调整方式,那肯定也有向下调整方式。如果我们把头结点和尾结点交换位置,并且减小sz,是不是就实现了减去堆顶的问题 ,那么堆的顺序坏了,我们使用向上调整方式,是不是就可以完成对堆顶的删除呢,那我们用向下调整的方法,实现一下后面的功能。
void HpPop(dui* ps)
{
assert(ps);
assert(ps->sz > 0);
Swap(&ps->arr[0], &ps->arr[ps->sz - 1]);
ps->sz--;
adjustdown(ps->arr, ps->sz, 0);
}
先写一下对堆顶和堆尾的交换。然后理一下思路
如果是向下交换,每一层和下一层进行比较交换,那么最后一层是不是不用进行交换了,那么这就少了非常多的数据进行比较交换,如果进行比较是和谁比较呢,那肯定是和两个子结点中更小的去比较了,所以我们先写一个找出更小子结点的判断,再进行向下调整
void adjustdown(innt* arr, int sz, int x)
{
int parent = x;
int child = 2 * x + 1;
while (child<sz)
{
if (child+1<sz&&arr[child] > arr[child + 1])
{
++child;
}
if (arr[parent] > arr[child])
{
Swap(&arr[parent], &arr[child]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
5 一些与顺序表相同的功能,就不过多介绍了
bool empty(dui* ps)
{
assert(ps);
return ps->sz == 0;
}
void destory(dui* ps)
{
free(ps->arr);
ps->arr = NULL;
ps->capcity = ps->sz = 0;
}