《常見演算法和資料結構》優先佇列(2)——二叉堆
阿新 • • 發佈:2018-12-31
二叉堆
本系列文章主要介紹常用的演算法和資料結構的知識,記錄的是《Algorithms I/II》課程的內容,採用的是“演算法(第4版)”這本紅寶書作為學習教材的,語言是java。這本書的名氣我不用多說吧?豆瓣評分9.4,我自己也認為是極好的學習演算法的書籍。
通過這系列文章,可以加深對資料結構和基本演算法的理解(個人認為比學校講的清晰多了),並加深對java的理解。
之前我們已經介紹了優先佇列的基本內容,現在來看看用二叉堆實現優先佇列吧。
1 二叉堆的定義
堆是一個完全二叉樹結構(除了最底下一層,其他層全是完全平衡的),如果每個結點都大於它的兩個孩子,那麼這個堆是有序的。
二叉堆是一組能夠用堆有序的完全二叉樹排序的元素,並在陣列中按照層級儲存(不用陣列的第一個位置)
2 二叉堆的性質
- 最大的元素在a[1] (root結點)
- 每個k的父親在k/2
- 每個k的孩子在k*2和k*2+1
3 二叉堆的操作
3.1 上浮(孩子大於父親)——對應插入操作
迴圈,每次比較自己和父親,如果比父親大就交換,直到root。
3.2 插入
先把元素加入陣列的最後一個位置,然後進行上浮到正確位置
3.3 下沉(父親小於兒子)——對應刪除操作
迴圈,每次比較自己和兩個孩子,如果比孩子小就交換,如果比兩個孩子都小,就交換到兩個裡面較大的一個。直到葉子。
3.4 刪除最大元素
先把根元素與最後一個元素交換,刪除最後一個元素,然後從根開始下沉到正確位置。
4 二叉堆優先佇列程式碼
/**
*
* @author rocky
*/
public class MaxPQ<Key extends Comparable<Key>> {
private Key[] pq;
private int N;
public MaxPQ(int capacity) {
pq = (Key[]) new Comparable[capacity + 1];
}
public boolean isEmpty() {
return N == 0;
}
public void insert(Key key)
{
N++;
pq[N] = key;
swim(N);
}
public Key delMax() {
Key max = pq[1]; //get the max element
exch(1, N); //exchange between the root and the last element
N--;
sink(1); //sink to the right place
pq[N+1] = null; //delete
return max;
}
private void swim(int k)
{
while(k > 1 && less(k/2, k))
{
exch(k, k/2);
k /= 2;
}
}
private void sink(int k) {
while(k*2 <= N) //if this node has left child
{
int child = k * 2;
if (child < N && less(child, child + 1)) { //if the left child is less than the right child
child = child + 1; //change child to the right child
}
if (less(k, child)) {
exch(k, child);
}
k = child;
}
}
private boolean less(int i, int j) {
return pq[i].compareTo(pq[j]) < 0;
}
private void exch(int i, int j) {
Key t = pq[i];
pq[i] = pq[j];
pq[j] = t;
}
}
5 二叉堆擴充套件
5.1 不可變性
我們演算法的前提是使用者不會改變佇列的元素,如果使用者能改變佇列的元素,那麼佇列成立的條件就會破壞。
不可變的資料型別,就是說一個例項一旦建立,就不可以改變。java裡很多型別都是不可變的。
這有很多好處,但是壞處就是如果你需要改變值必須要新建一個物件。