算法之堆排序

堆排序

堆:如果一棵完全二叉树的每个节点都大于(小于)它的子节点称之为堆。

k >= 2k + 1 && k >= 2k + 2

或者 k <= 2k + 1 && k <= 2k + 2

堆分:大顶堆和小顶堆。

完全二叉树:除了叶子节点所有的其他节点都有完整的左子树和右子数,除了最后一层的非叶子节点以外。

使用堆排序分两步:

1 建立一个无序的堆。

2 输出对顶元素,然后用最后一个元素代替堆顶元素,是之再成为一个堆。

比如: 3,22,8,11,55,1这样一个数使用堆排序的过程如下:

1. 先按顺序建立一颗完全二叉树:

按层级顺序填充数据,从最后一个非叶子节点看,是否满足堆的要求。

先从8开始,不满足堆的要求,和1换位。(该节点都必须大于或者小于左右子节点),我们按小顶堆排序,所以节点都应小于左右子节点。

再从22开始,不满足要求,和11换位。(若该节点小于左右时,和较小的子节点互换)

再从3开始,不满足要求,和1互换位置。

这样就建成了堆。所有的节点都不大于它的子节点。

2 第二阶段。

从初始的堆中输出堆顶元素,然后将最后一个元素放到堆顶,然后在进行堆的定义置换。

我们把较大元素从顶上落到底层的过程叫做------- 筛选。

先输出1,然后将8置于堆顶

然后按照步骤1的方式将其筛选成符合条件的堆,知道8落地为止,即筛选8。

先和3替换,8落地。符合堆

输出3,将55放置堆顶

然后筛选55得到如下

输出8,,22置堆顶

筛选22,将11置堆顶,

输出11,输出22,输出55

排序后应该是1,3, 8,11, 22, 55

我们可以用一维数组表示这个完全二叉树。

假如当前节点为K:

那么父节点为:  (k - 1) / 2

左孩子为: 2k  + 1

有孩子为:2k + 2

其Java代码实现如下:

package andy.test;

/**
 * @author Zhang,Tianyou
 * @version 2014年11月7日 下午10:32:31
 */

public class HeapSortTest {

	public static void main(String[] args) {
		int[] a = {3,22,8,11,55,1};
		heapSort(a);

	}

	private static void heapOne(int[] a, int n, int k) {
		//节点k进行筛选
		//a: 堆数据  n:堆中有效数据个数   k:筛选节点

		int k1 = 2*k + 1;//左子树序号
		int k2 = 2*k + 2;//右子数序号
		if(k1 >= n && k2 >= n)return ; //已经是叶子节点

		int a1 = Integer.MAX_VALUE;
		int a2 = Integer.MAX_VALUE;
		if(k1 < n)a1 = a[k1]; //左孩子值
		if(k2 < n)a2 = a[k2]; //右孩子值

		if(a[k] <= a1 && a[k] <= a2)return; //已符合堆的要求

		//找到左右孩子中最小的,和它交换
		if(a1 < a2){
			int temp = a[k];
			a[k] = a[k1];
			a[k1] = temp;
			heapOne(a, n, k1); //继续筛选子树
		}else{
			int temp = a[k];
			a[k] = a[k2];
			a[k2] = temp;
			heapOne(a, n, k2); //继续筛选子树
		}

	}

	static void heapSort(int[] a){
		//建立初始堆
		//a.length     最后一个叶子节点
		//a.length/ 2      最后一个非叶子节点
		for(int i = a.length / 2; i >= 0; i--)heapOne(a, a.length, i);

		//边输出堆顶元素,边调整
		int n = a.length;//剩余元素
		while(n > 0){
			System.out.print(a[0] + "  ");//输出堆顶元素
			a[0] = a[n - 1]; //最后一个元素置顶
			n--;
			heapOne(a, n, 0);//筛选第一个元素

		}
		System.out.println();
	}

}

输出结果为:

1  3  8  11  22  55

时间: 2024-10-11 13:10:35

算法之堆排序的相关文章

排序算法系列——堆排序

记录学习点滴,菜鸟成长记 堆排序引入了另一种算法设计技巧:使用一种我们称之为“堆”的数据结构来进行数据管理. 堆排序算是真正意义上的利用数据结构来求解数组排序的方法. “插入排序”和“归并排序”可以看做是一种“计算机体力活”,体现的思想更多的是去模拟最简单的人类思维,比如插入排序过程中的比较,归并中子问题合并时的比较. “堆排序”可以看做是“计算机脑力活”,他利用了一种结构化的语言来表达,这种结构化带来一些性质,比如左右孩子.比[堆大小的一半向下取整]大的下标都是叶节点不需要维护其最大堆性质等.

【数据结构与算法】堆排序

堆排序的时间复杂度是O(nlogn),下面上代码 public class HeapSort { public void adjustHeap(int[] in, int index, int length) { int leftcIndex = index * 2 + 1; int rightcIndex = index * 2 + 2; int bigest = index; while (leftcIndex < length || rightcIndex < length) { if

常用算法之----堆排序

预备知识 堆排序 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序.首先简单了解下堆结构. 堆 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆:或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆.如下图: 同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子 该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是: 大顶

排序算法之堆排序(优先队列)

1.堆排序的堆,其实是一个 完全二叉树.既是一个结点要么是叶子结点,要么必定有左右两个子节点的树. 2.堆有序:每个结点的值,都必须大于两个子节点.但是两个子结点的大小不作要求. 3.一棵大小为N的完全二叉树,高度为lgN(层). 用数组实现堆,假设数组下标从0开始,下标为k的元素,它的左子树是2k+1,右子树是左子树+1,即2k+2 一:由上至下的有序化(下沉) 如果堆的有序状态,因为某个结点比它的两个子结点或者其中之一小而打破了,那么可以通过与两个子结点中的较大者来交换. 交换后可能会在子结

【算法设计-堆排序】大根堆排序

1.堆排序不仅拥有与归并排序一样的时间复杂度为o(nlgn),而且空间复杂度所占秩序额外的几个元素空间,这个又有插入排序的优势. 2.堆排序分为3部分,第一部分是保持堆的性质的函数MAX_HEAPIFY函数,用于保持堆的性质:父结点的值要大于子节点. 第二部分是创建大根堆的函数,BUILD_MAX_HEAP这个函数从第一个非叶节点开始进行MAX_HEAPIFY函数.最终创建一个大根堆. 第三部分是堆排序部分,输出一个最大值KEY[1],然后将最后一个值替换第一个值,重复进行. 代码如下: #in

必须知道的八大种排序算法【java实现】(三) 归并排序算法、堆排序算法详解

一.归并排序算法 基本思想: 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的.然后再把有序子序列合并为整体有序序列. 归并排序示例: 合并方法: 设r[i-n]由两个有序子表r[i-m]和r[m+1-n]组成,两个子表长度分别为n-i +1.n-m. j=m+1:k=i:i=i; //置两个子表的起始下标及辅助数组的起始下标 若i>m 或j>n,转⑷ //其中一个子表已合并完,比较选取结束 //选取r[i]和r[j]

【转】算法之堆排序

http://blog.csdn.net/SJF0115/article/details/8610201 前序: (二叉)堆数据结构是一种数组对象,它可以被视为一棵完全二叉树.树中每个节点与数组中存放该节点值的那个元素对应. 树的每一层都是填满的,最后一层除外. 树的根为a[1] (在这里是从1开始的,也可以从0开始),给定了某个节点的下标i,其父节点为i/2,左二子为2*i,右儿子为2*i+1. 二叉堆满足二个特性: 1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值. 2.每个

排序算法之堆排序(Heapsort)解析

一.堆排序的优缺点(pros and cons) (还是简单的说说这个,毕竟没有必要浪费时间去理解一个糟糕的的算法) 优点: 堆排序的效率与快排.归并相同,都达到了基于比较的排序算法效率的峰值(时间复杂度为O(nlogn)) 除了高效之外,最大的亮点就是只需要O(1)的辅助空间了,既最高效率又最节省空间,只此一家了 堆排序效率相对稳定,不像快排在最坏情况下时间复杂度会变成O(n^2)),所以无论待排序序列是否有序,堆排序的效率都是O(nlogn)不变(注意这里的稳定特指平均时间复杂度=最坏时间复

排序算法之堆排序

1. 堆排序的思想 输入一个数组,利用一组二叉树的操作使其变成有序的数组,就是堆排序 堆排序利用的是二叉树的思想,操作对象是数组,所以数组需要在逻辑上映射到二叉树上,由于数组的下标是连续的,而二叉树中只有完全二叉树和满二叉树是连续的,所以将数组元素逐个映射到完全二叉树上,然后配备一系列的操作即可.例如数组data[]={9,6,5,4,3,2,1,7},映射到完全二叉树上如下图所示. 2.堆排序的过程 还是用上面的data数组作为输入数组,映射到完全二叉树如上图所示,怎么利用二叉树的性质,才能使

算法学习之排序算法:堆排序

要了解堆排序,首先要了解堆的概念,因为本文主要研究堆排序的算法,此处对数据结构堆只是给出概念:n个元素的序列{k1,k2,...kn},当且仅当满足如下关系时,称之为堆. k[i] <= k[2i]且k[i] <= k[2i+1] (或 k[i] >= k[2i]且k[i] >= k[2i+1]) 比如:序列96.83.27.38.11.09(或12.36.24.85.47.30.53.91)都是堆. 如果将堆对应的一维数组看成是一个二叉树,则堆的含义表明:完全二叉树中所有非终端结