看動畫輕鬆理解 堆

2022-05-17 16:19:58 字數 3345 閱讀 5406

堆(heap)又被為優先佇列(priority queue)。儘管名為優先佇列,但堆並不是佇列。

因為佇列中允許的操作是先進先出(fifo),在隊尾插入元素,在隊頭取出元素。

而堆雖然在堆底插入元素,在堆頂取出元素,但是堆中元素的排列不是按照到來的先後順序,而是按照一定的優先順序排列的。

本文通過堆的實現、最小堆(最大堆)、堆的時間複雜度、優先佇列的實現、堆排序來介紹「 堆 」。

堆的乙個經典的實現是完全二叉樹(complete binary tree),這樣實現的堆稱為二叉堆(binary heap)。

這裡來說明一下滿二叉樹的概念與完全二叉樹的概念。

滿二叉樹:除了葉子節點,所有的節點的左右孩子都不為空,就是一棵滿二叉樹,如下圖。

可以看出:滿二叉樹所有的節點都擁有左孩子,又擁有右孩子。

完全二叉樹:不一定是乙個滿二叉樹,但它不滿的那部分一定在右下側,如下圖

堆的特性:

堆的基礎實現

只要謹記堆的定義特性,實現起來其實是很容易的。

1public class minheap > 

78    public minheap()

1112    // 返回堆中的元素個數

13    public int size()

1617    // 返回乙個布林值, 表示堆中是否為空

18    public boolean isempty()

2122    // 返回完全二叉樹的陣列表示中,乙個索引所表示的元素的父親節點的索引

23    private int parent(int index)

2627    // 返回完全二叉樹的陣列表示中,乙個索引所表示的元素的左孩子節點的索引

28    private int leftchild(int index)

3132    // 返回完全二叉樹的陣列表示中,乙個索引所表示的元素的右孩子節點的索引

33    private int rightchild(int index)

36}

假設現有元素 5 需要插入,為了維持完全二叉樹的特性,新插入的元素一定是放在結點 6 的右子樹;同時為了滿足任一結點的值要小於左右子樹的值這一特性,新插入的元素要和其父結點作比較,如果比父結點小,就要把父結點拉下來頂替當前結點的位置,自己則依次不斷向上尋找,找到比自己大的父結點就拉下來,直到沒有符合條件的值為止。

動畫講解:

在這裡先將元素 5 插入到末尾,即放在結點 6 的右子樹。

然後與父模擬較, 6 > 5 ,父類數字大於子類數字,子類與父類交換。

重複此操作,直到不發生替換。

show me the code:

新增乙個輔助函式,用來交換傳入的索引兩個位置的元素值

1/**

2     * 交換傳入的索引兩個位置的元素值

3     *

4     * @param i

5     * @param j

6     */

7    public void swap(int i, int j) 

陣列中新增交換兩元素位置的方法,注意下面**中注釋的描述特性位置。

1    /**

2     * 堆中新增元素方法。

3     *

4     * @param e

5     */

6    public void add(e e) 

1112    /**

13     * index 為i位置元素上浮。

14     *

15     * @param i

16     */

17    private void siftup(int i) 

26    }

核心點:將最後乙個元素填充到堆頂,然後不斷的下沉這個元素。

假設要從節點 1 ,也可以稱為取出節點 1 ,為了維持完全二叉樹的特性 ,我們將最後乙個元素 6 去替代這個 1 ;然後比較 1 和其子樹的大小關係,如果比左右子樹大(如果存在的話),就要從左右子樹中找乙個較小的值替換它,而它能自己就要跑到對應子樹的位置,再次迴圈這種操作,直到沒有子樹比它小。

通過這樣的操作,堆依然是堆,總結一下:

show me the code:

1    public e findmin() 

45    public e extractmin() 

1516    /**

17     * k位置元素下移

18     *

19     * @param k

20     */

21    private void siftdown(int k) 

36    }

對於有 n 個節點的堆來說,其高度d = log2n + 1。 根為第 0 層,則第 i 層結點個數為 2i,

考慮乙個元素在堆中向下移動的距離。

堆有logn層深,所以插入刪除的平均時間和最差時間都是o(logn)

普通佇列是一種先進先出的資料結構,先放進佇列的元素取值時優先被取出來。而優先佇列是一種具有最高優先順序元素先出的資料結構,比如每次取值都取最大的元素。

優先佇列支援下面的操作:

**實現

1public class priorityqueue> implements queue 

89    @override

10    public int getsize()

1314    @override

15    public boolean isempty()

1819    @override

20    public e getfront()

2324    @override

25    public void enqueue(e e)

2829    @override

30    public e dequeue()

33}

理解了優先佇列,堆排序的邏輯十分簡單。

第一步:讓陣列形成堆有序狀態;

第二步:把堆頂的元素放到陣列最末尾,末尾的放到堆頂,在剩下的元素中下沉到正確位置,重複操作即可。

堆排序動畫

看動畫輕鬆理解 堆

堆 heap 又被為優先佇列 priority queue 儘管名為優先佇列,但堆並不是佇列。因為佇列中允許的操作是先進先出 fifo 在隊尾插入元素,在隊頭取出元素。而堆雖然在堆底插入元素,在堆頂取出元素,但是堆中元素的排列不是按照到來的先後順序,而是按照一定的優先順序排列的。本文通過堆的實現 最...

輕鬆理解EJB

昨天給乙個班講jsp,那個班已經把所有的技術都學完了,但是學的不好,那個學校讓我過去快速的重講一遍,真的很累,這些學生也很令人感到,他們除了聽我的課,還插班聽其他老師的課,上午我聽說他們剛剛聽別的老師講ejb很暈,我決定臨時調整授課內容,給他們講清楚ejb。ejb難點在多個檔案,他們的作用是什麼?如...

輕鬆理解ConcurrentHashMap的原理

concurrenthashmap是一種執行緒安全的hashmap,相對於hashtable,它擁有更高的併發性.現在,我們就來分析一下在jdk1.8下的concurrenthashmap的實現及原理 public v get object key hash值為負值表示正在擴容,這個時候查的是for...