【数据结构】堆


目录

一、堆

二、堆的模拟实现

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




}

相关推荐
唐诺1 小时前
几种广泛使用的 C++ 编译器
c++·编译器
XH华1 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生2 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_2 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
sanguine__2 小时前
Web APIs学习 (操作DOM BOM)
学习
落魄君子2 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
冷眼看人间恩怨2 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
菜鸡中的奋斗鸡→挣扎鸡2 小时前
滑动窗口 + 算法复习
数据结构·算法
红龙创客2 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin3 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin