前言
本文介紹三種外部排序算法:最小堆、勝者樹和敗者樹,以及為什么外部排序往往用敗者樹。
最小堆
最小堆有以下特點:
1、最小堆是一顆完全二叉樹
2、每個父節點的值總是小於等於左右孩子節點的值
3、每個節點的子樹都是一個堆樹
示例圖:
堆常用一維數組結構存儲,增刪改查的時間復雜度都是 log(n)。操作流程舉例:
1、插入操作
(1)將插入元素放到數組末尾
(2)從新插入元素位置開始,將數組頭方向開始調整,或者樹結構上說,就是向上調整
2、查詢操作
(1)取出堆頂元素,並將數組最后一個元素賦值到堆頂
(2)樹結構上,向下調整
勝者樹
勝者樹的特點:
1、勝者樹,是一棵完全二叉樹
2、每個葉子節點表示一個選手,記錄選手的標號
3、每個非葉子節點表示一場比賽,記錄勝者的標號,而每層也就表示一輪比賽
勝者樹示例(數值小的勝出,非葉子節點對應 b 后面的標號):
將 b3 替換成 11,勝者樹重構示例:
重構過程中,先取到父節點的值,拿到選手對應的標號,然后再根據標號拿到選手的值和新插入選手的值比較,勝者寫到父節點。
敗者樹
敗者樹:
1、敗者樹,也是一顆完全二叉樹
2、每個葉子節點表示一個選手,記錄選手的標號
3、每個非葉子節點表示一場比賽,記錄敗者的標號,勝者晉級上一層。並且,因為樹根節點記錄的是敗者的標號,會再新建一個節點記錄最終勝者的標號。
敗者樹示例(數值大的失敗):
將 b3 替換為 13,敗者樹重構示例:
重構上比勝者樹有優化,只需要取父節點的值並進行比較。
為什么外部排序往往用敗者樹
從歷史的發展上來看,首先選擇用來做外部排序的是最小堆。它的插入和查詢復雜度都是 log(n),可以說比較高效。不過,堆調整時,每個節點都需要和左右孩子進行比較,即需要兩次比較,在外部排序中,也就是需要讀取兩次外存,那能不能再優化下呢?
於是,研究出了勝者樹。勝者樹只需要和兄弟節點進行比較,減少了一般的比較量。但是,勝者樹還需要從父節點取一次值,並且,因為新插入的值取代了原先的最優勝者,這個新值向上調整的過程中,必定需要修改父節點的值,即必須要更新勝者。那能不能再優化呢?
既然有勝者樹,那自然也有敗者樹。敗者樹解決了勝者樹存在的弊端,只需要和父節點比較一次,並且新插入的值向上調整過程中,不一定要更新。
綜上所述,目前外部排序大多采用的都是敗者樹算法實現的。側面也反應出,外部排序(文件排序)的瓶頸在於訪存,而不是計算。