数据结构(初阶 七)堆排序

1. 堆排序

假设有一串乱序数组,如下:
在这里插入图片描述
逻辑结构图:
在这里插入图片描述

方法一:利用现有的堆进行堆排序

  • 思路:模拟实现了堆后即可对一传乱序数组进行堆排序。假设以小根堆,排升序为例:
    1.首先,把每个元素插入到堆中。
    2.其次,依次遍历堆顶的元素,将堆顶元素赋值到数组里,从下标0开始,赋值后删除堆顶元素,++数组下标。此时堆就会重新调整,最终堆顶依旧是最小的,再重复上述赋值堆顶到数组的操作,直到堆为空.
  • 代码:

void HeapSort(int* a, int size)
{
     
     
//创建堆结构并初始化
	HP hp; 
	HeapInit(&hp);
//将数组元素插入堆中
	for (int i = 0; i < size; i++)
	{
     
     
		HeapPush(&hp, a[i]);
	}
//此段代码的时间复杂度为O(N*logN),因为HeapPush函数的内部执行过程就是把
//数组的每个元素插入堆中,有N次。
//接着,每插入一个数据都要重新向上调整(AdjustUp)高度次以确保为堆,
//每个都要调整高度次,高度为logN,综上此段为O(N*logN)

	size_t j = 0;
//依次遍历,取堆顶赋值数组,++下标,pop堆顶,依次循环,直至堆为空
	while (!HeapEmpty(&hp))
	{
     
     
		a[j] = HeapTop(&hp);
		j++;
		HeapPop(&hp);
	}
//此段的时间复杂度同样为O(N*logN)


	HeapDestroy(&hp);//记得销毁动态开辟空间
}
int main()
{
     
     
	int a[] = {
     
      4,2,7,8,5,1,0,6 };
	HeapSort(a, sizeof(a) / sizeof(int)); //实现堆排序
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
		printf("%d ", a[i]); //打印
	}
	printf("\n");
	return 0;

//比冒泡排序O(N^2)要快不少,但是实现堆的过程太过复杂。而且,此法的空间复杂度也是很大的,
//达到了O(N)。原因是实现堆的过程是动态开辟的,所以空间复杂度自然是O(N)。
//因为实现堆的过程是动态开辟的,所以空间复杂度是O(N)
}

方法二:直接对数组建堆

1.向上调整建堆

  • 思路:
    把第一个数字看成堆,也就是4,当第二个数字插入进去的时候,进行向上调整算法,使其确保为小堆,插入数据过程就是遍历数组,确保数组里每一个数进行向上调整算法。

  • 流程图:
    在这里插入图片描述

  • 代码实现:

//交换
void Swap(int* pa, int* pb)
{
     
     
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}
//向上调整算法
void AdjustUp(int* a, size_t child)
{
     
     
	size_t parent = (child - 1) / 2;
	while (child > 0)
	{
     
     
		if (a[child] < a[parent]) //小根堆
		{
     
     
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
     
     
			break;
		}
	}
}
//升序
void HeapSort(int* a, int n)
{
     
     
	//建堆
	int i = 0;
	for (i = 1; i < n; i++) //应该从i=1时遍历,因为第一个数据在堆里不需要调整,后续再插入时调整
	{
     
     
		AdjustUp(a, i);
	}
}
int main()
{
     
     
	int a[] = {
     
      4,2,7,8,5,1,0,6 };
	HeapSort(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
     
     
		printf("%d ", a[i]);
	}
	return 0;
}

2. 向下调整建堆

不能使用向下调建堆的方式建堆,因为要使用向下调整算法的前提是必须保证根节点的左右节点均为小堆,这里,数组为乱序,无法直接使用。
但是可以从倒数第一个非叶子节点开始向下调整,从上往下调。
倒数第一个非叶子节点就是最后一个节点的父节点。找到这个节点后,把太和它的孩子看成一个整体,进行向下调整,调整后,再次将父节点向前挪动,再次向下调整,一次循环。

  • 图解:
    在这里插入图片描述
    在这里插入图片描述
  • 代码:
//升序
void HeapSort(int* a, int n)
{
     
     
	//1、向上调整
	int i = 0;
	for (i = 1; i < n; i++) //应该从i=1时遍历,因为第一个数据在堆里不需要调整,后续再插入时调整
	{
     
     
		AdjustUp(a, i);
	}
	//2、向下调整
	for (int i = (n - 1 - 1)/2; i >= 0; i--)
	{
     
     
		AdjustDown(a, n, i);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_45559559/article/details/126603291