【数据结构】堆


目录

一、堆

二、堆的模拟实现

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]);
	}




}

相关推荐
KingRumn5 小时前
Linux信号之标准信号与实时信号
linux·算法
半夏知半秋7 小时前
docker常用指令整理
运维·笔记·后端·学习·docker·容器
LXS_3578 小时前
Day 18 C++提高 之 STL常用容器(string、vector、deque)
开发语言·c++·笔记·学习方法·改行学it
蒸蒸yyyyzwd8 小时前
网络编程——threadpool.h学习笔记
笔记·学习
浪子不回头4158 小时前
SGLang学习笔记
人工智能·笔记·学习
源代码•宸8 小时前
Leetcode—620. 有趣的电影&&Q3. 有趣的电影【简单】
数据库·后端·mysql·算法·leetcode·职场和发展
2301_800256119 小时前
地理空间数据库中的CPU 和 I/O 开销
数据库·算法·oracle
deng-c-f9 小时前
Linux C/C++ 学习日记(53):原子操作(二):实现shared_ptr
开发语言·c++·学习
一个不知名程序员www9 小时前
算法学习入门---结构体和类(C++)
c++·算法