面試考察頻率:⭐⭐⭐⭐
什麼是大/小頂堆?
大/小根堆的實現可以看似爲一顆完全二叉樹,但和完全二叉樹還是有區別的(具體的完全二叉樹在之後會講。
堆頂元素爲整個堆的最大/小元素。
使用情景?
局部元素排序、實現優先隊列、SPFA優化
如何來實現?
構建思路如下
⚪操作只有Push和Pop。
⚪文中通過數組來實現,本文主要講解大頂堆(小頂堆同理),只要任意保證子節點小於父節點就可以。存儲可以從下標從0開始或下標從1開始,個人更偏向於使用從1開始存儲的方式操作更方便。
⚪從0開始存儲時,獲取子節點的公式爲:左孩子爲parent2+1,右孩子爲parent2+2。 獲取父節點的公式爲:(child-1)/2
⚪從1開始存儲時,獲取子節點的公式爲:左孩子爲parent2,右孩子爲parent2+1。 獲取父節點的公式爲:child/2。
基礎結構表示:
int[] heap = new int[1000];
int heapSize = 0;
Push元素:
public void Push(int x)
{
//思路:每次先將新元素放到最後,在進行調整。從下向上,比較父元素
heap[++heapSize] = x;
int now = heapSize, nxt = 0;
while (now > 1)
{
nxt = now / 2;
if (heap[now] <= heap[nxt]) break;
Swap(ref heap[now], ref heap[nxt]);
now = nxt;
}
}
每次講新元素放到堆的最後,在從下向上進行調整。
如果父節點比子節點小就交換當前子節點和父節點,爲了始終保持父節點大於子節點。
如果出現父節點大於子節點的情況退出循環。
Pop元素:
public int Pop()
{
//思路:把頂的元素先存到零時變量中,在將堆末尾的元素覆蓋到頂元素完成刪除。之後再由頂
// 向下進行調整。上一個元素比較子兩個元素,其中要比較兩個子元素的大小,選擇最大/ 小的與上邊的元素進行交換
int res = heap[1];
int now = 1, nxt = 0;
heap[1] = heap[heapSize--];
while (now * 2 <= heapSize)
{
nxt = now * 2;
if (heap[nxt + 1] > heap[nxt] && nxt + 1 <= heapSize) nxt++;
if (heap[nxt] <= heap[now])
{
return res;
}
Swap(ref heap[nxt], ref heap[now]);
now = nxt;
}
return res;
}
每次將堆頂元素保存到臨時變量中(因爲堆頂元素要Pop出去,最後還要返回堆頂元素所以需要保存下來)。
之後把堆尾元素覆蓋到堆頂元素去,再從上到下調整整個堆。
其中要注意,由於子節點有兩個,一定要選最大的進行於父節點交換。所以注意這個情況的判定。
完整代碼如下,更多數據C#數據結構源碼歡迎瀏覽我的倉庫:https://github.com/w199753/DataStructural-CSharp 最後附上完整代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataStructural
{
/// <summary>
/// 大根堆。小根堆實現方法相同
/// </summary>
public class MaxHeap
{
int[] heap = new int[1000];
int heapSize = 0;
/// <summary>
/// 添加元素
/// </summary>
/// <param name="x"></param>
public void Push(int x)
{
//思路:每次先將新元素放到最後,在進行調整。從下向上,比較父元素
heap[++heapSize] = x;
int now = heapSize, nxt = 0;
while (now > 1)
{
nxt = now / 2;
if (heap[now] <= heap[nxt]) break;
Swap(ref heap[now], ref heap[nxt]);
now = nxt;
}
}
/// <summary>
/// 刪除元素並返回最大值
/// </summary>
/// <returns></returns>
public int Pop()
{
//思路:把頂的元素先存到零時變量中,在將堆末尾的元素覆蓋到頂元素完成刪除。之後再由頂
// 向下進行調整。上一個元素比較子兩個元素,其中要比較兩個子元素的大小,選擇最大/ 小的與上邊的元素進行交換
int res = heap[1];
int now = 1, nxt = 0;
heap[1] = heap[heapSize--];
while (now * 2 <= heapSize)
{
nxt = now * 2;
if (heap[nxt + 1] > heap[nxt] && nxt + 1 <= heapSize) nxt++;
if (heap[nxt] <= heap[now])
{
return res;
}
Swap(ref heap[nxt], ref heap[now]);
now = nxt;
}
return res;
}
public void Clear()
{
heap = new int[1000];
heapSize = 0;
}
private void Swap(ref int a, ref int b)
{
a ^= b;
b ^= a;
a ^= b;
}
}
}