1、優先隊列
優先隊列和其實是隊列的一種
- 普通隊列:先進先出;後進先出
- 優先隊列:出隊順序和入隊順序無關;和優先級相關
2、堆
堆本身也是一棵樹,其實堆也有很多種,我們在這裏主要使用二叉樹來表示堆,說白了,
二叉堆就是滿足一些特殊性質的二叉樹:
-
二叉堆是一棵完全二叉樹
-
堆中某個節點的值總是不大於其父節點的值(所以也叫做最大堆),注意:層次大的元素值不一定小於層次小的元素
-
滿二叉樹:滿二叉樹就是對於整顆樹來說,除了葉子節點,所有的節點左右孩子都不爲空如下圖
-
完全二叉樹:不一定是滿二叉樹,但它不滿的那一部分,也就是缺失節點的那一部分一定是在整顆樹的右下側。對於一個滿二叉樹來講,一個樹有多少層,節點有多少個其實是固定的(如一層有1個,二層有3個,三層6個),但很多時候我們的元素數量不符合滿二叉樹的元素數量,所以我們當一層滿了後再進入下一層,且從左向右排列元素(即元素一層一層的放)
-
二叉樹可以和二叉樹一樣使用左右節點的方式來實現,但是我們可以看到我們現在是一層一層按順序排放的,所以我們可以用更巧妙的方式來實現 (數組)
public class MaxHeap<E extends Comparable<E>> {
private Array<E> data;
public MaxHeap(int capacity){
data = new Array<>(capacity);
}
public MaxHeap(){
data = new Array<>();
}
// 返回堆中的元素個數
public int size(){
return data.getSize();
}
// 返回一個布爾值, 表示堆中是否爲空
public boolean isEmpty(){
return data.isEmpty();
}
// 返回完全二叉樹的數組表示中,一個索引所表示的元素的父親節點的索引
private int parent(int index){
if(index == 0)
throw new IllegalArgumentException("index-0 doesn't have parent.");
return (index - 1) / 2;
}
// 返回完全二叉樹的數組表示中,一個索引所表示的元素的左孩子節點的索引
private int leftChild(int index){
return index * 2 + 1;
}
// 返回完全二叉樹的數組表示中,一個索引所表示的元素的右孩子節點的索引
private int rightChild(int index){
return index * 2 + 2;
}
}
向堆中添加元素和Shift Up
現在我們要添加元素52,先添加到索引爲10的位置,然而現在不滿足二叉堆的性質,所以要調整52的位置,所以讓52和自己的父親節點依次與父親節點相比較,如52大於父親節點,就交換52與父親節點的位置-------Shift up
// 向堆中添加元素
public void add(E e){
data.addLast(e);
siftUp(data.getSize() - 1); // 傳入它的索引
}
private void siftUp(int k){
while(k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0 ){
data.swap(k, parent(k));
k = parent(k);
}
}
從堆中取出元素(最大值)和Shift Down
我們先取出最大值62,因爲將兩個子樹融合成一棵樹比較複雜,所以我們可以把最小值16換到根節點,然後在採用shift-down操作,
shift-down:把16與左右兩個孩子節點相比較,選擇兩個孩子中最大的那個元素,如果最大的那個元素比16大,則將16與它交換,很顯然52與16做交換
然後繼續與41做交換
因爲16比15大,所以就完成啦
我們來看下代碼:
// 看堆中的最大元素
public E findMax(){
if(data.getSize() == 0)
throw new IllegalArgumentException("Can not findMax when heap is empty.");
return data.get(0);
}
// 取出堆中最大元素
public E extractMax(){
E ret = findMax(); // 找到最大的元素(根元素)
data.swap(0, data.getSize() - 1); // 交換0下標元素和最後一位元素
data.removeLast(); // 刪除原0下標元素
siftDown(0);
return ret;
}
private void siftDown(int k){
while(leftChild(k) < data.getSize()) { // 葉子節點
int j = leftChild(k); // 在此輪循環中,data[k]和data[j]交換位置
// k左右孩子中較大的一個
if( j + 1 < data.getSize() &&
data.get(j + 1).compareTo(data.get(j)) > 0 ) {
j++; // data[j] 是 leftChild 和 rightChild 中的最大值
}
if(data.get(k).compareTo(data.get(j)) >= 0 )
break;
data.swap(k, j);
k = j;
}
}
基於堆的優先隊列
public class PriorityQueue<E extends Comparable<E>> implements Queue<E> {
private MaxHeap<E> maxHeap;
public PriorityQueue(){
maxHeap = new MaxHeap<>();
}
@Override
public int getSize() {
return maxHeap.size();
}
@Override
public boolean isEmpty() {
return maxHeap.isEmpty();
}
@Override
public void enqueue(E e) {
maxHeap.add(e);
}
@Override
public E dequeue() {
return maxHeap.extractMax();
}
@Override
public E getFront() {
return maxHeap.findMax();
}
}