堆排序

本文深入讲解堆数据结构的概念,包括最大堆与最小堆的特点,并详细介绍了堆的存储方式及实现方法,如插入、删除操作的具体步骤,以及如何将数组调整为最大堆。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


堆是一个完全二叉树的数组对象。树每一层都是满的,最后一层可能除外。

最大堆:父节点关键字不小于两个子节点的关键字;

最小堆:父节点关键字不大于两个子节点的关键字。

堆一般存储在数组中,根节点可以从下标0或1开始。

下标从0开始:父节点位置 i ,左右子节点位置为 (i+1)*2 - 1;  (i+1)*2。

下标从1开始:父节点位置 i ,左右子节点位置为     i*2 ;  i*2+1。

 

堆排序思路:对于数组中a的元素a[0~n-1],首先把数据最大堆化,交换a[0]与a[n-1],即删除最大堆的根节点,放到后面,然后再把删除后的堆调整为最大堆。循环这个过程知道堆中只有一个元素。(完成后就是一个从小到大排列的数组了)。

 

堆得插入:

          (1)堆得大小加1,将数据放到堆的最后一个位置。

          (2)调整为堆:与父节点比较,如果值比父节点大,着交换父子节点;重复这个过程直到父节点比子节点大或者到达根节点。

堆的删除:

          (1)取出根节点。将最后一个节点放到根节点出。

          (2)从根节点开始向下调整为最大堆:找出值最大的儿子,如果该值比父节点大,则交换;重复直到不满足条件或者到达堆尾。

 

#define MAX_SIZE 200
#define HEAP_FULL(n) (n >= MAX_SIZE - 1)
#define HEAP_EMPTY(n) (n == 0)

typedef struct Heap
{
	int data[MAX_SIZE];  //数据可以用指针,然后动态内存分配。这样根据情况调整数组大小
	int size;
}Heap;

void initHeap(Heap *heap)
{
	heap->size = 0;
}
//最大堆得插入,元素下标从1开始的情况
void insertHeap(Heap *heap, int element)
{
	int i;
	if(HEAP_FULL(heap->size))
	{
		printf("heap is full...\n");
		return;
	}	

	heap->size++;
	i = heap->size;
	while((i != 1) && (element > heap->data[i/2]) )
	{
		heap->data[i] = heap->data[i/2];
		i /= 2;
	}
	heap->data[i] = element;
}

int deleteHeap(Heap *heap)
{
	int item,tmp;
	int parent = 1,child = 2;
	if(HEAP_EMPTY(heap->size))
	{
		printf("heap is empty...\n");
		exit(0);
	}
	item = heap->data[1];
	tmp = heap->data[heap->size--];   //指向堆得最后一个元素。  size减1
	while(child <=heap->size)
	{
		if((child < heap->size) && (heap->data[child] < heap->data[child + 1]))
			child++;
		if(tmp >= heap->data[child])
			break;
		heap->data[parent] = heap->data[child];
		parent = child;
		child *= 2;
	}
	heap->data[parent] = tmp;   //将最后第一个插入到这里。
	return item;
}


 将数组调整为最大堆:

         从最后一个有子节点的节点向前开始最大堆化,根据完全二叉树的特性:有儿子的节点=(没有儿子的节点)或者(没有儿子的节点+1)。因此最后一个有子节点的元素位置为 i=(n-1)/2 (注:相对于根节点从0开始的情况,如果是从1开始,则为i = n/2。其中n为堆最后一个元素的下标)。

 

void siftdown(int a[], int i, int n) 
{
 	int j; 
	int t= a[i];
	while((j = 2*i + 1 ) < n) 
	{
		if(j<n-1 && a[j] < a[j + 1])
			 j++; //两子女中选大者
 		if(t<a[j]) 
		{ 
			a[i] = a[j];
			i = j; 
		} 
		else 
			break;
 	}
 	a[i] = t;
}

//根节点从0开始。这里的n是数组长度
void heap (int a[], int n) 
{
	int i;
 	for(i= (n-2)/2; i>=0; i--)
 		siftdown(a, i, n); //调整为堆
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值