学习实现堆

 在我们上篇文章就提到了,实现完全二叉树或者是满二叉树,用以数组为底层结构的顺序表更加方便。

堆的概念:

如果有一个关键码的集合 K = { , , ,
} ,把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0, 1 , 2…,则称为小堆 ( 或大堆 ) 。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。所以我们今天以顺序表为基础来实现一下堆。

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;

}

  • 24
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值