优先队列之堆排序(一)
2017-11-24 15:08
369 查看
前言
为什么要是用优先队?优先队列又是什么?
许多应用程序都系要处理有序的元素,但不应定要求它们完全有序,或者不一定要一次就将它们排序。很多时候我们回收集一些元素,然后处处理当前键值最大的元素,然后再收集更多的元素,再狐狸当前键值最大的元素……举个生活中的例子吧,就像我们用的手机一样,手机里面有很多进程,但是有一个进程的优先级特别高,那就是来电显示(不然的话,放你在打游戏的时候也不会因为来电而退出,嘿嘿)。优先队列是局部有序的,它并不将所有的元素都排成成有序的。基本函数
Key delmax()用来删除队列中最大的元素void insert(Key value)用来插入新元素
boolean isEmpty() 用来判断队列是否为空
int getSize() 返回 队列中的元素个数
Key getMax() 返回队列中的最大值元素
我们用二叉堆表示法来实现优先队列,首先来介绍一下什么是二叉堆
在一个二叉树里面,如果每个结点都大于等于它的两个子结点的时候,那么那它就是堆有序的;二叉堆是一组能够用堆有序的完全二叉树(若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。)排序的元素,并在数组中按照层级存储(为了表示方便,我们不使用数组中的第一个元素)。所以根节点的地方是队列中键值最大的元素。函数具体实现(采用java语言)
堆实现的比较和交换算法private boolean less(int i,int j) { return pq[i] <pq[j]; } private void exch(int i,int j) { int temp = pq[i]; pq[i]=pq[j]; pq[j]=temp; }
由下至上的堆有序化—Swim(上浮)
private void swim(int k) { // 上浮 元素最大比较次数为logN+1次 (也就是二叉树为满二叉树的时候,再往里面插入元素的时候) while(k>1&&less(k/2,k)) { exch(k/2,k); k=k/2; } }
由上至下的堆有序化—Sink(下沉)
private void sink(int k) { //下沉 一共循环logN次 元素比较次数为2*logN次 while(2*k<=N) { int j=2*k; if(j<N && less(j,j+1)) { j++; } if(!less(k,j)) { break; } exch(k,j); k=j; } }
插入函数和删除最大元素最大函数
public void insert(int elem) { pq[++N]=elem; swim(N); } public int delMax() { int max=pq[1]; exch(1, N--);//交换根节点和最后一个节点,并删除交换后的最后一 个节点 也就是之前的根节点 //pq[N+1] = (Integer) null ;//防止对象游离 基本类型元素不可用 sink(1);;//恢复堆得有序性 return max; }
基于堆的优先队全部代码
package 优先队列1;
public class PriorityQueue {//堆顶元素最大
private int[] pq;
private int N = 0; //存储于pq[1...N]中,pq[0]没有使用
private boolean less(int i,int j) { return pq[i] <pq[j]; } private void exch(int i,int j) { int temp = pq[i]; pq[i]=pq[j]; pq[j]=temp; }
public PriorityQueue(int length) {
this.pq=new int[length+1]; //下标为0的元素不用 元素从下标为1的地方开始
}
private void swim(int k) {
// 上浮 元素最大比较次数为logN+1次 (也就是二叉树为满二叉树的时候,再往里面插入元素的时候)
while(k>1&&less(k/2,k)) {
exch(k/2,k);
k=k/2;
}
}
private void sink(int k) {
//下沉 一共循环logN次 元素比较次数为2*logN次
while(2*k<=N) {
int j=2*k;
if(j<N && less(j,j+1)) {
j++;
}
if(!less(k,j)) {
break;
}
exch(k,j);
k=j;
}
}
public int getElemSize()
{
return N;
}
public boolean isEmpty() {
return N == 0;
}
public void insert(int elem) {
pq[++N]=elem;
swim(N);
}
public int delMax() {
int max=pq[1];
exch(1, N--);//交换根节点和最后一个节点,并删除交换后的最后一个节点 也就是之前的根节点
//pq[N+1] = (Integer) null ;//防止对象游离 基本类型元素不可用
sink(1);;//恢复堆得有序性
return max;
}
private void display() {
for(int i=1;i<=N;++i)
System.out.print(pq[i] + " ");
System.out.println();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
PriorityQueue PQ = new PriorityQueue(12);
PQ.insert(12);
PQ.insert(15);
PQ.insert(2);
PQ.insert(18);
PQ.insert(7);
PQ.insert(7);
System.out.println("The count of elem is "+PQ.getElemSize());
System.out.print("All elem are :");
PQ.display();
System.out.println("Now,the max elem in priorityQueue is "+PQ.delMax());
System.out.println("Now,the max elem in priorityQueue is "+PQ.delMax());
System.out.println("Now,the max elem in priorityQueue is "+PQ.delMax());
}
}
运行结果如下:
算法分析
sink()方法中,一共循环logN次 元素最大比较次数为2*logN次swim()方法中, 元素最大比较次数为logN+1次,也就是二叉树为满二叉树的时候,再往里面插入元素的时候。
算法的分析 用图表表示为:
其中,
第一行unordered array是没有排序的数组,插入和删除还有求最大元素的算法复杂度分别为1,n,n。
第二行ordered array是排好序的数组,插入和删除还有求最大元素的算法复杂度分别为n,1,1。
第三行binary heap是二叉堆,插入和删除还有求最大元素的算法复杂度分别为log n,2*log n,1。
第四行d-ary heap是d叉堆(也就是每个结点有d个子结点),插入和删除还有求最大元素的算法复杂度分别为logd n,d*logd n,1
sweet spot: d = 4 意思就是说最好的情况是当d=4的时候,算法效率比较好。
改进版的堆排序请参考
算法–优先队列之堆排序((二)升级版)
相关文章推荐
- 二叉堆,堆排序,STL优先队列的底层实现,剑指offer数据流中的中位数
- 排序算法3-堆排序与优先队列
- 排序算法08:优先队列与堆排序
- 堆 优先队列 堆排序
- 堆、堆排序和优先队列的那些事
- 优先队列和堆排序
- 算法 2.4节 堆排序及其改进、索引优先队列
- 数据结构与算法--优先队列和堆排序
- 算法设计之,堆,堆排序,基于最大堆的最大优先队列的实现(C++实现)
- 数据结构-堆排序和优先队列
- 九、堆和优先队列---(4)堆排序
- 浅析数据结构与算法5--优先队列与堆排序
- 基于二叉堆实现的优先队列和堆排序
- 优先队列-堆排序
- 堆排序实现优先队列(Priority queue)
- 优先队列 之 堆排序实现(堆排序思想)
- 堆排序和优先队列【最大堆】
- 最小堆得实现;优先队列的堆实现;堆排序的时间复杂度nlgn;
- 堆排序实现优先队列
- POJ 2051 Argus(堆排序 or STL优先队列)