日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

容器源码分析之PriorityQueue(十)

發(fā)布時間:2024/2/28 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 容器源码分析之PriorityQueue(十) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

PriorityQueue的介紹

PriorityQueue也叫優(yōu)先隊列,所謂的優(yōu)先隊列其實就是每次從優(yōu)先隊列中取出來的元素要么是最大值,要么是最小值,PriorityQueue是用二叉堆數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的。

在了解PriorityQueue之前,我們最好先吧二叉堆先了解清楚。
可以參考這篇文章:二叉堆 圖文解析。

PriorityQueue的uml圖:

說明:

  • PriorityQueue繼承于AbstractQueue。
  • PriorityQueue內(nèi)部是通過Object[]數(shù)組來保存數(shù)據(jù)的,也就是說它本質(zhì)是通過數(shù)組來實現(xiàn)的。
  • PriorityQueue的源碼分析

    構(gòu)造方法

    PriorityQueue(),PriorityQueue(int),PriorityQueue(Comparetor)實際上都是使用PriorityQueue(int,Comparetor)

    public PriorityQueue(int initialCapacity,Comparator<? super E> comparator) {if (initialCapacity < 1)throw new IllegalArgumentException();this.queue = new Object[initialCapacity];this.comparator = comparator;}

    說明:PriorityQueue的構(gòu)造方法指定隊列的大小和比較器,默認的大小是DEFAULT_INITIAL_CAPACITY = 11

    還有三個構(gòu)造方法:
    PriorityQueue(Collection< ? extends E > c),
    PriorityQueue(PriorityQueue< ? extends E> c ),
    PriorityQueue(SortedSet< ? extends E > c)

    我們知道SortedSet,PriorityQueue是Collection的子接口和實現(xiàn),所以我們只需要了解 PriorityQueue(Collection< ? extends E> c )構(gòu)造方法。

    public PriorityQueue(Collection<? extends E> c) {if (c instanceof SortedSet<?>) {SortedSet<? extends E> ss = (SortedSet<? extends E>) c;this.comparator = (Comparator<? super E>) ss.comparator();initElementsFromCollection(ss);}else if (c instanceof PriorityQueue<?>) {PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;this.comparator = (Comparator<? super E>) pq.comparator();initFromPriorityQueue(pq);}else {this.comparator = null;initFromCollection(c);}}

    很容易理解,它分參數(shù)c的三種情況進行處理:SortedSet,PriorityQueue和Collection

    如果是PriorityQueue的話 調(diào)用initFromPriorityQueue:

    private void initFromPriorityQueue(PriorityQueue<? extends E> c) {if (c.getClass() == PriorityQueue.class) {this.queue = c.toArray();this.size = c.size();} else {initFromCollection(c);}}

    說明:直接把數(shù)組和大小復(fù)制過來。

    如果是SortSet的話 調(diào)用initElementsFromCollection:

    private void initElementsFromCollection(Collection<? extends E> c) {Object[] a = c.toArray();// If c.toArray incorrectly doesn't return Object[], copy it.if (a.getClass() != Object[].class)a = Arrays.copyOf(a, a.length, Object[].class);int len = a.length;if (len == 1 || this.comparator != null)for (int i = 0; i < len; i++)if (a[i] == null)throw new NullPointerException();this.queue = a;this.size = a.length;}

    說明:因為是SortSet也是排序的,所以一樣復(fù)制就可以了,區(qū)別是SortSet中的元素不一定是Object[],所以需要通過Arrays.copyOf(a, a.length, Object[].class)將元素轉(zhuǎn)成Object[]

    如果是Collection的話 調(diào)用initFromCollection:

    private void initFromCollection(Collection<? extends E> c) {initElementsFromCollection(c);heapify();}

    說明,先調(diào)用SortSet情況下的初始化方法,然后進行堆排序,下面講解一下如何堆排序:

    heapify()

    //從插入最后一個元素的父節(jié)點位置開始建堆 private void heapify() {for (int i = (size >>> 1) - 1; i >= 0; i--)siftDown(i, (E) queue[i]); }

    siftDown方法

    //在位置k插入元素x,為了保持最小堆的性質(zhì)會不斷調(diào)整節(jié)點位置 private void siftDown(int k, E x) {if (comparator != null)//使用插入元素的實現(xiàn)的比較器調(diào)整節(jié)點位置siftDownUsingComparator(k, x);else//使用默認的比較器(按照自然排序規(guī)則)調(diào)整節(jié)點的位置siftDownComparable(k, x); }

    這里只演示默認比較器的情況,自定義比較器其實差不多。

    //具體實現(xiàn)調(diào)整節(jié)點位置的函數(shù) private void siftDownComparable(int k, E x) {Comparable<? super E> key = (Comparable<? super E>)x;// 計算非葉子節(jié)點元素的最大位置int half = size >>> 1; // loop while a non-leaf//如果不是葉子節(jié)點則一直循環(huán)while (k < half) {//得到k位置節(jié)點左孩子節(jié)點,假設(shè)左孩子比右孩子更小int child = (k << 1) + 1; // assume left child is least//保存左孩子節(jié)點值Object c = queue[child];//右孩子節(jié)點的位置int right = child + 1;//把左右孩子中的較小值保存在變量c中if (right < size &&((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)c = queue[child = right];//如果要插入的節(jié)點值比其父節(jié)點更小,則交換兩個節(jié)點的值if (key.compareTo((E) c) <= 0)break;queue[k] = c;k = child;}//循環(huán)結(jié)束,k是葉子節(jié)點queue[k] = key; }

    說明,這個函數(shù)是調(diào)整某個節(jié)點的位置,方法是,從這個節(jié)點A開始,跟它的兩個子節(jié)點中最小一個B比較,如果該節(jié)點A更小則方法結(jié)束,如果B更小,則把B和A的位置互換,然后把這個字節(jié)點當(dāng)成“當(dāng)前節(jié)點”往下遞歸。

    添加元素

    public boolean add(E e) {//調(diào)用offer函數(shù)return offer(e);} public boolean offer(E e) {if (e == null)throw new NullPointerException();modCount++;int i = size;//如果size大于等于隊列的大小,就要對隊列,也就是數(shù)組擴容if (i >= queue.length)grow(i + 1);size = i + 1;if (i == 0)queue[0] = e;else//具體執(zhí)行添加元素的函數(shù)siftUp(i, e);return true;} //調(diào)用不同的比較器調(diào)整元素的位置 private void siftUp(int k, E x) {if (comparator != null)siftUpUsingComparator(k, x);elsesiftUpComparable(k, x); } //使用默認的比較器調(diào)整元素的位置 private void siftUpComparable(int k, E x) {Comparable<? super E> key = (Comparable<? super E>) x;while (k > 0) {int parent = (k - 1) >>> 1;//保存父節(jié)點的值Object e = queue[parent];//使用compareTo方法,如果要插入的元素小于父節(jié)點的位置則交換兩個節(jié)點的位置if (key.compareTo((E) e) >= 0)break;queue[k] = e;k = parent;}queue[k] = key; }

    可以從代碼中看出來,添加一個元素是首先吧元素添加到最后,然后跟父節(jié)點對比,如果比父節(jié)點小,就互換位置,相當(dāng)于它“上移”了,然后再往上比較。

    刪除元素

    remove方法

    private E removeAt(int i) {assert i >= 0 && i < size;modCount++;//s是隊列的隊頭,對應(yīng)到數(shù)組中就是最后一個元素int s = --size;//如果要移除的位置是最后一個位置,則把最后一個元素設(shè)為nullif (s == i) // removed last elementqueue[i] = null;else {//保存待刪除的節(jié)點元素E moved = (E) queue[s];queue[s] = null;//先把最后一個元素和i位置的元素交換,之后執(zhí)行下調(diào)方法siftDown(i, moved);//如果執(zhí)行下調(diào)方法后位置沒變,說明該元素是該子樹的最小元素,需要執(zhí)行上調(diào)方//法,保持最小堆的性質(zhì)if (queue[i] == moved) {//位置沒變siftUp(i, moved); //執(zhí)行上調(diào)方法if (queue[i] != moved)//如果上調(diào)后i位置發(fā)生改變則返回該元素return moved;}}return null; }

    在上面的代碼上調(diào)方法與下調(diào)方法只會執(zhí)行其中的一個,參看下面例子

    需要執(zhí)行下調(diào)方法的示意圖:

    這是需要執(zhí)行上調(diào)方法的示意圖:

    擴容操作

    在添加元素中有個grow擴容操作,下面我們對它進行解析:

    private void grow(int minCapacity) {int oldCapacity = queue.length;// 如果舊容量小擴大為原來2倍,否則1.5倍int newCapacity = oldCapacity + ((oldCapacity < 64) ?(oldCapacity + 2) :(oldCapacity >> 1));// 如果新容量太大,則作限制if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);queue = Arrays.copyOf(queue, newCapacity);}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}

    總結(jié):

    • 時間復(fù)雜度:remove()方法和add()方法時間復(fù)雜度為O(logn),remove(Object obj)和contains()方法需要O(n)時間復(fù)雜度,取隊頭則需要O(1)時間

    • 在初始化階段會執(zhí)行建堆函數(shù),最終建立的是最小堆,每次出隊和入隊操作不能保證隊列元素的有序性,只能保證隊頭元素和新插入元素的有序性,如果需要有序輸出隊列中的元素,則只要調(diào)用Arrays.sort()方法即可

    • PriorityQueue是非同步的,要實現(xiàn)同步需要調(diào)用java.util.concurrent包下的PriorityBlockingQueue類來實現(xiàn)同步

    • 在隊列中不允許使用null元素

    總結(jié)

    以上是生活随笔為你收集整理的容器源码分析之PriorityQueue(十)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 国产成人99 | 最新视频 - 88av | 欧美少妇bbw | 九九精品视频在线 | 成人免费影片 | 女同性做爰全过程 | 精品国产日本 | 日韩欧美影院 | 久操欧美| 欧美成人一区二区三区高清 | 在线一区二区观看 | 色爽 av| 国产美女福利在线 | 一区二区在线观看av | 国产无套丰满白嫩对白 | 97超碰国产精品无码蜜芽 | 久久网国产 | 黑人满足娇妻6699xx | 欧美韩日 | 俄罗斯破处 | 色老二导航 | 国产成人精品影视 | 黄色一级网址 | 天天爽网站 | 日韩精品久久一区二区 | 神宫寺奈绪一区二区三区 | 麻豆理论片 | 手机av资源 | 免费观看亚洲 | 捆绑中国女人hd视频 | 日韩欧美电影一区二区三区 | 日本美女黄色一级片 | 少妇与公做了夜伦理 | gai视频在线观看资源 | 91看片在线| 美女131爽爽爽做爰视频 | 久热色 | 精品无码三级在线观看视频 | 夜夜草影院| 国产欧美一区二区三区精华液好吗 | 少妇人妻丰满做爰xxx | 亚洲图色av | 91视频免费看 | 久久国产一级 | 日本草草视频 | 男女啊啊啊 | 波多野结衣导航 | 国产亚洲精品久久久久久久 | 国产乱码精品一区二区三区亚洲人 | 欧美一区二区三区爽爽爽 | 97视频久久| 麻豆91茄子在线观看 | 在线免费观看黄视频 | 二区三区在线观看 | 欧美不卡三区 | 性欧美jzjz2 九草影院 | 中文字幕在线免费播放 | 国产成人精品视频在线观看 | 国产永久视频 | 中国特级黄色大片 | 日韩av一区二区在线播放 | 亚洲精品推荐 | 亚洲欧美日韩在线一区 | 天天操天天弄 | 毛片啪啪啪 | 国产亚洲二区 | 麻豆成人久久精品一区二区三区 | 午夜剧场免费在线观看 | 国产超碰在线 | av天天有| 色就是色av | 亚洲国产精品综合久久久 | 亚洲视频一二 | 欧美18—19性高清hd4k | 亚洲色图av在线 | 有码一区二区 | 麻豆久久久午夜一区二区 | 午夜视频大全 | 韩日毛片| 18我禁在线观看 | 久久综合日本 | 性欧美久久久 | 爱爱网站免费 | 一区二区在线观看免费视频 | 日本一级淫片1000部 | 韩日一区二区 | 日韩av免费网址 | 国产精选一区二区三区 | 日韩av一区在线播放 | 日韩久久高清 | 中文字幕中文在线 | 国产免费网 | 激情四射网站 | 午夜激情视频网 | 国产精品福利在线播放 | 成人在线一区二区三区 | 色欲国产精品一区二区 | 深夜在线免费视频 | 国产精品五区 |