關於堆的一切

来源:https://www.cnblogs.com/Sugar-Cube/p/18040667
-Advertisement-
Play Games

前置知識 首先給出堆的定義: 堆是一顆樹,滿足堆的性質,即: 對於一個節點,它的權值大於(或小於)它的各個兒子的權值 有這個性質,顯然 堆的根節點權值是整個堆中最大或最小的 由此可分為小根堆和大根堆 二叉堆 最常見的堆就是二叉堆 二叉堆是一顆滿足堆的性質的完全二叉樹 顯然,二叉堆的子樹也是二叉堆 接 ...


前置知識

首先給出堆的定義:
堆是一顆樹,滿足堆的性質,即:
對於一個節點,它的權值大於(或小於)它的各個兒子的權值

有這個性質,顯然
堆的根節點權值是整個堆中最大或最小的
由此可分為小根堆大根堆

二叉堆

最常見的堆就是二叉堆
二叉堆是一顆滿足堆的性質完全二叉樹
顯然,二叉堆的子樹也是二叉堆

接下來,我們以小根堆為例:

當一個節點權值小於其父親時,我們嘗試不斷將這個節點與父親交換,直到其滿足堆的性質
這就是向上調整

同理,
當一個節點權值大於其父親時,我們嘗試不斷將這個節點與其兩個兒子中權值較小的一個交換,直到其滿足堆的性質
這就是向下調整

為什麼這裡要與權值較小的一個交換呢?
因為我們要使交換後滿足堆的性質
所以新的父節點權值應小於它的兩個兒子

由於堆的高度為 \(\log{n}\)
所以以上兩個操作複雜度顯然為 \(\mathcal {O}(\log{n})\)

那麼有了這兩個操作我們就可以完成:
插入(新建節點然後向上調整)
查詢最大(最小)值(根節點權值)
刪除最大(最小)值(與末尾節點交換,再向下調整)
刪除任意節點(與末尾交換,再向上或向下調整,見代碼)
修改任意節點權值(向上或向下調整)

Luogu (模板)堆

#include<bits/stdc++.h>

using namespace std;

static constexpr int AwA = 1e6 + 10;

inline static int Read() {
    int res = 0;
    bool flag = false;
    int c = getchar();
    while ((c < '0' || c > '9') && ~c) {
        flag |= c == '-';
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        res = (res << 1) + (res << 3) + (c ^ 48);
        c = getchar();
    }
    return flag ? -res : res;
}

//這裡我們認為一個節點的父親是u<<1,左兒子為u>>1,右兒子為u>>1|1
//根為1
//由堆為完全二叉樹的性質可得只需開一倍數組
int val[AwA];
//儲存總節點數
int len;

inline void MoveUp(int u) {
    while (u >> 1) {
        int fa = u >> 1;
        if (val[fa] <= val[u]) break;
        swap(val[fa], val[u]);
        u = fa;
    }
}

inline void MoveDown(int u) {
    while (u << 1 <= len) {
        int son = u << 1;
        if (son < len && val[son | 1] < val[son]) son |= 1;
        if (val[u] <= val[son]) break;
        swap(val[u], val[son]);
        u = son;
    }
}

inline int GetTop() { return val[1]; }

inline void Pop() {
    swap(val[1], val[len]);
    len--;
    MoveDown(1);
}

inline void Push(int _val) {
    val[++len] = _val;
    MoveUp(len);
}

//刪除任意已知節點
inline void Delete(int u) {
    swap(val[u], val[len]);
    len--;
    if (val[u << 1] <= val[u]) MoveDown(u);
    else MoveUp(u);
}

//每次對於根進行向下調整,來合併左右兒子代表的兩個堆
//OIwiki上有證明,這種建樹是O(n)的
inline void Build(int _len, const int *a) {
    len = _len;
    memcpy(val + 1, a + 1, sizeof(int) * len);
    //葉子節點不調整,減小常數
    for (int i = (len >> 1) - 1; i; i--) MoveDown(i);
}

int main() {
    int n = Read();
    while (n--) {
        int op = Read();
        if (op == 1) Push(Read());
        else if (op == 2) printf("%d\n", GetTop());
        else Pop();
    }
    return 0;
}

此外,對於這段代碼,我們來仔細討論一下:

inline void Build(int _len, const int *a) {
    len = _len;
    memcpy(val + 1, a + 1, sizeof(int) * len);
    for (int i = (len >> 1) - 1; i; i--) MoveDown(i);
}

這段代碼可以在 \(\mathcal {O}(n)\) 時間內建堆
我們按照節點bfs序倒序向下調整
每當調整到一個節點時
該節點的左右兒子所在子樹已然是二叉堆
這時再把根節點向下調整滿足堆的性質
可視為左右兒子代表的二叉堆的合併

證明:
待補充

可並堆

待補充

其他堆

待補充


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Java 包和 API Java 中的包 用於將相關的類分組在一起。可以將其視為文件目錄中的一個文件夾。我們使用包來避免名稱衝突,並編寫更易於維護的代碼。 包分為兩類: 內置包(來自 Java API 的包) 用戶定義的包(創建自己的包) 內置包 Java API 是一個預先編寫的類庫,可以在 Ja ...
  • Rust的智能指針有哪些?大多數人都能馬上答出Box<T>、Rc<T>和Arc<T>、Ref<T>和在非同步編程中很常見的Pin<P>等等。不過,有一個可能經常被大多數人遺忘的類型,它功能強大,利用好了可以節省很多複製開銷;它就是這篇文章的主角:Cow<B>。 什麼是COW(Copy-On-Write ...
  • 1.pom.xml引入依賴 <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.11</version> </dependency> 2.myba ...
  • 1.問題: 有如下代碼: public class Test { static { i = 0;// 給變數賦值可以正常編譯通過 System.out.print(i);// 編譯器會提示“非法向前引用”(illegal forward reference) } static int i = 1; ...
  • 線程安全 線程安全是多線程或多進程編程中的一個概念,在擁有共用數據的多條線程並行執行的程式中,線程安全的代碼會通過同步機制保證各個線程都可以正常且正確的執行,不會出現數據污染等意外情況。 線程安全的問題最主要還是由線程切換導致的,比如一個房間(進程)中有10顆糖(資源),除此之外還有3個小人(1個主 ...
  • 1.標準庫參考:shutil.rmtree。 根據設計,rmtree在包含只讀文件的文件夾樹上失敗。如果要刪除文件夾,不管它是否包含只讀文件,請使用 import shutil shutil.rmtree('/folder_name', ignore_errors=True) 2.從os.walk( ...
  • 在原文上有刪減,原文鏈接Rust 的面向對象特性。 目錄面向對象語言的特征對象包含數據和行為封裝隱藏了實現細節繼承,作為類型系統與代碼共用顧及不同類型值的 trait 對象定義通用行為的 trait實現 traittrait 對象執行動態分發麵向對象設計模式的實現定義 Post 並新建一個草案狀態的 ...
  • 一、測試運行python項目 1.1 Flask項目 說明1:當我們直接用編譯器運行Flask項目的時候,會有一個提示:意思就是:這是開發環境的伺服器,不能用於生產環境的部署,請使用WSGI的伺服器替換 1.2 Django項目 說明2:當我們直接用編譯器運行Django項目的時候,同樣有個提示,這 ...
一周排行
    -Advertisement-
    Play Games
  • 最近做項目過程中,使用到了海康相機,官方只提供了C/C++的SDK,沒有搜尋到一個合適的封裝了的C#庫,故自己動手,簡單的封裝了一下,方便大家也方便自己使用和二次開發 ...
  • 前言 MediatR 是 .NET 下的一個實現消息傳遞的庫,輕量級、簡潔高效,用於實現進程內的消息傳遞機制。它基於中介者設計模式,支持請求/響應、命令、查詢、通知和事件等多種消息傳遞模式。通過泛型支持,MediatR 可以智能地調度不同類型的消息,非常適合用於領域事件處理。 在本文中,將通過一個簡 ...
  • 前言 今天給大家推薦一個超實用的開源項目《.NET 7 + Vue 許可權管理系統 小白快速上手》,DncZeus的願景就是做一個.NET 領域小白也能上手的簡易、通用的後臺許可權管理模板系統基礎框架。 不管你是技術小白還是技術大佬或者是不懂前端Vue 的新手,這個項目可以快速上手讓我們從0到1,搭建自 ...
  • 第1章:WPF概述 本章目標 瞭解Windows圖形演化 瞭解WPF高級API 瞭解解析度無關性概念 瞭解WPF體繫結構 瞭解WPF 4.5 WPF概述 ​ 歡迎使用 Windows Presentation Foundation (WPF) 桌面指南,這是一個與解析度無關的 UI 框架,使用基於矢 ...
  • 在日常開發中,並不是所有的功能都是用戶可見的,還在一些背後默默支持的程式,這些程式通常以服務的形式出現,統稱為輔助角色服務。今天以一個簡單的小例子,簡述基於.NET開發輔助角色服務的相關內容,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 第3章:佈局 本章目標 理解佈局的原則 理解佈局的過程 理解佈局的容器 掌握各類佈局容器的運用 理解 WPF 中的佈局 WPF 佈局原則 ​ WPF 視窗只能包含單個元素。為在WPF 視窗中放置多個元素並創建更貼近實用的用戶男面,需要在視窗上放置一個容器,然後在這個容器中添加其他元素。造成這一限制的 ...
  • 前言 在平時項目開發中,定時任務調度是一項重要的功能,廣泛應用於後臺作業、計劃任務和自動化腳本等模塊。 FreeScheduler 是一款輕量級且功能強大的定時任務調度庫,它支持臨時的延時任務和重覆迴圈任務(可持久化),能夠按秒、每天/每周/每月固定時間或自定義間隔執行(CRON 表達式)。 此外 ...
  • 目錄Blazor 組件基礎路由導航參數組件參數路由參數生命周期事件狀態更改組件事件 Blazor 組件 基礎 新建一個項目命名為 MyComponents ,項目模板的交互類型選 Auto ,其它保持預設選項: 客戶端組件 (Auto/WebAssembly): 最終解決方案裡面會有兩個項目:伺服器 ...
  • 先看一下效果吧: isChecked = false 的時候的效果 isChecked = true 的時候的效果 然後我們來實現一下這個效果吧 第一步:創建一個空的wpf項目; 第二步:在項目裡面添加一個checkbox <Grid> <CheckBox HorizontalAlignment=" ...
  • 在編寫上位機軟體時,需要經常處理命令拼接與其他設備進行通信,通常對不同的命令封裝成不同的方法,擴展稍許麻煩。 本次擬以特性方式實現,以兼顧維護性與擴展性。 思想: 一種命令對應一個類,其類中的各個屬性對應各個命令段,通過特性的方式,實現其在這包數據命令中的位置、大端或小端及其轉換為對應的目標類型; ...