資料結構 堆

2022-03-25 19:39:38 字數 3693 閱讀 6585

堆是一棵完全二叉樹,可以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 根結點一定大於 或小於 子結點。因為要求堆必須是完全二叉樹,所以可以用線性的資料結構,比如陣列,來實現堆。利用陣列實現...

資料結構 堆

堆常用來實現優先佇列,在這種佇列中,待刪除的元素為優先順序最高 最低 的那個。在任何時候,任意優先元素都是可以插入到佇列中去的,是電腦科學中一類特殊的資料結構的統稱 最大 最小 堆是一棵每乙個節點的鍵值都不小於 大於 其孩子 如果存在 的鍵值的樹。大頂堆是一棵完全二叉樹,同時也是一棵最大樹。小頂堆是...