排序之堆排序






下面我们来看一个例子:



对上述解释:我们先将给出的序列从上到下从左到右排成一颗二叉树,然后将该树调整成一颗大根堆,调整成大根堆的过程是从叶子节点开始的,就拿图 a 来说,5 是 0 的左孩子,但是孩子比父亲节点的元素大,所以我们要进行交换,当交换完之后,我们可以得到 8 的孩子节点变成 9 和 5 了,然后 9 比 8 大,进行交换,然后 6 的孩子节点变成了 9 和 7,9 比 6 大,进行交换,交换之后对左子树造成了影响,因为 6 的孩子变成了 8 和 5, 8 比 6 大,然后进行交换,于是成了图 b 。


从调整的过程来看,我们要明白首先是从叶子节点先上调整,但是在叶子节点元素改变的过程中,也要注意对叶子节点的节点是否有影响,如果有影响,要及时做调整。

堆的调整宝宝们应该会了吧,那么排序的过程就是不断的让调整好的大根堆的根节点输出,然后将最后一个节点放到根节点的位置,再次进行调整,调整成大根堆之后,依然输出根节点,将最后一个节点放到根节点的位置,再次调整成大根堆。。。。。直到就剩一个节点。。。




相信各位经过上面的例子,都明白了堆排序的过程与原理,大根堆输出的是降序,那么对应的,小根堆输出的就是升序,小根堆的原理和大根堆一样,这里就不做重复讲解了,我们来看看部分代码实现部分吧(省去了头文件以及一些定义)!

typedef struct    //记录类型
{
	KeyType key;  //关键字项
	InfoType data; //其他数据项,类型为InfoType
} RecType;/排序的记录类型定义

void sift(RecType R[],int low,int high)  //调整堆得过程、筛选
{
    	int i=low,j=2*i; //R[j]是R[i]的左孩子
    	RecType temp=R[i];
    	while (j<=high)
		{
		if (j<high && R[j].key<R[j+1].key) //若右孩子较大,把j指向右孩子
				j++;   //变为2i+1
			if (temp.key<R[j].key)
			{
				R[i]=R[j];  //将R[j]调整到双亲结点位置上
				i=j;   //修改i和j值,以便继续向下筛选
				j=2*i;
			}
			else break;  //筛选结束
    	          }
    	R[i]=temp;  //被筛选结点的值放入最终位置
}

void HeapSort(RecType R[], int n)
{
    int i;
    RecType tmp;
    for(int i=n/2;i>=1;i--) //循环建立初始堆
        sift(R,i,n);
    for(int i=n;i>=2;i--)//进行n-1趟堆排序,每趟堆排序的元素个数减1
    {
        tmp=R[1];  //将最后一个元素同当前区间内R[1]对换
        R[1]=R[i];
        R[i]=tmp;
        sift(R,1,i-1);  //筛选R[1]节点,得到i-1个节点的堆
    }
}

int main()
{
	int i,n=10;
	RecType R[MaxSize];
	KeyType a[]={3,2,9,5,4,7,6,1,0,8};
	for (i=0;i<n;i++)
		R[i].key=a[i];
	printf("排序前:");
	for (i=0;i<n;i++)
		printf("%d ",R[i].key);
	printf("\n");
	HeapSort(R,n);
	printf("排序后:");
	for (i=0;i<n;i++)
		printf("%d ",R[i].key);
	printf("\n");
}

好了!就讲到这里了,有哪里不明白的小伙伴可以留言哦!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值