C语言实现堆

堆的概念及结构

                

堆的创建

         可以定义一个结构体,其中包含一个存放堆数据的一维数组,堆的容量,堆的元素个数

typedef int heapdatesype;
typedef struct heap
{
	heapdatesype* date;
	int size;
	int capacity;
}heap;

 堆元素顺序存储原理:

        

堆的初始化

        堆的初始化只需要将堆的容量,堆元素的个数置为零,再将数组首个元素地址置为空即可。

//堆的初始化:
void initheap(heap* hpp)
{
	assert(hpp);
	hpp->date = NULL;
	hpp->size = hpp->capacity = 0;
}

堆的销毁

        堆的销毁也只需要将堆的容量,堆元素的个数置为零,将数组空间free释放,再将数组首个元素地址置为空即可。

//堆的销毁:
void heapdestory(heap* hpp)
{

	assert(hpp);
	hpp->capacity = hpp->size =0;


	free(hpp->date);
	hpp->date = NULL;

}

堆数据的向上调整:

                1.child为需要调整的元素下标,根据二叉树的性质,我们知道其父亲的下标parent为

(child-1)/2;

                2.将child与parent比较,若child>parent则交换两个元素(大堆)(小堆为child<parent交换);

                3.然后child=parent;parent=(parent-1)/2;再进行步骤2;直到(child<=parent大堆)

(child>=parent小堆)时停下或者child<=0(即child已经没有parent)时停下。

//对数据向上调整:
void adjustup(heapdatesype* p, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (p[parent] < p[child])
		{
			swap(&(p[parent]), &(p[child]));
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
	
}

 堆数据的向上调整总结:

                原理图:
      

                向上调整前提(重要):数组前面的元素时堆;

                时间复杂度:log以2为底N的对数; 

                

堆元素的插入:

                1.首先要对堆中存放元素的数组进行容量检查(size=capacity时数组元素已满)

                2.若数组已经满了则需要扩容,定义一个newcapacity=(capacity ==0 ? 5 : capacity*2)

为新的容量。括号中三目操作符的意思是,若capacity==0则newcapacity=5,否则newcapacity=

capacity*2;然后用realloc进行扩容;

                3.尾插入新的元素x,然后size++;

                4.对尾插的新元素进行向上调整到合适位置;

//堆的插入:
void heappush(heap* hpp, heapdatesype x)
{
	assert(hpp);

	//检查容量:
	if (hpp->capacity == hpp->size)
	{
		int newcapticy = (hpp->capacity == 0 ? 4 : hpp->capacity * 2);

		heapdatesype* tmp = (heapdatesype*)realloc(hpp->date, newcapticy * sizeof(heapdatesype));

		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}

		hpp->capacity = newcapticy;
		hpp->date = tmp;
	}

	//插入:
	hpp->date[hpp->size] = x;
	hpp->size++;

	//对插入的数据进行向上调整:
	adjustup(hpp->date, hpp->size - 1);
}

堆的打印:

                堆的打印只需要将存放堆的数组遍历一遍打印即可

//堆的打印:
void heapprintf(heap* hpp)
{
	assert(hpp);
	int i = 0;
	for (i = 0; i < hpp->size; i++)
	{
		printf("%d ", hpp->date[i]);
	}

	printf("\n");
}

  返回堆顶的元素:

                直接返回存放堆元素数组的首个元素即可:

//返回堆顶的元素:
heapdatesype heaptop(heap* hpp)
{
	assert(hpp);
	assert(hpp->size > 0);

	return hpp->date[0];
}

  判断堆是否为空:

                判断size是否==0即可;

//判断堆是否为空:
bool emptyheap(heap* hpp)
{
	assert(hpp);

	return (hpp->size == 0);
}

堆元素的向下调整:

                1.parent为需要调整的元素的下标,n为堆元素的个数,p是存放堆元素的数组。

                2.根据所给的parent可以计算出其左儿子的下标child=2*parent+1;

                3.此处以大堆为例,需要判断左右两个儿子那个更大(小堆判断更小),将更大的那个

儿子的下标作为child即:若左儿子更大child=child;右儿子更大则child++;此时还需要注意左右

两个儿子的下标都要小于堆元素的个数n(child <n  && child+1 < n);

                4.比较此时child与parent的值,若child>parent(小堆child < parnet),则交换child与

parent的值。

                5.parent=child,child=2*child+1循环3,4步骤;直到parent被调整到合适的位置或者

child >=n时结束循环;

//堆(大堆)的向下调整:
void adjustdown(heapdatesype* p, int n, int parent)
{
	int child = 2 * parent + 1;
	while (child < n)
	{
		//找出更大的那个孩子:(需要保证child+1 < n)
		if ( child+1 < n && p[child] < p[child + 1])
		{
			child++;
		}
		if (p[parent] < p[child])
		{
			swap(&(p[child]), &(parent));
			//往下再次调整:
			parent = child;
			child = 2 * child + 1;
		}
		else
		{
			break;
		}
	}
}

总结:
                 

                时间复杂度:log以2为底N的对数;

                调整前提:被调整元素的左右子树仍然时堆;

堆顶元素的删除:

                1.交换堆顶元素和堆中最后一个元素;

                2.size--即删除堆顶的元素;

                3.将此时的堆顶元素进行向下调整;

 原理图:

//堆的删除:
void heappop(heap* hpp)
{
	assert(hpp);
	assert(hpp->size > 0);

	//交换第一个数据和最后一个数据
	swap(&(hpp->date[0]), &(hpp->date[hpp->size - 1]));

	hpp->size--;

	//对数据向下调整:
	adjustdown(hpp->date, hpp->size, 0);
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

(int*) nightfall

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值