堆是一棵完全二叉樹,可以o(
1)取最值,o(
logn
)刪除最值,或插入元素。
堆的性質:每個非葉子節點比它的孩子都大(大根堆/大頂堆)/每個非葉子節點比它的孩子都小(小根堆/小頂堆)。
從上面的性質可以推出堆的根就是最值。
堆的常用操作:top返回堆頂,pop彈出堆頂,push壓入新元素。
後兩個操作時間是o(
logn
),因為彈出或壓入後都要調整堆的結構,而調整的次數不超過堆的深度。
然而堆是完全二叉樹,深度不超過o(
logn
),因此pop和push都是o(
logn
)的。
接下來是手寫堆的**:
//完全二叉樹的儲存方式:
//根:1
//節點i的左兒子i*2,右兒子i*2+1
#include
using
namespace
std;
int n,tot;
int t[1000001];
void push(int x)
}void pop()
swap(t[1],t[tot]);
tot--;
int u=1;
while(((u<<1
<=tot)&&(t[u]>t[u<<1]))||(((u<<1)+1
<=tot)&&(t[u]>t[(u<<1)+1])))
if(t[u<<1]1)+1])
else
}}int top()
int main()
else
if(num==2)
else
}return
0;}
然後其實在競賽中更常用的是系統自帶堆——優先佇列priority_queue,很方便,就是慢了一倍左右。
關於優先佇列的使用方法:我這裡給出優先佇列的使用**:priority_queue< t > (t為型別)(後面括號內為時間複雜度)
1. t top(void):返回堆頂(即最值);(1)
2. void pop(void):彈出堆頂;(logn)
3. void push(t):壓入新元素;(logn)
4. bool empty(void):如果堆為空返回true,否則返回false;(1)
5. int size(void):返回堆的元素數量;(1)
另一種定義方式:(建議)
priority_queue< t , vector< t > ,less< t > > (大根堆)
priority_queue< t , vector< t > ,greater< t > > (小根堆)
用自定義型別的前提是那個型別有定義小於號(大根堆)/ 大於號(小根堆)
#include
using
namespace
std;
int n,tot;
priority_queue,greater > q;
int main()
else
if(num==2)
else
}return
0;}
具體時間差就是:
手寫堆828ms
優先佇列1677ms
到這裡,堆的初步介紹就結束了。
接下來最重要的還是題。
寫完堆先交這個題,確保時間不超1000ms就基本合格。
這個題其實本身很簡單,每次取兩堆最小的合併再放入,重複到只剩一堆。
我想把這個題放在這兒的目的就是要說這種思想很重要,就是放入再取出的思想。
有時候這種思想可以使你的操作變得可以撤銷,可以過好多題。
有一次我和@hzy一起去培訓的時候就碰到了這麼乙個題,他想出來了,可是沒時間寫了,錯失了100分,最後就讓我這麼乙個只會暴力的拿了第一(笑)。
這是堆的乙個經典應用:求若干個單調序列中的第m小(大)
這題我們可以將每個函式看成乙個單調上公升的序列,這樣最小值一定在某個隊首,取出最小值後次小值就一定還在某個隊首,這樣,我們就可以一開始將所有隊首放入堆中,然後每次取出某個值後,再將它的序列中的下乙個值放入堆中;如此重複m次就可以得到第m小。
貼**:
#include
using
namespace
std;
struct func
bool
operator > (const func& y) const
}f[10001];
int n,m;
priority_queuevector
,greater> q;
int main()
for(int i=1;i<=m;i++)
return
0;}
#include
using
namespace
std;
int a[100001],b[100001];
struct func
}f[10001];
int n;
priority_queuevector
,greater> q;
int main()
for(int i=1;i<=n;i++)
sort(a+1,a+n+1);
sort(b+1,b+n+1);
for(int i=1;i<=n;i++)
for(int i=1;i<=n;i++)
return
0;}
這題雖然算是乙個平衡樹模板,可是也是一道堆的高階題目,難度達到了提高+/省選-;
這個題中我們要開兩個堆,乙個大頂堆q1,乙個小頂堆q2,大頂堆的頂部對著小頂堆的頂部(q1.
top(
)top(
)),每次壓入乙個新元素的時候都要判斷往哪邊壓,讓其始終保持這個性質。
此時這兩個堆合在一起被稱為對頂堆。
我們還要記錄乙個變數size來記錄q1的大小;每次get的時候,如果size < i,就一直將q2的頂部壓入q1;
如果size > i,就一直將q1的頂部壓入q2;直到size==i之後q1的頂部就是查詢結果。
**不難寫出:
#include
using
namespace
std;
int a[200001],u[200001];
int n,m;
priority_queue,less > q1;
priority_queue,greater > q2;
int main()
for(int i=1;i<=m;i++)
sort(u+1,u+m+1);
int k=1;
int sz=0;
for(int i=1;k<=m;i++)
while(i==u[k])
printf("%d\n",q1.top());
k++;}}
return
0;}
題目我就放這麼多,關鍵還是要能想到用堆,資料結構對比賽只是輔助。
end
資料結構 堆
最大堆 最小堆 堆的定義是 n個元素的序列,當且僅當滿足如下關係時被成為堆 1 ki k2i 且 ki k2i 1 或 2 ki k2i 且 ki k2i 1 i 1,2,n 2 當滿足 1 時,為最小堆,當滿足 2 時,為最大堆。若將此序列對應的一維陣列堪稱是乙個完全二叉樹,則2i和2i 1個節點...
資料結構 堆
資料結構 堆的操作和實現 當應用優先順序佇列或者進行堆排序時,一般利用堆來實現。堆是乙個完全 除最底層 外都是滿的 二叉樹,並滿足如下條件 1 根結點若有子樹,則子樹一定也是堆。2 根結點一定大於 或小於 子結點。因為要求堆必須是完全二叉樹,所以可以用線性的資料結構,比如陣列,來實現堆。利用陣列實現...
資料結構 堆
堆常用來實現優先佇列,在這種佇列中,待刪除的元素為優先順序最高 最低 的那個。在任何時候,任意優先元素都是可以插入到佇列中去的,是電腦科學中一類特殊的資料結構的統稱 最大 最小 堆是一棵每乙個節點的鍵值都不小於 大於 其孩子 如果存在 的鍵值的樹。大頂堆是一棵完全二叉樹,同時也是一棵最大樹。小頂堆是...