1. 程式人生 > >《常見演算法和資料結構》優先佇列(2)——二叉堆

《常見演算法和資料結構》優先佇列(2)——二叉堆

二叉堆

本系列文章主要介紹常用的演算法和資料結構的知識,記錄的是《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裡很多型別都是不可變的。

這有很多好處,但是壞處就是如果你需要改變值必須要新建一個物件。