*在資訊學奧賽中常會遇到給你乙個數列,讓你求最大值或最小值的問題,這個問題很簡單,但是一旦遇到頻繁查詢那就很複雜了,這裡就要涉及到乙個新的資料結構,二叉堆.
一、堆的定義
堆的定義:n個元素的序列k=,當且僅當滿足如下條件時,稱之為堆。ki<=k2i且ki<=k2i+1或者ki>=k2i且ki>=k2i+1,(i=1,2,… 可以看出,若將此序列看成完全二叉樹.則堆的定義表明,完全二叉樹中每個非結點的關鍵字均小於等於 (或大於等於)其左右子結點.這樣根就成了最小或最大值.堆也因此稱為小根堆或大根堆.由此可見,從堆中獲取最小 (大)值是很方便的.堆的子樹也滿足堆得性質.
二、堆的儲存方式
[儲存方式]最小堆的元素儲存在a[1…n]內
– 根在a[1]
– k的左兒子是2k, k的右兒子是2k+1,
– k的父親是[k/2]
三、堆的基本操作
1.刪除最小值
• 三步法
①直接刪除根
②用最後乙個元素代替根上元素
③向下調整
//向下堆化
else
break;}
return;}
intdeletemin()
//刪除堆頂元素
2.插入元素和向上調整• 插入元素是先新增到末尾, 再向上調整
• 向上調整:比較當前結點p和父親, 如果父親比p小,停止; 否則交換父親和p, 繼續調整
void
heapup()
//向上堆化
return;}
void
setheap
(int c)
//建堆
3.刪除任意元素(delete)刪除任意元素類似於刪除最小元素,先用最後乙個元素代替被刪除元素,再進行調整。不過值得注意的是,最後乙個元素被交換後可能要向下調整,也有可能要向上調整。具體實現可參照以上兩段**。
4.堆的建立(build)
給n個整數,如何構造乙個二叉堆?可以乙個乙個插入,但是有更好的方法:從下往上一層一層向下調整。由於葉子無需調整,因此只需要從n/2開始遞減到1。由數學歸納法可以證明迴圈變數為i時,第i+1,i+2,…,n均為最小堆的根。**如下:
void
build()
5、時間複雜度向上調整/向下調整:每層是常數級別, 共logn層, 因此o(logn);
插入/刪除:只呼叫一次向上或向下調整, 因此都是o(logn);
接下來我們用一些例題來講解:
1560 – 【堆練習】堆排序description
輸入n個整數,進行二叉堆排序.輸出結果.
input
第1行:乙個整數n(n≤100000),表示個數。
第2行:n個空格分開的整數。
output
第1行:n個空格分開的整數,以不降序排列。
sample input
52 1 0 3 4
sample output
0 1 2 3 4
//hanyiyang c++ code
#include
#define maxn 100005
using
namespace std;
int n,num;
int a[maxn]=;
void
heapup()
//向上堆化
void
heapdown()
return;}
void
setheap
(int x)
//建堆
void
print_del()
intmain()
1421 – 【堆練習】最小函式值這道題看似很簡單,似乎只需要把每乙個函式的值算出來,在進行乙個堆排序就ac了,但是當看到(x∈n∗)的時候 (意思是x是任意自然數)可能就有些懵了,x並沒有限制.應該怎麼計算?通過分析此題,我們不難發現,如果將每乙個函式的所有函式值看作乙個序列,那麼這個序列一定是單調遞增子串行.那麼又有人會說:如果這樣,那就只需要每乙個函式都算m個值或者其他能保證所有函式算出來的數能又m個,再進行乙個堆排序就行了,但是時間複雜度太高容易tle,所以,我們可以採取如下方法:description
有n個函式,分別為f1,f2,…,fn。定義fi(x)=aix^2+bix+ci(x∈n∗)。給定這些ai、bi和ci,請求出所有函式的所有函式值中最小的m個(如有重複的要輸出多個)。
input
第一行輸入兩個正整數n和m。
以下n行每行三個正整數,其中第i行的三個數分別位ai、bi和ci。輸入資料保證ai<=10,bi<=100,ci<=10000。
output
將這n個函式所有可以生成的函式值排序後的前m個元素。這m個數應該輸出到一行,用空格隔開。
sample input
3 10
4 5 3
3 4 5
1 7 1
sample output
9 12 12 19 25 29 31 44 45 54
二叉堆方法:
①對於每乙個給的a[i],b[i],c[i],把此事函式的最小值也就是a[i]+b[i]+c[i]入堆.並用fn[i]記錄此值屬於的函式位置 ,fx[i]記錄此值是這個函式的第幾個值.
②輸出堆頂元素.
③將堆頂元素所在的函式的下乙個值計算出來,並放入堆頂,向下堆化.
④重複執行②,③m次
**如下:
//hanyiyang c++ code
#include
#define maxn 10005
using
namespace std;
int n,m;
int dui[maxn]=;
int a[maxn]
,b[maxn]
,c[maxn]
;int fn[maxn]
,fx[maxn]
;//fn[i]記錄此值屬於的函式位置 ,fx[i]記錄此值是這個函式的第幾個值.
void
heapup()
//向上堆化
return;}
void
heapdown()
//向下堆化
else
break;}
return;}
void
setheap
(int num)
//建堆
intmain()
for(
int i=
1;i<=m;
++i)
return0;
}
這就是二叉堆的大致概念,有能力的讀者可以去嘗試以下幾個問題:1.用堆優化prime演算法
2.用堆優化dijkstra演算法
3.描述二叉堆的陣列表示法,證明樹的高度為logn;
4.為什麼插入和刪除只需要調整一條從根到葉子的路徑而不會影響到其他?
5.如果減小了乙個結點的值,需要對此結點進行怎樣的調整,才能重新得到乙個堆?
常用資料結構 二叉堆
在資訊學奧賽中常會遇到給你乙個數列,讓你求最大值或最小值的問題,這個問題很簡單,但是一旦遇到頻繁查詢那就很複雜了,這裡就要涉及到乙個新的資料結構,二叉堆.一 堆的定義 堆的定義 n個元素的序列k 當且僅當滿足如下條件時,稱之為堆。ki k2i且ki k2i 1或者ki k2i且ki k2i 1,i ...
資料結構 二叉堆
二叉堆一般用來實現優先佇列 優先佇列是一種至少允許以下兩種操作的資料結構 insert 以及 deletemin 同查詢樹一樣,二叉堆具有結構性與堆序性,對二叉堆的基本操作可能會破壞這些性質,所以二叉堆的操作要直到其基本性質滿足才能結束。一 結構性 二叉堆在結構上為完全二叉樹,其具有完全二叉樹的結構...
資料結構 二叉堆
二叉堆 優先佇列 具有結構性和堆序性 結構性為 二叉堆是一棵完全被填滿的二叉樹,有可能的例外是在底層,底層上的元素從左到右填入。這樣的樹稱為完全二叉樹。二叉堆可以用陣列表示,對於陣列中任意位置i上的元素,其左兒子在位置2i上,右兒子在位置2i 1上,父親則在i 2上 小於i 2的最小整數 堆序性為 ...