​hello,大家好,我是小慕,今天分享的内容是优先级队列,堆排序的内容。“Priority Queue”和“堆排序”有些暧昧!_Priority Queue“Priority Queue”和“堆排序”有些暧昧!_Priority Queue“Priority Queue”和“堆排序”有些暧昧!_Priority Queue,我们来一起探索研究吧!学知识不能仅仅只学表面,要学就学它的设计原理。

 

一、啥是优先级队列(Priority Queue)?

我们都知道队列是一种特征为FIFO“先进先出”的数据结构,每次从队列中取出的是最早加入队列中的元素。但是,许多应用需要另一种队列,每次从队列中取出的应是具有最高优先权的元素,这种队列就是优先级队列(Priority Queue),也称为优先权队列

 

以下图为例,假设胖虎的权重最大,小夫的权重第二,大雄的权重最小。那么入队的时候虽然大雄是最先入队的,小夫第二个入队的,胖虎最后入队。但是出队的时候顺序却不是这样,出队的时候是胖虎第一个出队,然后是小夫,最后才是大雄。  (这下知道胖虎多难搞了吧,惹不起 “Priority Queue”和“堆排序”有些暧昧!_Priority Queue_04

“Priority Queue”和“堆排序”有些暧昧!_Priority Queue_05

而优先级队列的底层原理是怎么样的呢?  其实底层是思想是堆排序。那下面我们就来了解一下堆排序吧!

 

 

二、啥是堆排序?

堆的概念: 堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树的数组对象。   

 

小根堆(“最小堆”):父节点的值比每一个子节点的值都要小,对每一个节点都是如此。

 

大根堆(“最大堆”):父节点的值比每一个子节点的值都要大,,对每一个节点都是如此。

“Priority Queue”和“堆排序”有些暧昧!_Priority Queue_06

 

堆排序的存储方式:在描述堆的操作前,首先来看堆是怎样表示的,一种可能的方法就是使用数组,因为堆在形式上是一颗完全二叉树,用数组来存储它不会浪费任何空间,例如下图就是一个大根堆的存储方案,从根节点开始,从上到下,从左往右依次存储到数组中即可  (注:数组的0号位置不做存储哦!

“Priority Queue”和“堆排序”有些暧昧!_Priority Queue_07

 

 

由于堆的形状是一颗完全二叉树,故而我们能发现一些规律

parent(i)= i/2(取整)  (PS:这是求该父节点位置)
left child(i)=2*i      (PS: 这是求左孩子节点位置)
right child(i)=2*i +1  (PS: 这是求右孩子节点位置)

 

 

三、堆的shift up

知道了堆的这些特性以后,我们接下来看看堆的shift up操作吧,shift up操作一般往堆中添加元素后需要调整堆的结构而使用的。以下图为例,我们想往一个大根堆中添加一个节点52,首先我们把52放到堆的末尾。

“Priority Queue”和“堆排序”有些暧昧!_Priority Queue_08

 

然后我们发现52的父节点是16,而16比52小,故而我们将两者交换位置,如下图所示

“Priority Queue”和“堆排序”有些暧昧!_Priority Queue_09

 

 

然后我们发现此时52的父节点是41,而41比52小,故而继续交换52和41的位置,变成如下图

“Priority Queue”和“堆排序”有些暧昧!_Priority Queue_10

这个时候我们再看一下52的父节点是62,而62比52大,故而不需要交换位置,到次,堆的顺序调整就算完成了,这就是所谓的shift up,简单吧

 

shift up代码实现:

private void shiftUp(int k){//如果当前节点比父节点大,则和父节点交换位置,直到小于父节点为止    while(k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0 ){        data.swap(k, parent(k));        k = parent(k);    }}

 

四、堆的shift down

shift down操作一般往堆中取出一个元素后需要调整堆的结构而使用的。以下图为例,我们取出该大根堆的第一个元素之后,堆的顶部就是空缺的,如下图所示

“Priority Queue”和“堆排序”有些暧昧!_Priority Queue_11

 

  这个时候我们就把堆的最后一个元素迁移到这个空位来,如下图,把16这个节点转移堆的顶部

“Priority Queue”和“堆排序”有些暧昧!_Priority Queue_12

 

 

这个时候我们发现16的两个孩子节点都比它大,先比较子节点 52 和 30 哪个大,和大的交换位置,如下图所示

“Priority Queue”和“堆排序”有些暧昧!_Priority Queue_13

 

 

然后此时我们发现16的子节点28和41仍然比它大,于是把16和41交换位置,如下图

“Priority Queue”和“堆排序”有些暧昧!_Priority Queue_14


然后16和它的子节点15进行比较,16大于15,故而不需要交换位置,到此shift down过程就结束了

 

shift down完整代码如下

private void shiftDown(int k){     //2*k是左孩子的位置        while( 2*k <= count ){            int j = 2*k;               //如果右孩子的值大于左孩子的值,则把右孩子的位置赋值给j            if( j+1 <= count && data[j+1].compareTo(data[j]) > 0 )                j ++;            // data[j] 是 data[2*k]和data[2*k+1]中的最大值            if( data[k].compareTo(data[j]) >= 0 ) break;            swap(k, j);// 在此轮循环中,data[k]和data[j]交换位置            k = j;        }    }

 

好啦,今天的内容就到这里了,拜拜,我们下期再见“Priority Queue”和“堆排序”有些暧昧!_Priority Queue_15