堆排序详解

堆:是一种特殊的完全二叉树,一般通过顺序表存储,分为大堆和小堆两类。

大堆:父节点的值恒大于子节点的值。

小堆:父节点的值恒小于子节点的值。

创建堆,可以使得根节点成为整个堆中保存最大或最小的值的结,根据这个特性,堆可以用于排序和解决TopK问题。

创建堆:

方法一:向上排序法

原理:对由前k个结点构成的堆,插入第k+1个结点到最后,然后不断与其父节点进行比对,符合条件就互换,不符合条件就留在原地结束循环。循环结束后,前k+1个结点仍是堆。

堆排序详解_第1张图片堆排序详解_第2张图片堆排序详解_第3张图片

时间复杂度:O(nlogn)

堆排序详解_第4张图片

堆排序详解_第5张图片

方法二:向下排序

原理:首先创造一个完全二叉树,然后从倒数第二层最后一个结点开始,不断以当前结点为根节点创建堆,直至最后以根节点创建堆。每次创建堆,都是先比较该结点的子节点,找出更可能上移的结点,然后与父节点比较,符合条件就互换,然后继续该步骤,不符合条件就停止循环。循环结束后,该部分就成为了一个堆。

堆排序详解_第6张图片堆排序详解_第7张图片堆排序详解_第8张图片时间复杂度:O(N)

堆排序详解_第9张图片

堆排序:

思路:将根节点与最后一个结点互换,使得最后一个结点永远保存当前最大(小)的结点,然后对互换后的根节点进行向下排序,使得排完序列后整个二叉树仍然是堆,再将新的根节点与倒数第二个结点互换,重复上述操作。最后,整个堆就是一个升序或降序的顺序表。

时间复杂度:O(NlogN)

每个根节点最多移动logN次,一共N-1个结点进行移动,因此时间复杂度是NlogN。

具体代码实现:

void swap1(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void up_insert1(HPDataType* a, int i)
{
	int child = i;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			swap1(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;


		}
		else
		{
			break;
		}


	}


}
void down_insert1(HPDataType* a, int n, int i)
{
	int parent = i;
	int child = parent * 2 + 1;
	while (child < n)
	{


		if (child + 1 < n && a[child] > a[child + 1])
		{
			child++;
		}
		if (a[parent] > a[child])
		{
			swap1(&a[parent], &a[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}


	}


}
void HeapSort(int* a, int n)
{
	//创建堆
    for (int i = 1; i < n; i++)
    {
		up_insert1(a, i);
    }
	测试
	//for (int i = 0; i < n; i++)
	//{
	//	printf("%d ", a[i]);
	//}
	//printf("\n");


	//排序
	for (int i = 0; i < n; i++)
	{
		swap1(&a[0], &a[n - 1-i]);
		down_insert1(a, n - i - 1, 0);
	}
	//测试
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");


}

TopK问题:

问题要求:对N个数据,只取最大(或最小)的前k个。

思路:先用前k个数据创建堆,此处要求取最大的前k个,则创建小堆。

创建小堆后根元素为整个堆中最下的元素,新的元素若比根节点大则替换根节点,然后进行向下排序,使得整个二叉树仍然为堆。进行N-k次上述操作,得到的堆就会保存最大的前k个元素。

具体代码:

void CreateNDate()
{
	// 造数据
	int n = 10000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}


	for (size_t i = 0; i < n; ++i)
	{
		int x = rand() % 1000000;
		fprintf(fin, "%d\n", x);
	}




	fclose(fin);
}
void swap1(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void down_insert1(HPDataType* a, int n, int i)
{
	int parent = i;
	int child = parent * 2 + 1;
	while (child < n)
	{


		if (child + 1 < n && a[child] > a[child + 1])
		{
			child++;
		}
		if (a[parent] > a[child])
		{
			swap1(&a[parent], &a[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}


	}


}
void PrintTopK(int k)
{
	const char* file = "data.txt";
	FILE* fin = fopen(file, "r");
     
	//创建小堆  (找最大的前k个)
	int* arr = (int*)malloc(sizeof(int) * k);
	if (arr == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	int i = 0;
	int num = k;
	while (num--)
	{
		fscanf(fin, "%d\n", &arr[i]);
		i++;
	}


	Heap hp;
	HeapCreate(&hp, arr, k);


	//插入其余元素
	int x = 0;
	int count = 0;
	while (fscanf(fin, "%d\n", &x) != EOF)
	{   
	/*	if (count % 1000 == 0)
		{
			x *= 1000;
		}*/


		if (x > hp.a[0])
		{
			hp.a[0] = x;
			down_insert1(hp.a, k, 0);
		}
	}


	printf("插入成功\n");
	//测试
	for (int j = 0; j < k; j++)
	{
		printf("%d  ",hp.a[j]);


	}
	printf("\n");


}

你可能感兴趣的:(算法,c++,c语言,数据结构)