saulstavo發表於2024-12-06

堆是一種樹形結構,樹的根是堆頂,堆頂始終保持為所有元素中優先順序最高的元素,如小根堆與大根堆,小根堆的堆頂始終為最小的元素,大根堆的堆頂始終保持為最大的元素。堆一般用二叉樹實現,稱為二叉堆。二叉堆的典型應用有堆排序和優先佇列。

本片將包括:

目錄
    • (1.二叉堆的概念
    • (2.二叉堆的操作
      • 1.上浮
      • 2.下沉
      • 3.查詢堆頂
      • 4.查詢大小
      • 5.判斷是否為空
    • (3.堆與priority_queue
      • 1.初始化
      • 2.操作
    • (3.例題

(1.二叉堆的概念

二叉堆是一棵完全二叉樹。用陣列實現的二叉樹堆,樹中每個節點與陣列存放的元素對應。如圖所示,用陣列實現一棵二叉堆。

二叉堆中的每個節點,都是以它為父節點的子樹的最小值。

​ 用陣列儲存完全二叉樹,節點數量為 \(n\)\(a[1]\) 為根,透過上圖不難發現如下性質:

​ (1) 對於所有 \(i>1\) 的節點,其父節點為 \(i/2\) ;

​ (2) 若 \(2i>n\) ,則 \(i\) 節點無子節點;若 \(2i+1>n\)\(i\) 無右節點;

​ (3) 若 \(i\) 節點有子節點,則左子節點在 \(2i\) ,右子節點在 \(2i+1\) ;

(2.二叉堆的操作

堆的操作有兩種:上浮與下沉;

1.上浮

某個節點的優先順序上升,或在堆底新加入一個元素(建堆,把新元素加入堆),此時需要從上到下恢復堆的順序。

如下圖:

code:

void push(int x){
    heap[len++]=x;
    int i=len;
    while(i>1&&heap[i]<heap[i/2]){
        swap(heap[i],heap[i/2]);
        i=i/2;
	}
}

2.下沉

某個節點的優先順序下降,或將根節點替換為為一個優先順序更高的元素(彈出堆頂),此時需要從上到下維護堆的順序。

如下圖:

code:

void pop(){
    heap[1]=heap[len--];
    int i=1;
    while(2*i<n){
        int son=2*n;
        if(son<len&&heap[son+1]<heap[son])
            son++;
        if(heap[son]<heap[i]){
            swap(heap[son],heap[i]);
            i=son;
        }
        else 
            break;
    }
}

3.查詢堆頂

根據上述內容可知, \(heap[1]\) 即為堆頂

int top(){
    return heap[1];
}

4.查詢大小

即判斷 \(len\) 是否為0。

int size(){
   return len; 
}

5.判斷是否為空

bool empty(){
    if(len==0) return true;
    return false;
}

(3.堆與priority_queue

STL中的優先佇列(priority_queue)是用堆實現的。

1.初始化

在STL中優先佇列的初始化:

priority_queue<int>                                //預設為大根堆
priority_queue<int,vector<int>,less<int> >s1;      //大頂堆
//vector是存放的容器,less為優先順序
priority_queue<int,vector<int>,greater<int> >s2;   //小頂堆

2.操作

操作 作用
\(qu.push(x)\) 將元素 \(x\) 加入優先佇列
\(qu.pop()\) 彈出隊首
\(qu.top()\) 返回隊首元素(不彈出)
\(qu.size()\) 返回佇列長度
\(qu.empty()\) 佇列是否為空

(3.例題

  1. P3378 【模板】堆(模版題,沒什麼好說的)
  2. P1090 合併果子(比起用陣列,堆更加簡單)
  3. P1631 序列合併