<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
優先順序佇列是一種先進先出(FIFO)的資料結構,與佇列不同的是,操作的資料帶有優先順序,通俗的講就是可以比較大小,在出佇列的時候往往需要優先順序最高或者最低的元素先出佇列,這種資料結構就是優先順序佇列(PriorityQueue)
構造方法
這裡只介紹三種常用的構造方法
構造方法 | 說明 |
PriorityQueue() | 不帶引數,預設容量為11 |
PriorityQueue(int initialCapacity) | 引數為初始容量,該初始容量不能小於1 |
PriorityQueue(Collection<? extends E> c) | 引數為一個集合 |
程式碼展示:
import java.util.ArrayList; import java.util.List; import java.util.PriorityQueue; public class TestPriorityQueue { public static void main(String[] args) { PriorityQueue<Integer> p1 = new PriorityQueue<>(); //容量預設為11 PriorityQueue<Integer> p2 = new PriorityQueue<>(10); //引數為初始容量 List<Integer> list = new ArrayList<>(); list.add(0); list.add(1); list.add(2); PriorityQueue<Integer> p3 = new PriorityQueue<>(list); //使用集合list作為引數構造優先 // 級佇列 } }
常用方法
方法 | 說明 |
boolean offer(E e) | 插入元素e,返回是否插入成功,e為null,會拋異常 |
E peek() | 獲取堆(後面介紹堆)頂元素,如果佇列為空,返回null |
E poll() | 刪除堆頂元素並返回,如果佇列為空,返回null |
int size() | 獲取有效元素個數 |
void clear() | 清空佇列 |
boolean isEmpty() | 判斷佇列是否為空 |
offer方法的測試
PriorityQueue<Integer> p = new PriorityQueue<>(); p.offer(1); p.offer(2); p.offer(3); System.out.println(p.size()); p.offer(null);
列印結果:
1,2,3都正常插入,但是插入null的時候,報了NullPointerException空指標異常
peek與poll方法的測試
PriorityQueue<Integer> p = new PriorityQueue<>(); p.offer(1); p.offer(2); p.offer(3); System.out.println(p.peek()); System.out.println(p.poll()); System.out.println(p.size()); p.clear(); System.out.println(p.peek()); System.out.println(p.poll());
列印結果:
預設是小堆,所以堆頂元素是1,獲取到1,在刪除1,剩餘元素個數為兩個,當佇列為空的時候,這兩個方法都返回null
size,isEmpty,clear方法的測試
PriorityQueue<Integer> p = new PriorityQueue<>(); p.offer(1); p.offer(2); p.offer(3); System.out.println(p.size()); System.out.println(p.isEmpty()); p.clear(); System.out.println(p.isEmpty());
列印結果:
列印元素個數為3,所以不為空輸出false,清空後,佇列為空,輸出true
注意事項
PriorityQueue中存放的元素必須能比較大小,不能比較大小的物件不能插入,會丟擲ClassCastException異常
例如:向優先順序佇列中插入兩個學生型別的資料
class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } } public class Test { public static void main(String[] args) { Student s1 = new Student("張三",25); Student s2 = new Student("李四",30); PriorityQueue<Student> p = new PriorityQueue(); p.offer(s1); p.offer(s2); } }
結果:報了型別轉換異常的錯誤,因為student型別不能直接比較大小
如果想比較兩個自定型別的大小,請參考Java中物件的比較這篇文章
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2-o1; } });
注意:o2-o1是建立大堆,o1-o2是建立小堆
PriorityQueue的擴容方式
以下是JDK1.8中擴容的方式:
說明:
題目
方法:建立一個優先順序佇列,獎陣列中的元素依次放入該優先順序佇列中,在依次從該優先順序佇列取出k個即可
class Solution { public int[] smallestK(int[] arr, int k) { int[] ret = new int[k]; if(k == 0 || arr.length==0){ return ret; } PriorityQueue<Integer> p = new PriorityQueue<>(arr.length); for(int i = 0;i < arr.length;i++){ p.offer(arr[i]); } for(int i = 0;i < k;i++){ ret[i] = p.poll(); } return ret; } }
JDK1.8中PriorityQueue底層採用了堆資料結構,堆其實就是對完全二元樹的元素作出了一些調整
所謂堆就是將一組資料按照完全二元樹的順序儲存方式儲存,保證每一個根結點元素大於它的孩子結點的元素(大根堆)或者小於它的孩子結點的元素(小根堆)
堆的性質
堆中某個結點的值總是不大於或著不小於其父節點的值
堆是一顆完全二元樹
堆的建立
此處我們建立小堆,以21,15,19,17,18,23,25為例
發現上述序列根的左右子樹都已經滿足小堆的特性,故只需要將根結點向下調整即可
向下調整的過程:
1. 用parent標記要被調整的結點,child標記parent的左孩子
2. 如果左孩子存在,即child<size,size為序列元素的個數,進行以下操作,直到左孩子不存在
程式碼展示:
public void shiftDown(int[] array,int parent){ int child = parent*2+1; int size = array.length; while(child < size){ if(child+1<size && array[child]>array[child+1]){ child = child+1; } if(array[parent] > array[child]){ swap(array,parent,child); parent = child; child = parent*2+1; }else { break; } } }
注意:在調整以parent為根的二元樹時,必須滿足parent的左右子樹滿足堆的特性,此時才能向下調整parent
時間複雜度分析:最壞情況從根比到葉子,比較的次數為二元樹的高度,故時間複雜度為O(log2N)
那麼對於普通的序列如1,5,3,8,7,6,即根節點的左右子樹不滿足大堆的特性,該如何調整?
方法:從倒數第一個非葉子結點開始調整,直到調整到根
程式碼展示:
public void createHeap(int[] array){ int root = (array.length-2)>>1; for(;root>=0;root--){ shiftDown(array,root); } }
建立堆的時間複雜度
故建堆的時間複雜度為O(N)
堆的插入
堆的插入分為兩步:
例如:給大堆8,7,6,5,1,3插入9
程式碼展示:
public void shiftUp(int[] array,int child){ int parent = (child-1)/2; while(child > 0){ if(array[child] < array[parent]){ break; }else { swap(array,parent,child); child = parent; parent = (child-1)/2; } } }
堆的刪除
堆刪除的是堆頂元素
刪除步驟:
程式碼展示:
public int poll(){ int oldVal = array[0]; array[0] = array[array.length-1]; size--; shiftDown(array,0); return oldVal; }
此處用小堆實現優先順序佇列,並且佇列中儲存的元素為Integer型別
準備工作包括:構造方法,向上調整,向下調整,交換
public class MyPriorityQueue { Integer[] array; int size; public MyPriorityQueue(){ array = new Integer[11]; size = 0; } public MyPriorityQueue(int initCapacity){ if(initCapacity < 1){ throw new IllegalArgumentException("初始容量小於1"); } array = new Integer[initCapacity]; size = 0; } public MyPriorityQueue(Integer[] arr){ array = new Integer[arr.length]; for(int i = 0;i < arr.length;i++){ array[i] = arr[i]; } size = arr.length; int lastLeafParent = (size-2)/2; for(int root = lastLeafParent;root >= 0;root--){ shiftDown(root); } } public void shiftDown(int parent){ int child = parent*2+1; while(child < size){ if(child+1<size && array[child+1]<array[child]){ child = child+1; } if(array[parent] > array[child]){ swap(parent,child); parent = child; child = parent*2+1; }else { return; } } } public void shiftUp(int child){ int parent = (child-1)/2; while(child > 0){ if(array[child] < array[parent]){ swap(child,parent); child = parent; parent = (child-1)/2; }else { return; } } } public void swap(int a,int b){ int t = array[a]; array[a] = array[b]; array[b] = t; } }
插入
public boolean offer(Integer e){ if(e == null){ throw new NullPointerException("插入的元素為null"); } ensureCapacity(); array[size++] = e; shiftUp(size-1); return true; } private void ensureCapacity(){ if(array.length == size){ int newCapacity = array.length*2; array = Arrays.copyOf(array,newCapacity); } }
注意:插入前需要判斷是否擴容,此處擴容按照2倍方式擴容
刪除
public Integer poll(){ if(isEmpty()){ return null; } Integer ret = array[0]; swap(0,size-1); shiftDown(0); return ret; }
獲取堆頂元素
public Integer peek(){ if(isEmpty()){ return null; } Integer ret = array[0]; return ret; }
獲取有效元素個數
public int size(){ return size; }
判空
public boolean isEmpty(){ return size==0; }
清空
public void clear(){ size = 0; }
堆的應用
即求資料中前k個最大或者最小元素,一般情況下資料量都會比較大
如果資料量大使用排序那種方法就不可取了,那麼如何解決呢?
1. 使用資料中前k個資料建堆
求前k個最大,建小堆
求前k個最小,建大堆
2. 用剩餘的元素依次與堆頂元素比較
求前k個最大,若比堆頂元素大,則替換小堆堆頂元素
求前k個最小,若比堆頂元素小,則替換大堆堆頂元素
以上就是Java資料結構之優先順序佇列(PriorityQueue)用法詳解的詳細內容,更多關於Java優先順序佇列的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45