算法-详解堆排序算法
来源:互联网 发布:sql 生成临时表 编辑:程序博客网 时间:2024/05/20 16:01
堆排序是利用堆的性质进行的一种选择排序。
时间复杂度:
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)(就地排序,用于堆化(又称筛选)的辅助空间)
性能:
由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的。
堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成。
堆排序是一种不稳定排序。
科普:(排序的稳定性是指如果在排序的序列中,存在前后相同的两个元素的话,排序前和排序后他们的相对位置不发生变化)。即使在最坏情况堆排序的时间复杂仍然为O(nlogn)。而快速排序在最坏情况下会达到O(N^2),所以快排在最好和平均情况下比堆排快,堆排在最坏情况下比快排快。
基本思想:
利用大(小)顶堆堆顶记录的是最大(小)关键字这一特性,使得每次从无序中选择最大(小)记录变得简单。
下面的讨论全部基于大顶堆(小顶堆同理,不再赘述)。
将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆(堆化、筛选),然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1(原堆剩最后一个元素),则整个排序过程完成。
操作过程如下:
初始化堆:将R[1..n]构造为堆;
将当前无序区的堆顶元素R[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆(不断堆化)。
因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。
给定一个整形数组a[]={16,7,3,20,17,8},对其进行堆排序。
首先根据该数组元素构建一个完全二叉树,得到:
然后需要构造初始堆,则从最后一个非叶节点开始调整,调整过程如下:
以下为构造初始堆:
8和3交换,满足;
20和7交换,导致20和16不满足;
20和16交换,导致17和16不满足;
17和16交换;
完成构建(大顶堆)。
即每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)。有了初始堆之后就可以进行排序了。
以下为交换元素并每步堆化(排序):
交换3与20; 如下图:
此时3位于堆顶不满大顶堆的性质,则需继续调整。
交换3与17,导致3与16不满足;
交换3与16;
满足;交换17与3;如下图:
此时3位于堆顶不满大顶堆的性质,则需继续调整。
交换3与16,导致3与7不满足;
交换3与7;
满足;
3. 交换16与3;如下图:
此时3位于堆顶不满大顶堆的性质,则需继续调整。
交换3与8;
满足;
4. 交换3与8;如下图:
此时3位于堆顶不满大顶堆的性质,则需继续调整。
交换3与7;
满足;
5. 交换3与7;如下图:
满足,完成排序。
这样整个区间便已经有序了。
从上述过程可知,堆排序其实也是一种选择排序,是一种树形选择排序。只不过直接选择排序中,为了从R[1…n]中选择最大记录,需比较n-1次,然后从R[1…n-2]中选择最大记录需比较n-2次。事实上这n-2次比较中有很多已经在前面的n-1次比较中已经做过,而树形选择排序恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数。对于n个关键字序列,最坏情况下每个节点需比较logn次,因此其最坏情况下时间复杂度为nlogn。
算法实现(java):
/**定义堆*/ static class Heap{ int count; public int[] Array; public Heap(int count){ this.count=count; this.Array=new int[count]; } /**获取左孩子的方法*/ public int getLeft(int i){ int left=2*i+1; if(this.Array==null || left>=this.count){return -1;} return left; } /**获取右孩子的方法*/ public int getRight(int i){ int right=2*i+2; if(this.Array==null || right>=this.count){return -1;} return right; } /**堆化(筛选)*/ public void PercolateDown(int i){ int left,right,max = 0,temp; left=this.getLeft(i); right=this.getRight(i); if(left!=-1 && this.Array[i]>=this.Array[left]){ max=i; }else{ max=left; } if(right!=-1 && this.Array[right]>this.Array[max]){ max=right; } //交换,交换完之后结束方法,由外部调用 if(max!=-1 && max!=i){ temp=Array[max]; Array[max]=Array[i]; Array[i]=temp; PercolateDown(max); } } } public static Heap Heapsort(int[] A,int n){ Heap heap=new Heap(n); int temp; for(int i=0;i<n;i++){ heap.Array[i]=A[i]; } //从第一个非叶子节点开始堆化(筛选) for(int i=(n-1)/2;i>=0;i--){ heap.PercolateDown(i); } //第一次堆化完成, 开始交换,最后一个数和第一个数交换一次 for(int i=n-1;i>=0;i--){ //heap.Array[0] 存储最大元素 temp=heap.Array[0]; heap.Array[0]=heap.Array[i]; heap.Array[i]=temp; //使用count的目的是在getleft的时候避免再去判断已经交换过(排序过)的元素 heap.count--; //对第一个数不满足则再次进行堆化 heap.PercolateDown(0); } return heap; }
测试程序:
public static void main(String[] arg){ int[] A=new int[]{5,14,78,2,14,53,45,1}; long time1=System.nanoTime(); Heap heap=Heapsort(A, A.length); System.out.println(System.nanoTime()-time1); for (int j : heap.Array) { System.out.println(j); } }
打印内容如下:
709361251414455378
csdn地址:http://blog.csdn.net/u012534831
github地址:https://github.com/qht1003077897
个人博客地址:https://qht1003077897.github.io
QQ:1003077897
- 堆排序算法详解
- 堆排序算法详解
- 堆排序算法详解
- 算法-详解堆排序算法
- 堆排序算法思路详解
- 堆排序算法(排序详解)
- js排序算法详解-堆排序
- 堆排序详解与算法实现
- Heapsort 堆排序算法详解(Java实现)
- Heapsort 堆排序算法详解(Java实现)
- 排序算法--堆排序
- 排序算法-堆排序
- 排序算法---堆排序
- 【排序算法】堆排序
- 排序算法-堆排序
- 排序算法---堆排序
- 排序算法--堆排序
- 排序算法----堆排序
- Java的Collection.toArray方法
- 解决使用easyui的datagrid组件页面加载后发送两次请求的问题
- 常量与转义符和运算符
- ajax中$.post请求出现栈溢出Uncaught RangeError: Maximum call stack size exceeded错误
- 《算法概论》8.10
- 算法-详解堆排序算法
- webstorm meteor开发 安装及创建项目
- linux:生产者消费者模型
- poj 1743 最长不重叠子串 转调差值相等所以相减
- 4045 Machine scheduling(The 36th ACM/ICPC Asia Regional Beijing Site —— Online Contest)
- Win10中在哪里设置专用网络和公用网络 [截图]
- php系统内置的 观察者模式接口
- 关于 开启adblock 访问https://www.meteor.com/tutorials/blaze/creating-an-app
- 量化初学者的随想1