#### 堆及堆排序 ####

部分摘自【图解排序算法(三)之堆排序 - dreamcatcher-cx - 博客园

目录

堆排序

堆排序思路

建堆步骤

详细

操作

建堆(以大顶堆为例):

大根堆中插入节点时的向上调整:

堆排序:

堆排序

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。

首先简单了解下堆结构:

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子

该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

根结点arr[i]、根结点的左子树arr[2 * i]、右子树arr[2 * i + 1]

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

接下来,我们来看看堆排序的基本思想及基本步骤:

堆排序思路

将要排序的arr建堆,然后不断输出并删除堆顶节点,提取堆顶元素后删除堆顶,将堆的最后一个元素移到堆顶,然后对根结点往下调整,所以又保持了一个arr[len-1]的堆。以此重复。

建堆步骤

1、构造初始堆,将给定无序序列构造成一个大顶堆,假设给定无序序列结构如下。

2、此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

3、找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。

这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

此时,我们就将一个无需序列构造成了一个大顶堆。

详细

操作

  • 建堆:建堆就是一个反复筛选的过程,n个节点的完全二叉树,最后一个节点是第[n/2]个节点的孩子,对第[n/2]个节点为根子树筛选(对于大根堆:若根节点的关键字小于小于左右的子女中关键字的最大者,则交换),使该子树成为堆。对以向前([n/2]-1 至 1)的为根节点的子树重复上述过程进行筛选,过程中节点的交换可能破坏下一级的堆,于是就向下进行调整。所以遍历的顺序:内层往下,外层往上。
  • 堆中插入节点:将插入的新节点放在堆末端,然后对此结点往上调整。向上调整是只对新插入的节点及其直系上级或上上级...节点进行调整,而不涉及其他旁系的上[上]级节点。
  • 堆中删除节点:删除堆顶时,将堆的最后一个元素与堆顶交换,然后对根结点往下调整
  • 堆排序:将要排序的arr[len]建堆,然后不断输出并删除(即上面第3点)堆顶节点,删除堆顶时,将堆的最后一个元素与堆顶交换,然后对根结点往下调整,所以又保持了一个arr[len-1]的堆。

建堆(以大顶堆为例):

向下调整的时间复杂度为O(h),因为大部分节点的高度都较小,所以可以证明建堆为O(n)

public void buildMaxHeap(int arr[], int len) {
	for(int i = len / 2; i > 0; i--)  //最后一个节点是第len/2个节点的孩子
		adjustDown(arr, i, len)
}
public void adjustDown(int arr[], int i, int len) {
	arr[0] = arr[i];  //暂存
	for(int j = i * 2; j <= len; j++) {  //i为根,j为i的各子代
		if(j < len && arr[j] < arr[j + 1])  //比较左右孩子的大小
			j++;
		if(arr[0] > arr[j]) break;  //退出循环
		else {
			arr[i] = arr[j];  //将j上移到双亲节点上
			i = j;  //重新选跟,以这个子代(j)为下次循环的跟
		}
	}
	arr[j] = a[0];
}

大根堆中插入节点时的向上调整:

public void adjustUp(int arr[], int i) {  //i为向上调整的结点
	arr[0] = arr[i];
	int j = i / 2;  //j为双亲,i为向上的调整的起点节点
	while(j > 0 && arr[j] < arr[0]) {
		arr[i] = arr[j];
		i = j;
		j = i / 2;
	}
	arr[i] = arr[0];
}

堆排序:

堆排序,空间复杂度为O(1),时间复杂度最好/最坏/平均为O(nlogn),且不稳定

public void heapSort(int arr[], int len) {
	buildMaxHeap(arr, len);
	for(int i = len; i > 1; i--) {
		print(arr[1]);
		swap(arr[i], arr[1]);  // 交换之后只对n-1个节点进行排序:adjustDown(arr, 1, i - 1);
		adjustDown(arr, 1, i - 1);
	}
	print(arr[1]);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值