【C/C++】勝者樹與敗者樹:多路歸并排序的利器

文章目錄

  • 勝者樹與敗者樹:多路歸并排序的利器
    • 1 勝者樹簡介
      • 1.1 定義
      • 1.2 勝者樹結構與原理
        • 1.2.1 構造流程
        • 1.2.2 歸并過程
    • 2 敗者樹簡介
      • 2.1 背景場景
      • 2.2 基本定義
      • 2.3 敗者樹結構和原理
        • 2.3.1 樹的構造(初始建樹)
        • 2.3.2 查詢和更新
    • 3 勝者樹 vs 敗者樹
    • 4 勝者樹示例代碼
    • 5 敗者樹代碼示例
    • 6 總結

勝者樹與敗者樹:多路歸并排序的利器

“勝者樹”(Winner Tree)和“敗者樹”(Loser Tree)都是完全二叉樹結構,前者廣泛用于多路歸并排序和優先級選擇問題中,尤其是在歸并排序器、堆式選擇算法中有重要應用,后者常用于多路歸并排序中(例如外部排序),是**勝者樹(Winner Tree)**的一種變體。


1 勝者樹簡介

1.1 定義

  • 勝者樹是一棵完全二叉樹,每個葉子結點表示一個選手(或者一個序列當前值),
  • 每個非葉子節點存儲當前兩名子選手的勝者(較小者)索引。
  • 根節點最終存儲的是所有選手中最小值的索引(即冠軍/勝者)。

勝者樹常用于:

  • ? 多路歸并排序
  • ? 外部排序(歸并多個文件)
  • ? 堆選擇算法(優先隊列)
  • ? 歸并K個有序序列/流式合并

1.2 勝者樹結構與原理

1.2.1 構造流程

假設我們要從 k 個已排好序的序列中進行歸并,每個序列的當前元素是一個“選手”:

  1. 葉子結點存放 k 個選手的索引(或值)。
  2. 相鄰兩個選手進行比較,較小者(勝者)進入上層節點。
  3. 一直遞歸比較,直到根節點,表示最終勝者。
  4. 內部節點都記錄每次比賽的勝者索引。
1.2.2 歸并過程

每輪輸出根節點對應的值(最小值),然后用該“選手”所在的序列下一項替代,并自底向上更新路徑即可,時間復雜度為 O(log k)


2 敗者樹簡介

2.1 背景場景

假設你有多個已經排好序的數組(或文件),想把它們合并成一個大的有序序列。這就叫多路歸并。當有很多路時(比如上百個文件),直接線性比較效率很低,敗者樹可以高效解決這個問題。

2.2 基本定義

  • 敗者樹是一棵完全二叉樹,用于記錄多路歸并中每輪比較的失敗者(較大者)。
  • 樹的內部結點記錄的是敗者,而不是勝者。
  • 整棵樹的根節點并不表示最小值,而是指向在當前輪比賽中被打敗的選手。

敗者樹常用于:

  • 外部排序中的多路歸并排序
  • 優先級選擇器(類似小根堆)
  • K 路歸并排序器(如歸并 16 個文件,找到最小項)

2.3 敗者樹結構和原理

假設有 k 個已排好序的輸入序列,敗者樹的構建步驟如下:

2.3.1 樹的構造(初始建樹)
  1. k 個選手(對應于每個輸入序列的第一個元素)作為葉子節點。
  2. 比較相鄰兩個選手,將較大的(敗者)向上傳遞,較小的(勝者)繼續往上走。
  3. 最后勝者會落在“勝者路徑”上,沿路的結點記錄被其打敗的對手(即敗者)。
  4. 樹的根并不是最終勝者,而是最后一次比較的敗者。
2.3.2 查詢和更新
  • 每次輸出最小元素(勝者)。
  • 然后將該序列推進一個新元素,再從該葉子節點重新進行比較并更新路徑,時間復雜度是 O(log k)

3 勝者樹 vs 敗者樹

特性勝者樹敗者樹
內部節點記錄勝者(最小)敗者(較大)
根節點勝者(最小值)敗者
插入/更新效率O(log k)O(log k)
查詢最小值直接查根查 tree[0] 對應葉子
代碼邏輯復雜度相對較簡單相對復雜
實際使用優先隊列、歸并排序等多路歸并排序優化

4 勝者樹示例代碼

#include <iostream>
#include <vector>
#include <climits>
#include <cassert>class WinnerTree {
private:int k;std::vector<int> tree;   // 完全二叉樹結構,1-based,tree[1] 是根,tree[0] 存儲最終 winner 索引std::vector<int> leaves; // 葉子數組,存儲實際數據public:WinnerTree(const std::vector<int>& init) : k(init.size()), leaves(init) {// 樹大小需要 2*k(包含葉子和內部節點)tree.resize(2 * k, -1);build();}void build() {// 初始化葉子節點(從 tree[k] 到 tree[2k - 1])for (int i = 0; i < k; ++i) {tree[k + i] = i;}// 從底向上構建 winner treefor (int i = k - 1; i > 0; --i) {int left = tree[i * 2];int right = tree[i * 2 + 1];if (right == -1 || (left != -1 && leaves[left] <= leaves[right])) {tree[i] = left;} else {tree[i] = right;}}tree[0] = tree[1]; // winner 存到 tree[0]}void adjust(int leafIdx) {int pos = k + leafIdx;while (pos > 1) {int sibling = (pos % 2 == 0) ? pos + 1 : pos - 1;int parent = pos / 2;int left = tree[parent * 2];int right = (parent * 2 + 1 < (int)tree.size()) ? tree[parent * 2 + 1] : -1;if (right == -1 || (left != -1 && leaves[left] <= leaves[right])) {tree[parent] = left;} else {tree[parent] = right;}pos = parent;}tree[0] = tree[1];}int winner() const {return tree[0];}int value(int i) const {return leaves[i];}void popAndReplace(int idx, int newValue) {leaves[idx] = newValue;adjust(idx);}
};int main() {std::vector<int> init = {3, 6, 1, 9};WinnerTree wt(init);for (int i = 0; i < 4; ++i) {int win = wt.winner();std::cout << wt.value(win) << " ";wt.popAndReplace(win, INT_MAX); // 替換為無窮大,模擬歸并過程}return 0;
}

輸出結果:

1 3 6 9

5 敗者樹代碼示例

#include <iostream>
#include <vector>
#include <climits>class LoserTree {
private:int k;std::vector<int> tree;   // 內部節點,size k,tree[0]為勝者葉子編號std::vector<int> leaves; // 葉子值,size kpublic:explicit LoserTree(const std::vector<int>& input) : k((int)input.size()), tree(k, -1), leaves(input) {build();}void build() {// 初始化所有內部節點為-1for (int i = 0; i < k; ++i) tree[i] = -1;for (int i = k - 1; i >= 0; --i) {adjust(i);}}void adjust(int s) {int t = s;int parent = (s + k) / 2;while (parent > 0) {if (parent >= k) {std::cerr << "ERROR: parent index out of range: " << parent << std::endl;std::cerr << "k = " << k << std::endl;std::exit(1);}// debug輸出當前比較的節點和值// std::cout << "Adjust: parent = " << parent << ", t = " << t//           << ", leaves[t] = " << leaves[t]//           << ", tree[parent] = " << tree[parent]//           << ", leaves[tree[parent]] = " << (tree[parent] == -1 ? INT_MAX : leaves[tree[parent]])//           << std::endl;if (tree[parent] == -1 || leaves[t] < leaves[tree[parent]]) {std::swap(t, tree[parent]);}parent /= 2;}tree[0] = t;}int winner() const { return tree[0]; }int value(int idx) const {if (idx < 0 || idx >= k) return INT_MAX;return leaves[idx];}void popAndReplace(int idx, int newVal) {if (idx < 0 || idx >= k) {std::cerr << "popAndReplace: idx out of range: " << idx << std::endl;return;}leaves[idx] = newVal;adjust(idx);}
};int main() {std::vector<int> input = {20, 15, 30, 10};LoserTree lt(input);std::cout << "Winner index: " << lt.winner() << std::endl;std::cout << "Winner value: " << lt.value(lt.winner()) << std::endl;lt.popAndReplace(lt.winner(), 25);std::cout << "After replacement:" << std::endl;std::cout << "Winner index: " << lt.winner() << std::endl;std::cout << "Winner value: " << lt.value(lt.winner()) << std::endl;return 0;
}

6 總結

勝者樹:

優點描述
? 高效查詢最小值時間復雜度 O(1),更新 O(log k)
? 結構清晰比堆更適合用于 K 路歸并選擇
? 實用性強外排序、文件合并、流式排序等場景常見

敗者樹:

  • 敗者樹是一種適合多路歸并排序的高效數據結構。
  • 每次找最小值和更新的操作是 O(log k)
  • 通常用于數據量過大、需要外部存儲的場景(如磁盤文件排序)。
  • 實現比堆略復雜,但效率在多路歸并時更優。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/82032.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/82032.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/82032.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

零基礎設計模式——第二部分:創建型模式 - 原型模式

第二部分&#xff1a;創建型模式 - 5. 原型模式 (Prototype Pattern) 我們已經探討了單例、工廠方法、抽象工廠和生成器模式。現在&#xff0c;我們來看創建型模式的最后一個主要成員——原型模式。這種模式關注的是通過復制現有對象來創建新對象&#xff0c;而不是通過傳統的…

C++(初階)(十九)——紅黑樹

紅黑樹 紅黑樹概念規則實現結點插入變色變色參考代碼&#xff1a; 查找查找參考代碼 遍歷 紅黑樹檢查完整代碼 概念 紅?樹是?棵?叉搜索樹。它的每個結點增加?個存儲位來表示結點的顏?&#xff0c;可以是紅色或者黑色&#xff08;并不會出現第三種顏色&#xff09;。 通過…

Mistral AI 開源最新 Small 模型——Devstral-Small-2505

Devstral 是一款專為軟件工程任務設計的代理型大語言模型&#xff08;LLM&#xff09;&#xff0c;由 Mistral AI 和 All Hands AI 合作開發 &#x1f64c;。Devstral 擅長使用工具探索代碼庫、編輯多個文件以及驅動軟件工程代理。該模型在 SWE-bench 上表現出色&#xff0c;使…

CDGA|一線二線企業數據治理項目目前發展狀況

一線城市與二線城市企業在數據治理項目的發展狀況上存在一定差異&#xff0c;主要體現在目標、資源投入、策略實施以及文化培育等方面。 一線城市企業數據治理項目發展狀況 ?數據治理目標全面系統?&#xff1a; ?數據質量與安全?&#xff1a;一線城市的大型企業通常擁有海量…

Lyra學習筆記1地圖角色加載流程

目錄 1 地圖加載流程1.1 默認Experience的加載1.2 加載角色1.3 加載場景中的幾個傳送點 2 幾個內建類的筆記2.1 UDataAsset2.2 UAssetManager 純個人筆記&#xff0c;有錯誤歡迎指正&#xff0c;學習階段基本看到不會的就寫一寫&#xff0c;最后有時間會梳理整體結構 先看完了官…

SurfaceFlinger及Android應用RenderThread角度觀察Jank丟幀卡頓

SurfaceFlinger及Android應用RenderThread角度觀察Jank丟幀卡頓 CPU、GPU、Display 三個部分&#xff1a;CPU 負責計算幀數據&#xff0c;把計算好的數據交給 GPU&#xff0c;GPU 會對圖形數據進行渲染&#xff0c;渲染好后放到 buffer &#xff08;圖像緩沖區&#xff09;存起…

《牛客》數組中出現次數超過一半的數字

牛客的刷題之路不停歇 ??? 不積跬步無以至千里&#xff0c;不積小流無以成江海 The harder you work,the luckier you will be 題目及示例 題目鏈接 描述 給一個長度為 n 的數組&#xff0c;數組中有一個數字出現的次數超過數組長度的一半&#xff0c;請找出這個數字。 例…

七彩喜康養護理——科技賦能下的全周期健康守護

在當今社會&#xff0c;隨著人們健康意識的不斷提高&#xff0c;護理行業逐漸走向專業化、精細化&#xff0c;而七彩喜智養護理作為一種新興的護理方式&#xff0c;逐漸受到了廣泛的關注和應用。 它不僅僅是針對單一病癥的治療護理&#xff0c;而是一種全面的、全方位的健康管…

【爬蟲】12306自動化購票

上文&#xff1a; 【爬蟲】12306查票-CSDN博客 下面是簡單的自動化進行搶票&#xff0c;只寫到預定票&#xff0c;沒有寫完登陸&#xff0c; 跳出登陸后與上述代碼同理修改即可。 感覺xpath最簡單&#xff0c;復制粘貼&#xff1a; 還有很多寫法&#xff1a; 官網地址&#…

Java設計模式之組合模式:從入門到精通(保姆級教程)

文章目錄 1. 組合模式概述1.1 專業定義1.2 通俗解釋1.3 模式結構2. 組合模式詳細解析2.1 模式優缺點2.2 適用場景3. 組合模式實現詳解3.1 基礎實現3.2 代碼解析4. 組合模式進階應用4.1 透明式 vs 安全式組合模式4.2 組合模式與遞歸4.3 組合模式與迭代器5. 組合模式在實際開發中…

游戲如何應對反編譯工具dnspy

Unity Mono 是 Unity 引擎默認的腳本運行時環境&#xff0c;由跨平臺的開源 .NET 框架實現&#xff0c;它允許開發者使用 C# 等編程語言編寫游戲邏輯&#xff0c;憑借簡單易用的開發環境和高效的腳本編譯速度&#xff0c;得到了眾多游戲的青睞。 在 Mono 模式下&#xff0c;游…

騰訊云證書過期提醒的應對措施,Caddy 自動管理的 Let‘s Encrypt 證書.

用騰訊的免費證書&#xff0c;90天需要換一次。 Caddy 自動管理的 Lets Encrypt 證書. 在網站上按F12然后找到security選項&#xff0c;然后選擇View certifcate 就可以看到證書的有效期。 完全無需操作 你的網站實際使用的是 Caddy 自動管理的 Lets Encrypt 證書&#xff0c;…

[Java實戰]Spring Boot整合Elasticsearch(二十六)

[Java實戰]Spring Boot整合Elasticsearch&#xff08;二十六&#xff09; 摘要&#xff1a;本文通過完整的實戰演示&#xff0c;詳細講解如何在Spring Boot項目中整合Elasticsearch&#xff0c;實現數據的存儲、檢索和復雜查詢功能。包含版本適配方案、Spring Data Elasticsea…

【關聯git本地倉庫,上傳項目到github】

目錄 1.下載git2.綁定用戶3.git本地與遠程倉庫交互4.github項目創建5.上傳本地項目到github6.完結撒花???&#xff01;&#xff01;&#xff01; 1.下載git git下載地址&#xff1a;https://git-scm.com/downloads 下載安裝后創建快捷地址&#xff1a;&#xff08;此處比較…

[Vue]路由基礎使用和路徑傳參

實際項目中不可能就一個頁面&#xff0c;會有很多個頁面。在Vue里面&#xff0c;頁面與頁面之間的跳轉和傳參會使用我們的路由: vue-router 基礎使用 要使用我們需要先給我們的項目添加依賴:vue-router。使用命令下載: npm install vue-router 使用路由會涉及到下面幾個對象:…

軟考-軟件工程開發模型

軟考-軟件工程開發模型 參考視頻&#xff1a; 軟件工程概述&開發模型 &#xff0c;配合視頻理解更清晰&#xff5e; 軟件的生命周期為&#xff1a;需求分析、軟件設計、軟件開發、運行維護直至被淘汰 幾個階段。 軟件工程支持 4 個活動&#xff0c;簡稱 PDCA&#xff0c…

【寫在創作紀念日】基于SpringBoot和PostGIS的各省東西南北四至極點區縣可視化

目錄 前言 一、空間檢索簡介 1、空間表結構 2、四至空間檢索 二、前后端實現 1、后端實現 2、前端集成 三、成果展示 1、東部省份 2、西部省份 3、南部省份 4、北部省份 5、中部省份 四、總結 前言 在當今數字化時代&#xff0c;地理信息數據的分析與可視化對于眾…

智能守護校園“舌尖安全“:AI視頻分析賦能名廚亮灶新時代

引言&#xff1a; 在校園食品安全備受關注的今天&#xff0c;一套融合視頻監控管理平臺與AI視頻分析盒子的智能解決方案正在全國多地學校食堂悄然落地&#xff0c;為傳統的"名廚亮灶"工程注入科技新動能。這套系統不僅實現了后廚操作的"透明化"&#xff0…

【軟件設計師】計算機網絡考點整理

以下是軟件設計師考試中 ??計算機網絡?? 的核心考點總結&#xff0c;幫助您高效備考&#xff1a; ??一、網絡體系結構與協議?? ??OSI七層模型 & TCP/IP四層模型?? 各層功能&#xff08;物理層-數據鏈路層-網絡層-傳輸層-會話層-表示層-應用層&#xff09;對應協…

Starrocks的CBO基石--統計信息的來源 StatisticAutoCollector

背景 本文來從底層代碼的實現來分析一下Starrocks怎么獲取統計信息&#xff0c;這些統計信息在后續基于CBO的代價計算的時候有著重要的作用 本文基于Starrrocks 3.3.5 結論 Starrocks的統計信息的收集是通過周期性的運行一系列的SQL&#xff08;以分區為維度&#xff0c;如果…