堆排序

堆在逻辑结构上是顺序存储的完全二叉树,左孩子2i,右孩子2i+1,付结点是i/2。

大根堆,根>左右。小根堆,左右>根。

算法思想:

建堆:比如构造大根堆,就是将所有的非叶子结点(编号<=n/2)下坠做调整。小元素下坠与更大的结点交换。

排序:  将堆顶元素加入有序序列(堆顶元素与堆底元素互换),堆底元素到堆顶后,需要下坠调整,恢复成大根堆。上述过程重复n-1次。

空间复杂度:O(1)

时间复杂度:O(nlogn)

稳定性:不稳定

基于大根堆是递增序列,基于小根堆是递减序列。


建立堆

//排序
void HeadAdjust(int A[], int k, int len) //k是当前要处理的一个非叶结点
{
	count++;
	int i, j, max;
	A[0] = A[k];//记录被调整的根结点
	for (i = 2 * k; i <= len; i *= 2)//向自己的左右儿子遍历
	{
		if (i < len && A[i] < A[i + 1]) //比较左右儿子谁更大
		{
			i++;
		}
		if (A[0] >= A[i])break;//选出更大的与根节点比较
		else
		{
			A[k] = A[i];//若大于根节点,则将交换儿子与根节点的值
			k = i;//并且将此次的儿子作为根继续寻找下去
		}
	}
	A[k] = A[0];//将最初根节点放入调整后该放入的位置
	printf("第%d次调整后的顺序为:", count);
	for (i = 1; i < 9; i++)
	{
		printf("%d ", A[i]);//从实际数组里第二个元素开始遍历
	}
	printf("\n");
}

//堆排序
void BulidMaxHeap(int A[], int len) 
{
	for (int i = len / 2; i > 0; i--)//从后向前调整堆 
	{
	    HeadAdjust(A, i, len);
	}

}

将表长划分为2,前半部分是非根结点,从后向前将每一个根结点调整好。用A[0]保存当前要调整的根节点,然后比较根结点的两个孩子,选出更大的与跟结点比较,若小于根则直接结束,这也就是为什么从后往前,若从前往后,则根结点的孩子中是否有更大的结点就不知道了。若大于根节点则将孩子的值赋值给根结点,并且将要调整的根换做为这个孩子,然后遍历此孩子的孩子结点。最终确定出来最早的那个根实际要在的地方,将A[0]这个副本赋值过去,完成一次调整。

全部代码

int count=0;
void HeadAdjust(int A[], int k, int len) //k是当前要处理的一个非叶结点
{
	count++;
	int i, j, max;
	A[0] = A[k];//记录被调整的根结点
	for (i = 2 * k; i <= len; i *= 2)//向自己的左右儿子遍历
	{
		if (i < len && A[i] < A[i + 1]) //比较左右儿子谁更大
		{
			i++;
		}
		if (A[0] >= A[i])break;//选出更大的与根节点比较
		else
		{
			A[k] = A[i];//若大于根节点,则将交换儿子与根节点的值
			k = i;//并且将此次的儿子作为根继续寻找下去
		}
	}
	A[k] = A[0];//将最初根节点放入调整后该放入的位置
	printf("第%d次调整后的顺序为:", count);
	for (i = 1; i < 9; i++)
	{
		printf("%d ", A[i]);//从实际数组里第二个元素开始遍历
	}
	printf("\n");
}

//堆排序
void BulidMaxHeap(int A[], int len) 
{
	for (int i = len / 2; i > 0; i--)//从后向前调整堆 
	{
	    HeadAdjust(A, i, len);
	}

}


int main() 
{
	int i, j, A[9] = { 0,53,17,78,9,45,63,87,32 };
	printf("初始数据为:");
	for (i = 1; i < 9; i++) 
	{
		printf("%d ", A[i]);//从实际数组里第二个元素开始遍历
	}
	printf("\n");
	BulidMaxHeap(A, 8);
	printf("最终序列为:");
	for (i = 1; i < 9; i++)
	{
		printf("%d ", A[i]);//从实际数组里第二个元素开始遍历
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值