基于jdk PriorityQueue的思考

前言

众所周知堆排序就是个二叉堆,其实本质上就是个完全二叉树,我其实是想讲堆排序的,可为什么会和优先队列扯上关系呢,而优先队列又为何和jdk扯上关系呢。看完你就明白的

优先队列

优先队列顾名思义,是带有优先级的队列,普通队列是怎么样的(先进先出),那么优先级队列呢肯定是优先级最大的先出,这有什么好处?

我们可以通过它来得到最大数或者最小数,也能插入数字

映射到现实中来说某一天你跟室友去吃饭,外面有很多家餐馆。你们一开始没有任何的计划,只是心中确定了几家自己平时较常来的餐馆。A餐馆价格高,非常好吃;B餐馆价格便宜,好吃;C餐馆价格一般,比较好吃。那么根据你们当天的心情就会有不同的选择。比如说月底了大家都没钱可想吃好吃的,那么B餐馆的优先级别肯定是最大的。当然新开了一家不错的餐馆也有可能加入计划行列中来。

我们可能没什么思路去实现一个优先队列,那么怎么办呢,jdk源码就是很好的学习地方
找到java.util.PriorityQueue这个类我们发现几个关键成员变量。

 private static final int DEFAULT_INITIAL_CAPACITY = 11;
 transient Object[] queue; 
 private int size = 0;
 private final Comparator<? super E> comparator;

1.第一个不用说了默认初始化大小为11
2.很神奇它是靠一个Object数组维持一个优先队列的
3.size记录它的大小
4.comparator肯定是意味着靠它来比较优先级大小

接下来我们就彻底懵圈了,我们该怎么看?我们可以先从add(E e)这个一般队列都有的方法为突破口。发现add(E e)实际上调用了offer(E e)这个方法

 public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
        if (i == 0)
            queue[0] = e;
        else
            siftUp(i, e);
        return true;
    }

我们可以发现以下几点:
1.参数不能为null
2.当size大小大于等于队列长度会自动扩容(队列长度小于64扩容至size的2倍是大小反之增加原来的50%)
3.我们发现一个特别的方法private void siftUp(int k, E x) ,而其中在是否传入comparator作出了两个方法分别的选择,我们简单起见看private void siftUpComparable(int k, E x)这个方法

private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = key;
    }

可能看不明白,我稍微敏感一下,parent是插入位置的父节点的位置,(k-1)/2,如果它比它的父亲节点大就赋值在这个位置k,反之循环,直到循环结束再赋值。

这个循环十分精妙精妙在哪里,精妙在如果在循环内部交换n次的话的话会赋值3n次,但是拿出来就不一样了,总共需要n+2次。我们其实可以发现这个数组最小的默认在第一个。而且父亲结点都是比孩子节点小的。
有没有发现这个数组的结构很熟悉,其实它就是个完全二叉树(二叉堆)



当然上图只是举例子说明样子。

当然对应的我们照猫画虎可以自己实现个简单的优先队列

public class PriorityQueue<T> {
    private Comparable<T>[] keys;
    private int size;
    @SuppressWarnings("unchecked")
    public PriorityQueue (int max) {
        keys = new Comparable[max+1];
        size = 0;
    }
    public int getSize() {
        return size;
    }
    public void insert(Comparable<T> a) {
        keys[++size] = a;
        swim(size);
    }
    public Comparable<T> delMax() {
        Comparable<T> max = keys[1];
        exch(1, size);
        keys[size] = null;
        size--;
        sink(1);
        return max;
    }
    public void exch(int i, int j) {
        Comparable<T> temp = keys[i];
        keys[i] = keys[j];
        keys[j] = temp;
    }
    @SuppressWarnings("unchecked")
    public boolean less(int i, int j) {
        if (keys[i].compareTo((T)keys[j]) < 0)
            return true;
        return false;
    }
    public void swim(int k) {
        Comparable<T> key = keys[k];
        while(k>1) {
            if(less(k/2 , k))
            exch(k, k/2);       
            k = k/2;
        }
    }
    public boolean isEmpty() {
        return size==0;
    }
    public void sink(int k) {
        while(2*k<=size) {
            int j=2*k;
            if(less(j, j+1)) j++;
            if(!less(k, j)) break;       
            else exch(k, j);
            k=j;
        }
    }
}

我的swim对应源码的siftUp,sink对应siftDown(当然没有优化过方便理解)

假设在小顶堆中插入操作相当于插在队列最后再不断上升直到不能再小了,而删除最小操作则是拿根节点的值跟最后面的值交换,再把最后的值设为null,再size减小。

堆排序

思路很简单我们想要一个升序的序列
1.先构建一个大顶堆
2.不断从大顶堆的根节点跟最后面的值交换,然后不设置为null,只是让size减小,那么逐渐变得有序。(我们无法去比较左右叶子节点的大小)

实现如下:

public class HeapSort {
    private static <T>void sink(Comparable<T>[] a, int k, int len) {
        while(2*k<len) {
            int j=2*k;
            if(j<len&&less(a, j, j+1)) j++;
            if(less(a, j, k)) break;
            exch(a, j, k);
            k = j;
        }
    }
    private static <T>void exch(Comparable<T>[] a, int i, int j) {
        Comparable<T> temp = a[i-1];
        a[i-1] = a[j-1];
        a[j-1] = temp;
    }
    @SuppressWarnings("unchecked")
    private static <T> boolean less(Comparable<T>[] a, int i, int j) {
        return a[i-1].compareTo((T)a[j-1])<0;
    }
    private static <T> void sort(Comparable<T>[] a) {
        int len = a.length;
        for(int i=len/2;i>=1;i--) {
            sink(a, i, len);//0123->1234(操作exch和less index即可)
        }
        while(len>1) {
            exch(a, 1, len--);
            sink(a, 1, len);//=1变0,已经到底一次了
        }
    }
    private static <T> void show(Comparable<T>[] a) {
        for(int i=0;i<a.length;i++) {
            System.out.print(a[i]+" ");
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容