堆排

堆排步骤:

1、放数据

2、建堆(大根堆或者小根堆)

第一次创建大根堆从中间节点开始,依次到第一个节点,以后都从第一个节点开始,因为其他的之前排好序了。

注:

大根堆:所有的父节点都大于它自己本身的子节点。

小根堆:所有的父节点都小于它自己本身的子节点。

为了使数组下标和堆下标对应,数组a[0]不存数据,堆的根节点下标为1,子节点下标依次递增。

3、排序

复杂度和稳定性情况:

  • 最好的时间复杂度:O(nlog n)
  • 最坏的时间复杂度:O(nlog n)
  • 平均的时间复杂度:O(nlog n )
  • 空间复杂度:O(1)
  • 稳定性:不稳定

例:(图片转自:https://blog.csdn.net/fenger3790/article/details/82837891

在这里插入图片描述

大根堆: 

 在这里插入图片描述

 这就是从初始数据建立大根堆的过程。可以看到经过一次建堆之后,数组中最大的数来到了堆顶,这时我们把堆顶元素和末尾元素交换,然后把末尾元素输出。(可以理解成跟原始数据斩断关系)

在这里插入图片描述

把堆顶元素交换然后输出之后,我们发现堆又不平衡了,不是大根堆了,但是因为我们只交换了堆顶,只有堆顶是不平衡的,所以我们只需要调整堆顶就能再得到平衡的大根堆。

在这里插入图片描述

调整完之后输出了当前的最大数8,我们发现堆顶又不平衡了,所以我们再调整堆顶,然后再输出最大数,堆排序的步骤就是重复调整堆、输出到末尾这两个过程。下面是排序的全过程。

在这里插入图片描述

 

C实现代码如下:

#include<iostream>
using namespace std;

//创建大根堆函数
//a为数组首地址,root为根节点下标,len为数组长度
void CreateHeap(int *a, int root, int len)
{
	int i = 2*root; //i为一个标记,记录子节点中较大值的下标
	int t = a[root]; //t为根节点的值
	while(i <= len) //当前节点后至少有两个子节点
	{
		if(i < len) //说明有两个子节点
		{
			if(a[i] < a[i+1])
			{
				++i; //若右孩子值大于左孩子的值,则标记后移,指向大的
			}
		}

		if(t >= a[i]) //若根节点值大于子节点中较大的值,则其已经是大根堆了,结束循环
			break;
		else		  //若根节点值小于子节点中较大的值,则将子节点值较大的给根节点
		{
			a[i/2] = a[i]; //把子节点较大值给父节点
			i = 2*i; //用于下一次循环判断子节点作为父节点时是否有两个子节点
		}
	}
	a[i/2] = t; //将根节点值给循环前的值较小的子节点
}

//堆排序函数
void Sort(int *a, int len)
{
	int i;
	int t;

	//第一次创建大根堆,从中间节点开始创建

	for(i = len/2; i >= 1; --i)
	{
		CreateHeap(a,i,len);
	}

	for(i = len; i >= 1; --i)
	{
		//交换根节点值和最大的叶节点值
		t = a[1];
		a[1] = a[i];
		a[i] = t;
		CreateHeap(a,1,i-1); //每次交换值后都重新建立大根堆
	}
}

//打印函数
void Print(int *a, int len)
{
	for(int i = 1; i < len; ++i)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
}

//测试
int main()
{
	int a[] = {3,7,5,1,2,6,4,8,9,0};
	int len = sizeof(a)/sizeof(a[0]);
	Sort(a,len);
	Print(a,len);
        return 0;
}

其他排序算法: 

  1. 直接插入排序
  2. 希尔排序(优化版)
  3. 直接选择排序
  4. 快速排序(递归版和非递归版)
  5. 冒泡排序及其优化
  6. 归并排序
  7. 基数排序
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值