TsingHuaDSA-樹

該文章為清華大學數據結構與算法設計MOOC課程讀書筆記.

1. 數據結構的靜態操作與動態操作

靜態操作(static operation) : search
動態操作(dynamic operation) : insert, remove

效率 Vector List
static O(lgn) 線性
dynamic 線性 O(1)

因為vector和list在靜態或者動態操作中,有一種操作為線性的效率,因此稱他們為線性數據結構

Tree綜合了Vector和List的優點,高效地兼顧了靜態操作與動態操作。雖然具有線性特征,但是不完全是線性結構,因此可以稱之為半線性結構

2. 有根樹與有序樹

有根:樹本身是一個圖,指定圖中某個點為root,該圖則變成有根樹。
有序:在數學上樹的孩子可以無序,但是在計算機對樹的表示中,需要指定出孩子間所存在的一種有序性,因此稱為有序樹。

有序樹

關于樹的節點還有邊的關系,重要的是:它們的數量級同階

3. 樹結構的特性(與圖的區別)

簡單來講,樹是一種連通無環圖.

  1. 連通性(connected):任意兩點之間均有路徑 -> 邊數不會太少
  2. 無環性(acyclic):不存在環路 -> 邊數不能太多

推論:

任何節點v與root之間存在唯一的path

  • 對于半線性的另一種理解:前驅(prev)只有一個,唯一確定,后繼(succ)可以有多個,不確定。

4. 樹的表示

“長子兄弟(1st-child & sibling)”法:每個節點維持兩個指針,一個指針指向第一個child node,另一個指針指向后一個sibling node。

5. 二叉樹

5.1 性質

二叉樹性質

節點數最少與高度h同階,最多可以是高度h的冪指數級。

5.2 表示與實現

二叉樹實現

6. 二叉樹的遍歷

通過遍歷,將半線性結構的樹轉換為完全線性結構的遍歷序列,使某些問題得到簡化。

二叉樹的遍歷

按照節點被訪問的順序分類:先序(pre-order),中序(in-order),后序(post-order),層次(level-order)

特點:每個節點只被訪問一次,復雜度O(n).

6.1 先序遍歷

(1)遞歸實現

按照定義,遞歸實現很簡單。但是在運行棧中,由于每個遞歸實例需要占據固定的空間,遞歸實現的效率其實并不高。

(2)迭代實現1

先序遍歷迭代實現思路
先序遍歷迭代實現

因為stack的LIFO性質,注意節點入棧的順序:先右后左

(3)迭代實現2

雖然迭代實現1非常簡單易懂,但是不利于推廣到中序以及后序遍歷中,因此需要一個更加能夠抓住遍歷本質的算法。

觀察
左側鏈

通過對遍歷過程的觀察可知,遍歷具有這樣一個特點:先沿著左側鏈(left-branch)不斷向下訪問,再自下而上地去訪問右子樹。

因此,實現的思路是:

沿著left-branch自上而下地訪問節點,同時將其對應的右孩子壓入棧中。

實現-part1
實現-part2

6.2 中序遍歷

(1) 遞歸實現

同理

(2) 迭代實現

觀察
左側鏈

觀察可知:最先訪問的節點是最左的節點,總體而言自下而上地進行訪問。因此需要一種LIFO的數據結構-Stack。

因此,實現的思路是:

沿著left-branch找到最左節點并訪問,在這個過程中將沿途的節點壓入棧中。訪問完當前節點后控制權轉移到當前節點的右子樹。

實現

(3) 效率分析 - 分攤分析(Amortized Analysis)法

效率分攤分析

乍一看,兩個循環,每個循環最壞可以達到O(n),因此O(n2)。這好比一個正方形,每個循環分別代表一條邊,因為每條邊的長度O(n),因此面積O(n2)。

但是仔細分析之后,真正的運行時間好比正方形框中的一條條直線,每條直線的長度對應s.push()的操作次數,因為每個節點最多入棧一次,節點數目為n,因此各條直線的累積和為O(n)。相當與平均下來,每條直線的長度只是O(1),因此實際面積(即實際時間復雜度)為O(n).

6.3 后序遍歷

難點
觀察
左側鏈

觀察可知:最先訪問的節點是最左的葉節點,總體而言自下而上地進行訪問。因此需要一種LIFO的數據結構-Stack。

因此,實現的思路是:

沿著left-branch找到最左葉節點并訪問,在這個過程中將沿途的節點以及其右子樹節點壓入棧中。訪問完當前節點后控制權轉移到當前節點的右子樹。

實現-part1
實現-part2

后序遍歷應用 - 表達式樹

前綴表達式構建表達式樹
后序遍歷表達式樹可生成RPN

對表達式樹的后序遍歷可以生成RPN,再根據RPN的算法來計算表達式的值。

6.4 層次遍歷

思路:在之前的三種遍歷中,都有著一種逆序訪問的過程,即后代咸魚祖先被訪問。因此,這三種遍歷的實現都借助與Stack這種具有LIFO特性的數據結構。但是在層次遍歷中,嚴格遵循著父輩先與子輩被訪問,因此需要一種FIFO的數據結構-Queue。

實現

6.5 重構

思想:通過遍歷序列確定root + 左子樹節點 + 右子樹節點,然后遞歸地重構出二叉樹。

(1) 普適方法

二叉樹的重構

通過先序/后序遍歷的序列來確定root,再根據中序遍歷序列來確定左、右子樹。

注意,只通過先序和后續遍歷的序列,是不能夠保證重構的,因為:

若只存在左子樹或者右子樹,是無法確定是左子樹還是右子樹的。

(2) 特殊情況:真二叉樹proper binary tree

由于在真二叉樹中,某個節點要么左右子樹都沒有,要么左右子樹都有,因此可以確定出root,左子樹以及右子樹。

真二叉樹重構
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 樹的概述 樹是一種非常常用的數據結構,樹與前面介紹的線性表,棧,隊列等線性結構不同,樹是一種非線性結構 1.樹的定...
    Jack921閱讀 4,489評論 1 31
  • 基于樹實現的數據結構,具有兩個核心特征: 邏輯結構:數據元素之間具有層次關系; 數據運算:操作方法具有Log級的平...
    yhthu閱讀 4,320評論 1 5
  • 一直以來,我都很少使用也避免使用到樹和圖,總覺得它們神秘而又復雜,但是樹在一些運算和查找中也不可避免的要使用到,那...
    24K男閱讀 6,782評論 5 14
  • 1 序 2016年6月25日夜,帝都,天下著大雨,拖著行李箱和同學在校門口照了最后一張合照,搬離寢室打車去了提前租...
    RichardJieChen閱讀 5,165評論 0 12
  • 前言 總括: 本文講解了數據結構中的[樹]的概念,盡可能通俗易懂的解釋樹這種數據結構的概念,使用javascrip...
    秦至閱讀 819評論 0 6