算法漫游指北(第十二篇):二分查找、樹、樹的相關概念、樹的種類


一、二分查找

二分查找又稱折半查找,優點是比較次數少,查找速度快,平均性能好;其缺點是要求待查表為有序表,且插入刪除困難。因此,折半查找方法適用於不經常變動而查找頻繁的有序列表。

二分查找操作的數據集是一個有序的數據集。開始時,先找出有序集合中間的那個元素。如果此元素比要查找的元素大,就接着在較小的一個半區進行查找;反之,如果此元素比要找的元素小,就在較大的一個半區進行查找。在每個更小的數據集中重復這個查找過程,直到找到要查找的元素或者數據集不能再分割。

 

二分查找的實現與分析

二分查找法實質上是不斷地將有序數據集進行對半分割,並檢查每個分區的中間元素。在以下介紹的實現方法中,有序數據集存放在sorted中,sorted是一塊連續的存儲空間。參數target是要查找的數據。

此實現過程的實施是通過變量left和right控制一個循環來查找元素(其中left和right是正在查找的數據集的兩個邊界值)。首先,將left和right分別設置為0和size-1。在循環的每次迭代過程中,將middle設置為left和right之間區域的中間值。如果處於middle的元素比目標值小,將左索引值移動到middle后的一個元素的位置上。即下一組要搜索的區域是當前數據集的上半區。如果處於middle的元素比目標元素大,將右索引值移動到middle前一個元素的位置上。即下一組要搜索的區域是當前數據集的下半區。隨着搜索的不斷進行,left從左向右移,right從右向左移。一旦在middle處找到目標,查找將停止;如果沒有找到目標,left和right將重合。

 

二分查找圖示

 

動圖

二分法查找實現

非遞歸方式實現

# 非遞歸方式
def binary_search(nums,target):
​
    if len(nums) == 0:
        return False
    left = 0 
    right = len(nums) - 1
    while left <= right:
        middle = (left + right)//2
        if nums[middle] == target:
            return True
        elif nums[middle] < target:
            left = middle + 1
        elif nums[middle] > target:
            right = middle - 1
    return False
​
li = [10,14,21,38,45,47,53,81,86,99]        
print(binary_search(li,47))
print(binary_search(li,9))

  

遞歸方式實現

def binary_search(nums,target):
    n = len(nums)
    if n > 0:
        middle = len(nums) // 2
        if nums[middle] == target:
            return True
        elif nums[middle] > target:
            return binary_search(nums[:middle],target)
        elif nums[middle] < target:
            return binary_search(nums[middle+1:],target)
    return False
​

  


二分查找復雜度

 

時間復雜度

  • 最優時間復雜度:O(1)

  • 最壞時間復雜度:O(logn)

 

二分查找雖然性能比較優秀,但應用場景也比較有限。底層必須依賴數組,並且還要求數據是有序的。對於較小規模的數據查找,我們直接使用順序遍歷就可以了,二分查找的優勢並不明顯。二分查找更適合處理靜態數據,也就是沒有頻繁的數據插入、刪除操作。

 

二、樹

樹的定義

樹(英語:tree)是一種抽象數據類型(ADT)或是實作這種抽象數據類型的數據結構,用來模擬具有樹狀結構性質的數據集合。它是由n(n>=1)個有限節點組成一個具有層次關系的集合。把它叫做“樹”是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。

舉個例子,每個人都有家族樹,家族樹一般長這樣:

 

家族樹的樣子看起來像一顆正常的樹,而數據結構中的樹則像是一顆倒過來的樹:

 

可以看出,一棵樹有多個節點,上圖中帶〇的字母就是一個節點。

它具有以下的特點:

  • 每個節點有零個或多個子節點;

  • 沒有父節點的節點稱為根節點;

  • 每一個非根節點有且只有一個父節點;

  • 除了根節點外,每個子節點可以分為多個不相交的子樹;

樹的相關概念術語

  • 1)節點<node> 樹中每個元素都叫節點

  • 2)根節點或樹根<root> 樹頂端的節點稱之為根節點,也叫樹根

  • 3)子樹<subTree> 除根節點之外,其他節點可以分為多個樹的集合,叫做子樹,在上圖中,K這個節點可以稱之為一顆子樹,而H、K、L三個節點組合起來也可以叫做一顆子樹

  • 4)節點的度 一個節點直接含有的子樹的個數,稱之為節點的度。比如上圖中B節點的度為3,C節點的度是2,I、J、K、L節點的度是0

  • 5)葉子節點、葉節點、終端節點 度為0的節點叫做葉子節點,也叫葉節點、終端節點,其實就是沒有子節點的節點,或者說沒有子樹的節點

  • 6)雙親節點、父節點 父節點就是一個節點上頭的那個節點,如果一個節點包含若干子節點,那么這個節點就是這些子節點的父節點,也叫雙親節點

  • 7)兄弟節點 擁有相同父節點的節點互稱為兄弟節點

  • 8)樹的度 一棵樹中最大節點的度稱之為樹的度,即樹中哪個節點的子節點最多,那么這個節點的度也就是樹的度

  • 9)節點的層次

    從根這一層開始,根算1層,根的子節點算2層,一直到最下面一層

  • 10)樹的高度、深度 樹的深度是從根節點開始、自頂向下逐層累加(根節點的高度是1)助記:深度從上到下 樹的高度是從葉節點開始、自底向上逐層累加(葉節點的高度是1)助記:高度由下向上 雖然樹的高度和深度一樣,但是具體到某個節點上,其高度和深度通常是不一樣的。

  • 11)堂兄弟節點 堂兄弟節點是同一層,父節點不同,或者說雙親節點在同一層的節點稱之為堂兄弟節點

  • 12)節點的祖先 從根節點到某一節點一路順下來的除了該節點的所有節點都是該節點的祖先節點

  • 13)節點的子孫 以某節點為根的子樹中,任何一個節點都是其子孫,也就是說這個節點下面與這個節點有關的節點都是這個節點的子孫

  • 14)森林 由m棵不相交的樹組成的集合,叫做森林

樹的種類

  • 無序樹:樹中任意節點的子節點之間沒有順序關系,這種樹稱為無序樹,也稱為自由樹;

  • 有序樹

    :樹中任意節點的子節點之間有順序關系,這種樹稱為有序樹;

    • 二叉樹:每個節點最多含有兩個子樹的樹稱為二叉樹;

      • 完全二叉樹:對於一顆二叉樹,假設其深度為d(d>1)。除了第d層外,其它各層的節點數目均已達最大值,且第d層所有節點從左向右連續地緊密排列,這樣的二叉樹被稱為完全二叉樹,其中滿二叉樹的定義是所有葉節點都在最底層的完全二叉樹;

      • 平衡二叉樹(AVL樹):當且僅當任何節點的兩棵子樹的高度差不大於1的二叉樹;

      • 排序二叉樹(二叉查找樹(英語:Binary Search Tree),也稱二叉搜索樹、有序二叉樹);

    • 哈夫曼樹(用於信息編碼):帶權路徑最短的二叉樹稱為哈夫曼樹或最優二叉樹;

    • B樹:一種對讀寫操作進行優化的自平衡的二叉查找樹,能夠保持數據有序,擁有多余兩個子樹。

樹的存儲與表示

順序存儲:

將數據結構存儲在固定的數組中,然在遍歷速度上有一定的優勢,但因所占空間比較大,是非主流二叉樹。二叉樹通常以鏈式存儲。

將樹按照從根到葉節點,從左到右的順序依次存儲到數組里。

 

鏈式存儲:

將樹的數據結構存儲到鏈表中,由於對節點的個數無法掌握,常見樹的存儲表示都轉換成二叉樹進行處理,子節點個數最多為2。

 

常見的一些樹的應用場景

  1. xml,html等,那么編寫這些東西的解析器的時候,不可避免用到樹

  2. 路由協議就是使用了樹的算法

  3. mysql數據庫索引

  4. 文件系統的目錄結構

  5. 所以很多經典的AI算法其實都是樹搜索,此外機器學習中的decision tree也是樹結構

 

參考資料

[1]https://www.cnblogs.com/idreamo/p/9000762.html

[2]https://segmentfault.com/a/1190000014741176

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM