优先队列,堆与堆排序

1 优先队列

有时我们在处理有序元素时,并不一定要求他们全部有序. 很多情况下我们会收集一些元素, 处理当前最大的元素, 然后再收集更多元素, 再处理当前最大元素 …

这种情况下, 一个合适的数据结构一个支持两种操作 : 删除最大元素插入元素. 这种数据类型叫做 优先队列.

我们可以使用有序或无序的数组或链表实现.使用无序序列是解决这个问题的 惰性方法, 我们仅在必要的时候才会找出最大元素(比如在pop的时候).

而使用有序序列是解决问题的积极方法, 在插入insert元素时就保持列表有序.

对于上述的对优先队列的所有实现中 , 插入元素和删除最大元素这两个操作之一在最坏情况下需要线性时间完成:

  • 有序数组插入元素是O(n)的, 删除是O(1)
  • 无序数组插入元素是O(1)的, 删除是O(n)

而二叉堆能保证这两种操作能够更快地运行.

2 堆的定义与算法

2.1定义

二叉堆能够很好地实现优先队列的基本操作. 在二叉堆数组中,每个元素都要大于等于另外两个特定位置的元素.(默认大顶堆)

  • 定义堆有序: 当一个二叉树的每个结点都大于等于它的两个子结点时, 被称为堆有序.

所以 不难得出根结点是堆有序的二叉树中的最大结点

2.2 二叉堆表示法

  • 定义二叉堆是一组能够用堆有序的完全二叉树排序的元素, 并在数组中按层级储存(第一个位置不用)

以下二叉堆简称 . 在一个堆中 , 位置k的 结点的父节点位置为 k/2 (整型类型, 小数不保留, 比如 5 的父节点在 2), 而它的两个子节点的位置分别在 2k 和 2k+1.

由此我们可以通过计算数组的索引在树中上下移动: a[k]向上一层就 k = k /2 ; 向左子树就 k = 2k 或 2k +1

2.3堆的一些算法

我们用长度为N + 1 的数组来保存一个大小为N的堆, 我们不使用a[0] , 元素全放在a[1] ~ a[N]中

  1. 辅助函数less() 和 exch()
/* a 为实现了Comparable的类型的堆 , i和j为数组下标 */
/* 交换下标为 i , j的两个元素 */
void exch(Comparable[] a , int i , int j ){Comparable temp = a[i]; a[i] = a[j]; a[j] = temp;
}
/*判断下标为i的元素是否小于下标为j的函数*/
boolean less(Comparable[] a , int i , int j){return (a[i].compareTo(a[j]) < 0) ;
}
  1. 由下至上的堆有序swim( )

    若某结点比他的父节点还要大, 则他需要通过交换他和他的父节点来修复堆. 但交换后的父节点可能比父节点的父节点还大 , 所以还得一遍遍地向上恢复.

        private void swin (Comparable[] a , int k , int N){ //N 初始表示最后一个元素下标 N = a.length - 1 , 也是堆中实际元素个数while (k > 1 && less(a , k /2 , k)){exch(a , k/2 , k);k /= 2;}}
    

  1. 由下至上的堆有序化sink( )

    如果某结点比他的两个子节点或者其中之一还要小 , 则需要通过将他和他的子节点中的较大值进行交换. 但交换后依然可能比子节点的子节点还小, 所以也得一遍遍地向下恢复

        private void sink(Comparable[] a , int k , int N){     /* 向下遍历, 但是只在前一半元素中考虑 */while ( 2 * k <= N){int j = 2 * k ;/* 如果他的右孩子比他大 , 而且也比左孩子大 , 则选择与右孩子进行交换 */if(  j < N  && less(a , j, j + 1)) j ++;/* 如果此时k已经不小于他的孩子了 , 跳出循环 */if( ! less(a ,k  , j )) break;/* 进行交换 */exch(a , k , j);k = j;}}
    

  1. 向堆中插入一个元素v , 并且调整堆保持堆有序

    public void intsert(Comparable[] a , Comparable v , int N){a[++N] = v;swim(N);
    }
    
  2. 删除堆中的最大元素,调整堆保持其有序

    public Comparable delMax(Comparable[] a , int N ){/* 得到最大元素 */Comparable max = a[1];/* 和最后一个元素进行交换, 交换完后将N减1 */exch(a , 1 , N -- );a[N + 1] = null;sink(a , 1 , a.length)
    }
    

2.4 堆排序

在上述构造了堆的数据结构后 , 堆排序就比较简单了.

堆排序主要是两个步骤:

  • 将一个无序的数组a (还是默认a[0]不存值 )变得堆有序(成为一个堆) – 构造堆

  • 将该最大元素与最后一个元素交换(交换后堆顶为最小元素) , 同时用sink将最小元素下沉, 用while对每个元素进行该步骤 – 交换+下沉排序

    public void heapSort(Comparable[] a){int N = a.length - 1;/* 对前一半的元素进行sink下沉 , 得到一个堆 */for (int k = N / 2 ; K >= 1 ; k --)sink(a , k , N );/* 对每个元素进行操作 */while( N > 1){/* 交换最大的元素与最后一个元素 , 此时最大元素跑到a[N] , 然后N-- , 表示当前a[N]排序完成, */exch(a , 1 , N --);/* 上面的N--相当于从堆中拿出了排好的元素  , 现在把最小的元素沉下去, 当前堆顶又是当前最大值了 */sink(a , 1 , N );}
    }
    


附堆排序实现:

   public  void heapSort(Comparable[] a){int N  = a .length -1 ;for(int k =  N /2 ; k >= 1 ; k --)sink(a , k , N);while ( N > 1){exch(a , 1  ,N -- );sink(a , 1  ,N  );}}void exch(Comparable[] a , int i , int j ){Comparable temp = a[i]; a[i] = a[j]; a[j] = temp;}boolean less(Comparable[] a , int i , int j){return (a[i].compareTo(a[j]) < 0) ;}private void sink(Comparable[] a , int k , int N){while ( 2 * k <= N){int j = 2 * k ;if(  j < N  && less(a , j, j + 1)) j ++;if( ! less(a ,k  , j )) break;exch(a , k , j);k = j;}}private void swin (Comparable[] a , int k , int N){while (k > 1 && less(a , k /2 , k)){exch(a , k/2 , k);k /= 2;}}@Testpublic void test(){Integer[] a = {null , 33 ,5,78,23,4 , 354 ,78,114, 6};for (int i  = 1; i < a .length ; i ++ ) {System.out.print( a[i] +  " ");}System.out.println();heapSort(a);for (int i  = 1; i < a .length ; i ++ ) {System.out.print( a[i] +  " ");}}

[算法系列]优先队列,堆与堆排序相关推荐

  1. 白话经典算法系列之七 堆与堆排序

     堆排序与高速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法.学习堆排序前,先解说下什么是数据结构中的二叉堆. 二叉堆的定义 二叉堆是全然二叉树或者是近似全然二叉树. 二叉堆满 ...

  2. 精通八大排序算法系列:二、堆排序算法

    精通八大排序算法系列:二.堆排序算法 作者:July .二零一一年二月二十日 本文参考:Introduction To Algorithms,second edition. ------------- ...

  3. python堆排序算法_Python算法学习之堆和堆排序

    什么是堆? 堆是一种完全二叉树(请你回顾下上一章的概念),有最大堆和最小堆两种.最大堆: 对于每个非叶子节点 V,V 的值都比它的两个孩子大,称为 最大堆特性(heap order property) ...

  4. python range倒序_Python算法学习之堆和堆排序

    什么是堆? 堆是一种完全二叉树(请你回顾下上一章的概念),有最大堆和最小堆两种. 最大堆: 对于每个非叶子节点 V,V 的值都比它的两个孩子大,称为 最大堆特性(heap order property ...

  5. 算法系列(十)堆实现优先队列

    在算法系列(九)平衡二叉查找树AVL树中介绍了AVL树.这篇文章主要讲解优先队列. 一般都使用二叉堆来实现优先队列.暂时之说这种实现. 堆的定义和性质 堆实际上是一棵完全二叉树,其任何一非叶节点满足性 ...

  6. 排序算法之——优先队列经典实现(基于二叉堆)

    许多应用都需要处理有序的元素,但有时,我们不要求所有元素都有序,或是一定要一次就将它们排序,许多情况下,我们会收集这些元素里的最大值或最小值. 这种情况下一个合适的数据结构应该支持两种操作:插入元素. ...

  7. 算法学习笔记17:堆、堆排序

    目录 堆和堆排序:为什么说堆排序没有快速排序快 如何理解"堆" 如何实现一个堆 1. 往堆中插入一个元素 2. 删除堆顶元素 如何基于堆实现排序 1. 建堆 2. 排序 解答开篇 ...

  8. 【python算法系列四】堆排序算法

    堆排序,就像它的名字一样,利用了堆的特性来进行排序.实现堆排序的思路是,把数组构建成一棵二叉树,并随着每次堆的变化更新堆顶的最大/最小值.堆排序的时间复杂度在所有情况下都是 O(nlgn),它也是一个 ...

  9. 看懂堆排序——堆与堆排序(三)

    看懂堆排序--堆与堆排序(三) 看懂堆排序--堆与堆排序(三) 堆排序的基本思想 代码详解 父亲下标和孩子下标的关系 打印数组的函数 下滤函数 构造堆的函数 删除最大元函数 排序主函数 完整代码及运行 ...

最新文章

  1. centos 创建 logrotate 进行日志分割
  2. vant 软键盘_移动端页面输入底部被软键盘遮挡问题
  3. C语言科技感图片,科技感与运动范十足,几何C黑棚图曝出,年轻消费者又多了新选择...
  4. PostMessage发送字符串和结构体
  5. jmeter+Fiddler:通过Fiddler抓包生成jmeter脚本
  6. Docker搭建SonarQube代码质量检查平台
  7. 执行一次怎么会写入两次数据_Java进阶知识:一文详解缓存Redis的持久化机制,新手看完也会用
  8. C++实现 逆波兰表达式计算问题
  9. 关于安装更新office版本时,需要卸载office所遇到的问题
  10. ORACLE多表关联UPDATE 语句
  11. 英语语法篇 - 动词的分类和形式
  12. 当编程语言都变成女孩子
  13. Android Contact分析(二):实战篇之读取联系人,模糊查询,通过汉字返回拼音
  14. 微信小程序-全局分享、自定义分享
  15. Attention注意力机制——ECANet以及加入到1DCNN网络方法
  16. 数字化转型 — 新能源汽车 — 生产制造流程 — Overview
  17. 第十届蓝桥杯c语言试题,第十届蓝桥杯真题编程题1-7解析(高级组).pdf
  18. 计算机音乐谱棠梨煎雪,天谕手游棠梨煎雪乐谱代码分享
  19. 精彩回顾 | 2021世界人工智能大会圆满落幕,容智信息助力拓展企业数字生产力
  20. 苹果CMS对接公众号教程

热门文章

  1. 腾讯俞栋:AI发展不是独角戏,要与合作伙伴一起AI in All
  2. wyh的考核(dp/容斥原理)
  3. 高防服务器和高防IP的区别
  4. 云计算机玩绝地求生,什么是云电脑?为什么低配电脑也能玩绝地求生?
  5. SOR迭代法python实现
  6. Hexo建站步步升级
  7. jQuery 输入框的onblur事件
  8. 双时格林函数演练总结
  9. 微博中微服务缓存_微服务常见问题
  10. elementui校验表格出现英文