与堆相关的知识

基础知识:

逻辑结构: 完全二叉树

物理(存储)结构: 顺序表

大根堆

  • 每一个结点元素的值都不小于左右子树结点的值

  • 根结点的元素最大

小根堆

  • 每一个结点元素的值都不大于左右子树结点的值

  • 根结点的元素最小

完全二叉树结点位置计算

  • 对于完全二叉树,最后一个分支结点是第n/2个结点 下标为n/2-1

  • 对于完全二叉树,第i个结点如果有左右孩子,则左孩子是第2i个 右孩子是第2i+1个

  • 对于完全二叉树,下标为i结点如果有左右孩子,则左孩子下标为2i+1,右孩子下标为2i+2

  • 对于完全二叉树,第i个结点的父结点为 第 i/2 个

  • 对于完全二叉树,下标为i结点的父结点下标为 (i-1)/2

大根堆功能实现

#include "maxheap.h"
/*
typedef int ElemType;
struct Maxheap{
    size_t cap;         //容量
    size_t size;        //元素个数
	ElemType elems[];
};

typedef struct MaxHeap * MHeap;
*/ 
MHeap create_maxheap(size_t cap){
	MHeap heap = (MHeap)malloc(sizeof(struct MaxHeap) + cap * sizeof(ElemType));
	if(heap != NULL){
		heap->cap = cap;
		heap->size = 0;
	}
	return heap;
}

//只调整了pos下标  使其作为一棵子树满足大根堆 
static void adjust_maxheap(ElemType elems[],size_t pos,size_t n){
	ElemType key = elems[pos];
	size_t child = 2*pos + 1;
	while(child < n){//左孩子存在的 
		if(child+1 < n && elems[child] < elems[child+1]){//右孩子存在 且 右孩子比左孩子大 
			++child; 
		}
		//child记录了左右孩子中较大孩子的下标位置
		if(key < elems[child]){//孩子结点元素比 父结点元素 大 
			elems[pos] = elems[child];  //把孩子元素放到父结点中
			pos = child;
			child = 2*pos + 1; 
		}else{
			break;
		}
	} 
	elems[pos] = key;
}


void rebuild_maxheap(ElemType elems[],size_t n){
	int i = n/2-1;
	for(;i>=0;--i){
		adjust_maxheap(elems,i,n);
	}
} 

//用n个元素创建一个大根堆
MHeap create_by_elems_maxheap(ElemType elems[],size_t n){
	MHeap heap = (MHeap)malloc(sizeof(struct MaxHeap) + n * sizeof(ElemType));
	if(heap != NULL){
		heap->cap = n;
		heap->size = n;
		int i;
		for(i=0;i<n;++i){
			heap->elems[i] = elems[i];
		}
		rebuild_maxheap(heap->elems,heap->size);//把这组数据调整为大根堆 
	}
	return heap;
}

void foreach_maxheap(MHeap heap,void (*foreach)(ElemType)){
	assert(heap!=NULL);
	int i;
	for(i=0;i<heap->size;++i){
		foreach(heap->elems[i]);
	}
}
/*
	MHeap h = create_xx(arr,10);
	push_maxheap(h,1024);
*/
//压入一个元素之后 需要保持大根堆的特征
int push_maxheap(MHeap heap,ElemType elem){
	assert(heap!=NULL);
	if(heap->cap == heap->size){
		return FAILURE;
	}
	int i = heap->size;  
	while(i>0){
		int parent = (i-1)/2; //父结点的下标位置
		if(heap->elems[parent] < elem){
			heap->elems[i] = heap->elems[parent];
			i = parent;
		}else{
			break;
		}
	} 
	heap->elems[i] = elem;
	++heap->size;
	return SUCCESS;
}

int pop_front_maxheap(MHeap heap,ElemType *pElem){
	assert(heap!=NULL);
	if(empty_maxheap(heap)){
		return FAILURE;
	}
	if(pElem!=NULL){
		*pElem = heap->elems[0];
	}
	//用最后一个元素替换第一个元素  
	--heap->size; 
	heap->elems[0] = heap->elems[heap->size];
	adjust_maxheap(heap->elems,0,heap->size);
	return SUCCESS;
}

int remove_maxheap(MHeap heap,size_t pos,ElemType *pElem){
	assert(heap!=NULL);
	//pos  [1,heap->size]
	if(heap->size==0 || pos==0 || pos>heap->size){
		return FAILURE;
	}
	if(pElem!=NULL){
		*pElem = heap->elems[pos-1];
	}
	--heap->size;
	ElemType elem = heap->elems[heap->size]; //最后一个元素 
	heap->elems[pos-1] = elem;
	adjust_maxheap(heap->elems,pos-1,heap->size); //向下调整 
	int index = pos-1;
	//elem = heap->elems[index];
	if(elem == heap->elems[index]){//没有把elem往下沉       试着向上浮 
		while(index>0){
			int parent = (index-1)/2;
			if(heap->elems[parent] < elem){
				heap->elems[index] = heap->elems[parent];
				index = parent;
			}else{
				break;
			}
		}
		heap->elems[index] = elem;
	}
	return SUCCESS;
}

int pop_back_maxheap(MHeap heap,ElemType *pElem){
	assert(heap!=NULL);
	if(empty_maxheap(heap)){
		return FAILURE;
	}
	--heap->size;
	if(pElem!=NULL){
		*pElem = heap->elems[heap->size];
	}
	return SUCCESS;
}

void clear_maxheap(MHeap heap){
	assert(heap!=NULL);
	heap->size = 0;
}

size_t size_maxheap(MHeap heap){
	assert(heap!=NULL);
	return heap->size;
}

bool empty_maxheap(MHeap heap){
	assert(heap!=NULL);
	return heap->size == 0;
}

void destroy_maxheap(MHeap heap){
	assert(heap!=NULL);
	free(heap);
}

//有一组数据  要进行排序     堆排序   时间复杂度 O(nlogn) 
//排行榜          TOP10    TOP100               1亿
void heap_sort(ElemType elems[],size_t n){
	rebuild_maxheap(elems,n);
	int i;
	for(i=n-1;i>0;--i){//n
		int tmp = elems[i];
		elems[i] = elems[0];
		elems[0] = tmp;
		adjust_maxheap(elems,0,i); //函数里循环执行次数最多情况  logn 
	}
}





堆的用途:排序 TOP10 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值