【C语言算法】堆排序

思想

堆排序把线性序列看作一个完全二叉树,通过构造大顶堆来实现排序,步骤如下:
1.从完全二叉树的最后一个子树开始从右到左,从下到上执行数据下沉算法,直到根节点为止,构造一个大顶堆;
2.交换线性序列的首位和末尾,待排序列长度-1,根节点执行数据下沉算法;
3.重复第2步,直到待排序列长度为0,此时序列为非递减排列.

核心算法

下沉算法为此排序思想的核心算法:
设下沉算法的指定节点为F,从节点F开始,查询节点F的左右子节点,取子节点中较大值为C,若节点C的值比节点F的值大则交换,然后更新F为C,对新的F重复执行上述步骤,直到F的子节点都不大于F或F没有子节点为止.

定义宏,在线性序列中求对应父子节点的序号:

// 计算对应节点序号
#define GET_FATHER_INDEX(index) (((index)-1)/2) 
#define GET_LEFT_INDEX(index)	((((index)+1)*2)-1)
#define GET_RIGHT_INDEX(index)	(((index)+1)*2)

数据下沉算法:

// 数据下沉
// 输入:待排序列,要下沉的父节点序号,待排序列长度
void heap_sink(int array[], int start,int size)
{
	int left, right, temp, index;
	for (int i = start; i < size;)
	{
		left = GET_LEFT_INDEX(i);
		right = GET_RIGHT_INDEX(i);
		if (right < size)
		{
			if (array[left] > array[right])
			{
				temp = array[left];
				index = left;
			}
			else
			{
				temp = array[right];
				index = right;
			}
		}
		else if (left < size)
		{
			temp = array[left];
			index = left;
		}
		else
		{
			// 序号超出堆范围
			break;
		}
		// 子节点比父节点小
		if (temp <= array[i])
			break;
		array[index] = array[i];
		array[i] = temp;
		i = index;
	}
}

打印堆

以堆的形式打印,便于查看算法是否正确:

// 堆打印
void heap_print(int array[], int size)
{
	int step = 1, space;
	while (step < size) step *= 2;
	for (int i = 1; i < step; i *= 2)
	{
		space = (step / 2/i)*6/2-3;
		for (int j = 0; j < i; j++)
		{

			if (i + j -1< size)
			{
				for (int k = 0; k < space; k++) printf(" ");
				printf("|%04d|", array[i + j-1]);
				for (int k = 0; k < space; k++) printf(" ");
			}
		}
		printf("\r\n");
	}
}

调用

// 把序列初始化为大顶堆
void heap_init(int array[], int size)
{
	// 最后一层没有子节点,所以从倒数第二层开始
	// 当然,从最后一层开始也不影响结果
	for (int i = size/2; i > 0; i--)
	{
		heap_sink(array, i - 1, size);
	}
}

// 堆排序
void heap_sort(int array[], int size)
{
	int temp;
	heap_init(array, size);
	heap_print(array, size);
	for (int i = size; i > 0; i--)
	{
		temp = array[0];
		array[0] = array[i-1];
		array[i - 1] = temp;
		heap_sink(array,0, i - 1);
	}
}

在初始化为大顶堆之后打印了排序后的数据,可以看到已经是一个大顶堆,整体排序也没有问题.
堆排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值