【数据结构】堆


目录

一、堆

二、堆的模拟实现

1.结构体

2.push

3.pop和top

三.实现堆排序

1.成堆算法

2.堆排序


heap模拟实现源码_gitee

一、堆

分为大堆和小堆

大堆是每个父节点都大于子节点,小堆则相反是每个父节点都小于子节点

底层抽象结构是完全二叉树,下面实现方式底层是数组

二、堆的模拟实现

1.结构体

cpp 复制代码
typedef int hptype;

typedef struct heap
{
	hptype* data;
	int size;
	int capacity;

}hp;


void hpinit(hp* x)
{
	assert(x);

	x->data = NULL;
	x->capacity = x->size = 0;

}

void hpdestory(hp* x)
{
	assert(x);
	free(x->data);
	x->data = NULL;
	x->capacity = x->size = 0;


}

上面的typedef是为了方便改动

2.push

cpp 复制代码
void swap(hptype* p1, hptype* p2)
{
	hptype tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;

}


//把底下不符合堆的进行调整
//小堆
void adjustup(hptype* data, int child)
{
	assert(data);
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (data[child] < data[parent])
		{
			swap(&data[child], &data[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else { break; }
	}



}
void hppush(hp* p, hptype x)
{
	assert(p);
	if (p->capacity == p->size)
	{
		int num = p->capacity == 0 ? 4 : p->capacity * 2;
		hptype* newdata = (hptype*)realloc(p->data, sizeof(hptype) * num);
		if (newdata == NULL)
		{
			perror("newdata==null");
			return;
		}
		p->data = newdata;
		p->capacity = num;


	}
	p->data[p->size++] = x;
	adjustup(p->data, p->size - 1);



}

3.pop和top

cpp 复制代码
void adjustdown(hptype* data, int size, int parent)
{
	assert(data);
	
	

	int small = parent * 2 + 1;



	while (small < size)
{  if(small+1<size&&data[small]>data[small+1])
		{
			small++;
		}
		if (data[parent] > data[small])
		{
			swap(&data[parent], &data[small]);
			parent = small;
			small = parent * 2 + 1;

		}
		else { break; }

	}




}
void hppop(hp* x)
{
	assert(x);
	assert(x->size > 0);
	swap(&x->data[0], &x->data[x->size - 1]);

	x->size--;

	adjustdown(x->data, x->size, 0);

}




hptype hptop(hp*x)
{
	assert(x);
	return x->data[0];
}


bool hpempty(hp*x)
{

	assert(x);
	return x->size == 0;
}

pop是实现的把堆顶pop,还要保持堆的形象

应用:实现top_k个最小数的打印

思路:先将数据建堆,在获取top后pop,循环k次

cpp 复制代码
void test2()
{
	int a[] = { 10,2,99,4,5,9,7,8,6, };
	hp x;
	hpinit(&x);
	for (int i=0;i<9;i++)
	{
		hppush(&x, a[i]);
	}
	int k = 0;
	scanf("%d", &k);
	while(k--)
	{
		printf("%d ", hptop(&x));
		hppop(&x);

	}





}

三.实现堆排序

1.成堆算法

1.adjustup+for;

2.adjustdown+for

实例逻辑是小堆

cpp 复制代码
void adjustup(hptype* data, int child)
{
	assert(data);
	int parent = (child - 1) / 2;


	while (child > 0)
	{
		if (data[child] < data[parent])
		{
			swap(&data[child], &data[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else { break; }
	}



}











void adjustdown(hptype* data, int size, int parent)
{
	assert(data);

	int small = parent * 2 + 1;
	while (small < size)
{  if(small+1<size&&data[small]>data[small+1])
		{
			small++;
		}
		if (data[parent] > data[small])
		{
			swap(&data[parent], &data[small]);
			parent = small;
			small = parent * 2 + 1;
		}
		else { break; }

	}




}

for正序遍历+adjustup,时间复杂度 nlogn

cpp 复制代码
void test3(){
	int a[10] = { 2,3,1,5,6,7,0,39,99,33 };
	for(int i=1;i<10;i++)
	{
		adjustup(a, i);

	}
	for(int  i=0;i<10;i++)
	{
		printf("%d ", a[i]);
	
	}


}

for从叶子节点的父节点遍历+adjustdown

cpp 复制代码
void test4() {
	int a[10] = { 2,3,1,5,6,7,0,39,99,33 };
	for (int i = (10-1-1)/2;i >=0;i--)
	{
		adjustdown(a, 10,i);

	}
	for (int i = 0;i < 10;i++)
	{
		printf("%d ", a[i]);

	}


}

*调整算法复杂度讨论

adjustup+for

adjustdown+for

2.堆排序

底层思想是先成堆,在取堆顶数与末尾交换,即此时末尾一定是最大或最小,再以此类推

cpp 复制代码
void heapsort(hptype *x,int n)
{
	assert(x);
	for(int i=1;i<n;i++)
	{
		adjustup(x, i);
	
	}


	int end = n - 1;

	while(end>0)
	{
		swap(&x[0], &x[end]);
		
		adjustdown(x, end, 0);
		
		end--;
	}


}

堆排序的升降是由堆是大小堆决定的,如果建成大堆,那么顶就是最大,取到队尾就是升序

如果小堆,取到队尾就是降序

升序=>大堆

降序=>小堆

*堆排序中while+adjustdown复杂度

四.*topk问题

求一堆数据中最大的前k个,这k个返回时不需要之间大小排序
思路1:直接对数据建大堆,顶上为最大,接着pop+top继续

时间复杂度:建堆O(n)+pop_topO( k*log(N) ) =>O(n)

空间复杂度:建堆O(n)
思路2:建k小堆,比顶上大的就顶上交换,然后再调整堆,以此类推

时间复杂度:建堆O(K)+遍历O(N-K) =>O( n )

空间复杂度:O(K)

cpp 复制代码
void topk()
{
	int k = 0;
	scanf("%d", &k);

	int* kmax = (int*)malloc(k * sizeof(int));
	FILE* fout = fopen("data.txt", "r");//先取k个数进行建堆
	for(int i=0;i<k;i++)
	{
		fscanf(fout, "%d", &kmax[i]);

	}

	for(int i=(k-1-1)/2;i>=0;i--)
	{
		adjustdown(kmax, k, i);
	
	}


	int x = 0;
//打开文件开始遍历比较
	while(fscanf(fout,"%d",&x)>0)
	{
	if(x>kmax[0])
	{
		kmax[0] = x;
		adjustdown(kmax, k, 0);
	}
	
	}

//打印
	for(int i=0;i<k;i++)
	{
	
		printf("%d ", kmax[i]);
	}




}

相关推荐
We་ct29 分钟前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·javascript·算法·leetcode·typescript
lilihuigz4 小时前
Tutor LMS 4.0 Beta版全新上线:以学习者为中心的移动优先学习体验
学习·在线教育·lms
王老师青少年编程4 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:合并果子
c++·算法·贪心·csp·信奥赛·哈夫曼贪心·合并果子
叼烟扛炮5 小时前
C++第二讲:类和对象(上)
数据结构·c++·算法·类和对象·struct·实例化
天疆说5 小时前
【哈密顿力学】深入解读航天器交会最优控制中的Hamilton函数
人工智能·算法·机器学习
wuweijianlove6 小时前
关于算法设计中的代价函数优化与约束求解的技术7
算法
leoufung6 小时前
LeetCode 149: Max Points on a Line - 解题思路详解
算法·leetcode·职场和发展
样例过了就是过了6 小时前
LeetCode热题100 最长公共子序列
c++·算法·leetcode·动态规划
HXDGCL7 小时前
矩形环形导轨:自动化循环线的核心运动单元解析
运维·算法·自动化
谭欣辰7 小时前
C++ 排列组合完整指南
开发语言·c++·算法