许多应用都需要处理有序的元素,但有时,我们不要求所有元素都有序,或是一定要一次就将它们排序,许多情况下,我们会收集这些元素里的最大值或最小值。

这种情况下一个合适的数据结构应该支持两种操作:插入元素、删除最大元素。

优先队列与栈和队列类似,但它有自己的奇妙之处。

在本文中,会讲解基于二叉堆的一种优先队列的经典实现方法(代码没有任何难度,主要是理解思想)。

一、关于堆

1、堆的定义:

数据结构二叉堆能很好地实现优先队列的操作。在二叉堆中,每个元素都要保证大于等于另外两个位置的元素,相应的,这些位置的元素又至少要大于等于数组中的另外两个元素。

将所有元素画成一颗二叉树,就能很容易看出这种结构。

(图示1)

2、堆的算法:

在堆有序的过程中我们会遇到两种情况:

某个节点的优先级上升,我们需要由下至上恢复堆的顺序。

当某个节点的优先级下降,我们需要由上至下恢复堆的顺序。

在排序算法中,我们只通过私有辅助函数来访问元素:

1     private void exch(int i, int j) {
2         Key temp = pq[i];
3         pq[i] = pq[j];
4         pq[j] = temp;
5     }
6
7     private boolean less(int i, int j) {
8         return pq[i].compareTo(pq[j]) < 0;
9     }

①、由下至上的堆的有序化(上浮)

1     private void swim(int k) {// 二叉堆
2         while (k > 1 && less(k / 2, k)) {
3             exch(k / 2, k);
4             k = k / 2;
5         }
6     }

k/2即为k节点的父节点,当k大于k/2时交换两者,并继续与其父节点比较,直到找到合适的位置。

②、由上至下的堆的有序化(下沉)

 1     private void sink(int k) {// 二叉堆
 2         while (2 * k <= N) {
 3             int j = 2 * k;
 4             if (j < N && less(j, j + 1)) {
 5                 j++;
 6             }
 7             if (!less(k, j)) {
 8                 break;
 9             }
10             exch(k, j);
11             k = j;
12         }
13     } 

对于二叉树,2*k即为k的左子节点,将左右子节点进行比较,再将父节点与较大的子节点比较,如果子节点大于父节点,就将他们交换,并继续向下比较,直到找到合适的位置。

③、调整数组大小

如果不知道元素的个数,任意在初始化时造成空间的浪费。我们需要创造一个函数,用来调整数组的大小。

在插入方法中,如果空间已满,就将数组大小扩展为原来的两倍。在删除方法中,如果元素的个数小于数组长度的1/4,就将数组的长度减小一半。

1     private void resize(int n) {
2         Key[] temp = (Key[]) new Comparable[n + 1];
3         for (int i = 1; i <= N; i++) {
4             temp[i] = pq[i];
5         }
6         pq = temp;
7         L = n;
8     }

有了上面的方法,我们只需在插入和删除方法中加入判断语句即可。

④、多叉堆(了解即可)

在掌握了二叉堆的原理之后,将其改进为多叉堆只需要做几个改动。下面直接放代码,有兴趣的朋友可以自己动手。

 1     private void swim(int k, int d) {// d叉堆:(k+d-2)/d为d叉堆第k个节点的父节点
 2         while (k > 1 && less((k + d - 2) / d, k)) {
 3             exch((k + d - 2) / d, k);
 4             k = (k + d - 2) / d;
 5         }
 6     }
 7
 8     private void sinkM(int k, int d) {// d叉堆
 9         while (d * k - (d - 2) <= N) {// d叉堆k节点的第一个子节点
10             int j = d * k - (d - 2);
11             int flag = k;
12             while (j <= N && j <= d * k + 1) {
13                 if (less(flag, j)) {
14                     flag = j;
15                 }
16                 j++;
17             }
18             if (flag == k) {// flag没有改变
19                 break;
20             }
21             exch(k, flag);
22             k = flag;
23         }
24     }

二、堆排序(非降序):

(示意图2)

堆排序的sink()方法经过修改sink(a,b)中a是被排序的元素,b为排序的最大范围(修改之前排序的最大范围为元素总个数)。

 1     public void sort(Comparable[] a) {//堆排序
 2         int n=N;
 3         for(int k=n/2;k>=1;k--) {
 4             sink(k,n);
 5         }
 6         while(n>1) {
 7             exch(1,n--);
 8             sink(1,n);
 9         }
10     }

1、heap construction(堆的构造)

可以看到在for循环中,我们只扫描了数组一半元素,因为我们跳过了大小为1的子堆,每次对一个节点排序时,以该节点为根节点的子堆就是有序的,所以我们最后会得到一个堆有序的二叉堆。

2、sortdown(下沉排序)

下沉排序每次选出最大的元素放入数组空出的位置,这有点像选择排序,但所需的比较要小得多,因为堆提供了一种从未排序部分找到最大元素的有效方法。

三、java代码展示(所有代码)

  1 public class MaxPQ<Key extends Comparable<Key>> {
  2     private Key[] pq;
  3     private static int N = 0;// 元素个数
  4     private static int L;// 数组长度(不包括0)
  5
  6     public MaxPQ(int maxN) {
  7         pq = (Key[]) new Comparable[maxN + 1];
  8         L = maxN;
  9     }
 10
 11     public boolean isEmpty() {
 12         return N == 0;
 13     }
 14
 15     public int size() {
 16         return N;
 17     }
 18
 19     public void insert(Key v) {// 二叉堆
 20         if (N == L) {
 21             resize(2 * N + 1);
 22             System.out.println("resize Double");
 23         }
 24         pq[++N] = v;
 25         swim(N);
 26     }
 27
 28     public void insert(Key v, int d) {// d叉堆
 29         if (N == L) {
 30             resize(2 * N + 1);
 31             System.out.println("resize Double");
 32         }
 33         pq[++N] = v;
 34         swim(N, d);
 35     }
 36
 37     public Key delMax() {
 38         Key max = pq[1];
 39         exch(1, N--);
 40         pq[N + 1] = null;
 41         sink(1);
 42         if (N > 0 && N == L / 4) {
 43             resize(L / 2);
 44             System.out.println("resize 1/2");
 45         }
 46         return max;
 47     }
 48
 49     public Key delMax(int d) {
 50         if (N == 0) {
 51             System.out.println("none!");
 52         }
 53         Key max = pq[1];
 54         exch(1, N--);
 55         pq[N + 1] = null;
 56         sinkM(1, d);
 57         if (N > 0 && N == L / 4) {
 58             resize(L / 2);
 59             System.out.println("resize 1/2");
 60         }
 61         return max;
 62     }
 63
 64     private void swim(int k) {// 二叉堆
 65         while (k > 1 && less(k / 2, k)) {
 66             exch(k / 2, k);
 67             k = k / 2;
 68         }
 69     }
 70
 71     private void swim(int k, int d) {// d叉堆:(k+d-2)/d为d叉堆第k个节点的父节点
 72         while (k > 1 && less((k + d - 2) / d, k)) {
 73             exch((k + d - 2) / d, k);
 74             k = (k + d - 2) / d;
 75         }
 76     }
 77
 78     private void sink(int k) {// 二叉堆
 79         while (2 * k <= N) {
 80             int j = 2 * k;
 81             if (j < N && less(j, j + 1)) {
 82                 j++;
 83             }
 84             if (!less(k, j)) {
 85                 break;
 86             }
 87             exch(k, j);
 88             k = j;
 89         }
 90     }
 91
 92     private void sinkM(int k, int d) {// d叉堆
 93         while (d * k - (d - 2) <= N) {// d叉堆k节点的第一个子节点
 94             int j = d * k - (d - 2);
 95             int flag = k;
 96             while (j <= N && j <= d * k + 1) {
 97                 if (less(flag, j)) {
 98                     flag = j;
 99                 }
100                 j++;
101             }
102             if (flag == k) {// flag没有改变
103                 break;
104             }
105             exch(k, flag);
106             k = flag;
107         }
108     }
109
110     private void resize(int n) {
111         Key[] temp = (Key[]) new Comparable[n + 1];
112         for (int i = 1; i <= N; i++) {
113             temp[i] = pq[i];
114         }
115         pq = temp;
116         L = n;
117     }
118
119     private void exch(int i, int j) {
120         Key temp = pq[i];
121         pq[i] = pq[j];
122         pq[j] = temp;
123     }
124
125     private boolean less(int i, int j) {
126         return pq[i].compareTo(pq[j]) < 0;
127     }
128
129     public void sort(Comparable[] a) {//堆排序
130         int n=N;
131         for(int k=n/2;k>=1;k--) {
132             sink(k,n);
133         }
134         while(n>1) {
135             exch(1,n--);
136             sink(1,n);
137         }
138     }
139
140     private void sink(int k, int n) {//二叉树 从k到n排序
141         while (2 * k <= n) {
142             int j = 2 * k;
143             if (j < n && less(j, j + 1)) {
144                 j++;
145             }
146             if (!less(k, j)) {
147                 break;
148             }
149             exch(k, j);
150             k = j;
151         }
152     }
153
154     public static void main(String[] args) {
155         MaxPQ mpq = new MaxPQ<>(3);
156         mpq.insert(1);
157         mpq.insert(2);
158         mpq.insert(3);
159         mpq.insert(4);
160         mpq.insert(5);
161         mpq.insert(6);
162         mpq.insert(7);
163         mpq.insert(8);
164         mpq.insert(9);
165         mpq.insert(10);
166         mpq.insert(11);
167         mpq.sort(mpq.pq);
168         for(int i=1;i<=N;i++) {
169             System.out.println(mpq.pq[i]);
170         }
171         /*for (int i = 1; i <= 13; i++) {
172             System.out.println(mpq.delMax());
173         }*/
174     }
175
176 }

转载于:https://www.cnblogs.com/Unicron/p/9664696.html

排序算法之——优先队列经典实现(基于二叉堆)相关推荐

  1. 【nodejs原理源码杂记(8)】Timer模块与基于二叉堆的定时器

    [摘要] timers模块部分源码和定时器原理 示例代码托管在:http://www.github.com/dashnowords/blogs 一.概述 Timer模块相关的逻辑较为复杂,不仅包含Ja ...

  2. 【数据结构与算法拓展】二叉堆原理、实现与例题(C和java)

    前言 数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷. 也因如此,它作为博主大二上学期最重 ...

  3. 数据结构之优先队列--二叉堆(Java实现)

    前言 数据结构队列的学习中,我们知道队列是先进先出的.任务被提交到队列中,按照先进先出的原则 对各个任务进行处理.不过在现实的情况下,任务通常有着优先级的概念,例如短任务.管理员的操作 应该优先执行. ...

  4. 关于二叉堆(优先队列)的其他操作及其应用

    [0]README 0.1)本文总结于 数据结构与算法分析:源代码均为原创, 旨在了解到我们学习了优先队列后,还能干些什么东西出来, 增加学习的interest: 0.2)以下列出了 关于二叉堆(优先 ...

  5. 优先队列与相关题目(Python、二叉堆)

    1. 优先队列知识 1.1 优先队列简介 优先队列:一种特殊的队列.在优先队列中,元素被赋予优先级,当访问队列元素时,具有最高优先级的元素最先删除. 优先队列与普通队列最大的不同点在于出队顺序 普通队 ...

  6. 图解:什么是二叉堆?

    在正式开始学习堆之前,一定要大脑里回顾一下什么是完全二叉树,因为它和堆可是息息相关奥! 如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树. 而如果二叉树中除去最后一层节点为满二叉 ...

  7. 二叉堆详解实现优先级队列

    二叉堆详解实现优先级队列 文章目录 二叉堆详解实现优先级队列 一.二叉堆概览 二.优先级队列概览 三.实现 swim 和 sink 四.实现 delMax 和 insert 五.最后总结 二叉堆(Bi ...

  8. 《恋上数据结构第1季》二叉堆实现优先级队列

    优先级队列(Priority Queue) 优先级队列简介 优先队列的底层实现 二叉堆实现优先级队列源码 测试代码 数据结构与算法笔记目录:<恋上数据结构> 笔记目录 想加深 Java 基 ...

  9. 图论——Dijkstra+prim算法涉及到的优先队列(二叉堆)

    [0]README 0.1)为什么有这篇文章?因为 Dijkstra算法的优先队列实现 涉及到了一种新的数据结构,即优先队列(二叉堆)的操作需要更改以适应这种新的数据结构,我们暂且吧它定义为Dista ...

最新文章

  1. host ntrip 千寻rtk_什么是千寻知寸cors账号?它提供的定位服务精度如何?使用时需要注意哪些问题?...
  2. 课后练习----实现窗口的切换
  3. 五年级下册电子计算机与多媒体,语文人教版五年级下册《电子计算机与多媒体》.doc...
  4. 上传文件的跨域处理(转)
  5. 7-13 镖局运镖 (10 分)
  6. Hystrix熔断机制原理剖析
  7. VOC2007/2012数据集解析
  8. C语言——将一个正整数分解质因数。例如:输入90,打印出90=2*3*3*5。
  9. lighttpd 配置 ssl证书
  10. SpringBoot使用分布式锁
  11. Gitlab 12.9.4 的搭建部署及遇到的问题。
  12. 设计一个学生学籍管理系统
  13. 基于姿态估计的运动计数APP开发
  14. 浅谈企业信息化建设的整体规划
  15. 使用ffmpeg转码m3u8并播放及跨域问题
  16. 五十音图平假名随机生成
  17. 华为认证到底值不值得考?
  18. 34岁王晓松的成绩单:新城控股收入股价双增,毛利率骤降9%
  19. 使用js在桌面上写一个倒计时器_现代课程网教学互动平台课件编辑器:计时器、倒计时器(图文版)...
  20. 硬盘检测软件测试培训,认识专业的考机工具PassMark BurnInTest_软件测试_软件测试培训_软件测试频道_中国IT实验室...

热门文章

  1. mysql5.7单机多实例_Mysql 5.7.21单机多实例安装
  2. 卸载 流程_「工具」Windows 卸载软件,这一个就够了
  3. opencv机器学习线性回归_全面讲解手推实战机器学习之线性回归
  4. foxmail提示不知道这样的主机_不知道和婚礼策划师沟通时谈哪些?这样做让你高效备婚...
  5. eos和以太坊有什么关系_以太坊 2.0是什么?
  6. python中关键字参数含义_python中接受任意关键字的参数
  7. 【mysql】sql查询速度不变?不同数据量下,查询速度不会变化的问题
  8. docker客户端连接远程服务器
  9. python【蓝桥杯vip练习题库】ADV-181质因数2(短除法)
  10. mysql中设置字符集语句_mysql设置字符集