【数据结构】【堆】【c】详解堆的自顶向下算法、和堆的建立。

最近笔者花了几天时间研究了堆,下面做一些分享。

首先什么是堆。

堆在逻辑上是特殊的完全二叉树,在存储结构上是顺序表。一般用数组实现。

堆只分为大堆和小堆

所谓大堆是指:在堆中所有的最小二叉树的父亲节点必须比它的叶子节点大。

所谓小堆是指:在堆中所有的最小二叉树中父亲节点比它的叶子小。只有这两种特殊的情况下一个数组才能被称为堆。

堆的自顶向下算法:

 

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整

成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

如图所示

我们可以发现,此时看到的是一个小堆,就是不断地对比父节点 和它的叶子节点中较小的值进行交换。

接下来我们来看代码,笔者将详细介绍:

#define INIT_ARRAY_SIZE 50

int heap_size; //堆大小
int heap_cap_size;  //堆容量大小
int main() {
	int i = 0;
	int a[] = { 27,15,19,18,28,34,65,49,25,37 };
	int *array = NULL;
	array = (int *)malloc(INIT_ARRAY_SIZE * sizeof(int));
	int length = sizeof(a) / sizeof(int);
	printf("数组的长度是:%d\n", length);
	for (i = 0; i < length; ++i) {
		array[i] = a[i];
	}
	printf("原始数组为\n");
	print_array(array, length);
}

首实现先是一些准备工作,定义堆的大小,容量、用数组模拟一个假堆,用在实现我们的自顶向下算法中。

来看算法


/*返回以index为根的完全二叉树的左子树的索引,整个二叉树索引以0为开始*/
int left(int index) {
	return ((index << 1) + 1);
}
/*返回以index为根的完全二叉树的右子树的索引,整个二叉树索引以0为开始*/
int right(int index) {
	return ((index << 1) + 2);
}
/*两个数的交换*/
void swap(int* a, int* b) {
	int temp = *a;
	*a = *b;
	*b = temp;
	return;
}
void max_heap_adjust(int array[], int index) {//index是位置
	int left_index = left(index);
	int right_index = right(index);
	int largest = index;
	//左子树和父节点进行对比
	if (left_index <= (heap_size - 1) && array[left_index] > array[largest]) {
		largest = left_index;
	}
	//右子树和父节点进行对比
	if (right_index <= (heap_size - 1) && array[right_index] > array[largest]) {
		largest = right_index;
	}
	if (largest == index) {
		/*判断是否要进行递归调用,没交换一次最小二叉树的时候,可能会破坏前面已经调整好的堆
		的结构,所以交换一次需要从当前父亲节点开始重新进行自顶向下算法,重新调整堆*/
		/*这里的递归退出条件是左右子树下标都大于二叉树的总长度,即调整不能调整为止
		*/
		return;
	}
	else {
		//需要交换
		swap(&array[index], &array[largest]);
		//递归调用
		max_heap_adjust(array, largest);
	}
}

调整为大堆

看到的是传入堆头和要开始的位置,接下来计算它的左叶子和右叶子。再用largest记录它的父节点位置,然后分别判断左右子树是否大于父节点。

如果大于,下标赋值给largest,然后判断largest和index是否相等,如果不相等,进行交换,并且将largest(较大的位置)作为下一次要进行算法的起始点传入算法函数中,调整下面的假堆。

在这里值得一提的是,是否进入递归的判断条件,从程序的设计方式可以看到,如果函数体内所有的语句都不执行,判断就会成真,跳出函数,完成所有的调整,隐含意思是走到正数第一个叶子节点为止。

实现了自顶向下算法之后,我们构建一个堆,

下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算

法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的

子树开始调整,一直调整到根节点的树,就可以调整成堆。

下面我们来看代码实现:

void build_heap(int array[], int length) {
	heap_size = length;
	for (int i = ((heap_size - 1) >> 1); i >= 0; --i) {
		max_heap_adjust(array, i);
	}
}

 在堆的一些操作当中,自顶向下算法是基础,堆的创建很简单,它是将数组转换成一个大堆或者小堆的过程,我们的代码建立的是小堆。方法是从倒数第一个非叶子节点开始对堆进行自顶向下算法。就可以建立一个堆。

在这里特别鸣谢https://blog.csdn.net/u011068702/article/details/52771173

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值