• 沒有找到結果。

演算法 Algorithm - 臺北市立建國高級中學

N/A
N/A
Protected

Academic year: 2023

Share "演算法 Algorithm - 臺北市立建國高級中學"

Copied!
1
0
0

加載中.... (立即查看全文)

全文

(1)

演算法 與 資料結構 Algorithm and Data Structure

- Tree

Tree,樹

大家都知道種樹是非常有益於人體健康的,但在程式中卻不是這麼一回事……啊!我要說的 不是這個。總之樹是一種很特別的圖(acyclic connected graph),因此有很特殊的性質方便 我們對他做探討。除此之外,非常多資料結構也是以樹的型態呈現的,方便我們處理各種問 題。

這次探討的主要是有根樹(rooted tree)。

Definition

‧節點node:樹中的點(vertex)。

‧根(根節點)root node:在有根樹中特別指定的一個點,是「深度為0的點」。

‧有根樹rooted tree:有定出根節點的樹。

‧葉子leaf node:沒有任何子節點的節點。

‧子節點child:若u和v相連且v的深度比u的深度多1,則v是u的child

‧子樹subtree:在原本的tree中,以某一節點為根,並取所有在它之下的邊得到的樹。

‧父節點parent node:若v是u的child,則v是u的parent node。

‧兄弟節點sibling:v和u有相同的parent node則稱v和u互為sibling。

‧祖先ancestor:若v位於u到root的路徑上或v是root,則稱v為u的ancestor。

‧後代descendant:若u是v的ancestor,則v是u的descendant。

‧度數degree:Deg(v)代表v的child數。

‧深度depth:一個節點v深度就是root到v的距離。

‧高度height:所有節點的depth中最深的一個就是這棵樹的height。

‧k元樹k-ary tree:若一個樹每個non-leaf node的degree都k,則稱之k元樹。特別地,

2-ary tree就是我們所謂的binary tree。

‧叢林forest:若干個樹。(其實也就是acyclic graph)

‧tree的等價定義:

1. G是一tree。

2. G中的任兩個點恰有一條simple path相連。

3. G是connected graph,但若任一條邊被移除,G變為disconnected。

4. G是connected graph,且| E | = | V |  1。

5. G是acyclic graph,且| E | = | V |  1。

6. G是acyclic graph,但如果任一條邊被加到G中,則G變成cyclic。

樹的表示法 Representation

要儲存一棵tree的資料,一般會考慮這顆樹的特性,再決定要以什麼要的資料結構儲存他。

首先考慮最單純的二元樹binary tree,二元樹中每個點的degree都小於等於2,所以我們可

建中資訊培訓講義 (六) - by kelvin

(2)

以在每個點中用兩個指標*LeftChild和*RightChild來儲存binary tree的資料。

struct BinaryTreeNode { DataType Data;

struct BinaryTreeNode *ParentNode;

struct BinaryTreeNode *LeftChild;

struct BinaryTreeNode *RightChild;

};

除此以外,若知道這棵binary tree是complete binary tree,或只要是不歪斜的或者深度不深,

可以直接用陣列來模擬binary tree。可以以A[1]為root node,對於每個節點A[x],A[2x]是其 left child,A[2x+1]是其right child,A[ x/2 ]則是其parent node。

比較一般地,若已知是k-ary tree,可以用一個陣列儲存child的資料:

struct KaryTreeNode { DataType Data;

struct TreeNode *ParentNode;

struct TreeNode *Child[1…k];

};

更一般地,如果這棵樹沒有特別的規範,只好用left-child-right-sibling的方法來紀錄它,也 就是紀錄每個節點其最「左邊」的child node,以及緊鄰於其右邊的sibling。這樣雖然沒有這麼 方便,卻是紀錄任意的樹時最合理的方法(尤其就memory usage來說)。

struct TreeNode { DataType Data;

struct TreeNode *ParentNode;

struct TreeNode *LeftChild;

struct TreeNode *RightSibling;

};

至 於 若 不 是 rooted tree, 只 是 一 般 connected acyclic graph, 只 需 要 用 普 通 的 graph representation即可,而且通常用的是adjacency list,因為| E | = | V |  1的性質,使得使用 adjacency list時的一些O(m)複雜度,顯著地勝過使用adjacency matrix的O(n2)。

Traversing a Binary Tree 二元樹的走訪

對於binary tree,我們可以用以下三種特定方式來traverse(走訪、遍歷)這棵樹。

‧前序表示法pre-order:root -> left-subtree -> right-subtree

‧中序表示法in-order:left-subtree -> root -> right-subtree

‧後序表示法post-order:left-subtree -> right-subtree -> root 而有趣的是,給定任兩個表示法,我們可以

思考:1. 給定pre-order和in-order,如何求出post-order?

2. 給定post-order和in-order,如何求出pre-order?

建中資訊培訓講義 (六) - by kelvin

(3)

3. 給定pre-order和post-order,如何求出in-order?

Binary Search Tree 二元搜尋樹

Binary search tree(BST)是一種樹狀資料結構,每個node都存有一個元素的資料。它的特性

是對每個node,其left subtree中所有元素都小於等於該node中儲存的元素值,而right

subtree中的所有元素都大於該node中儲存的元素值。這樣的性質可以幫助它進行幾種操作:

搜尋 - O(h), hBST高度

可以利用遞回的方式,(當然也是可以用while寫成),從root node開始搜尋,若所搜尋 元素比當前node中元素小便往右走,若比較大則往左走,若相等則搜尋完成。

新增資料 - O(h)

和搜尋時的方法類似,從root node開始往下traverse,並把新的node新增在最終的leaf下。

刪除資料 - O(h)

刪除節點比較麻煩一點,需要考慮三種情況(假設欲刪除節為x):

1. 當x為leaf node時,直接刪去即可。

2. 當x只有一個child,直接把x的parent連到x的child,並把x刪去。

3. 當x有兩個children時,我們選取x之左子樹中最右邊的一個元素y(亦可用右子樹中

最左邊的元素)替代x,並把原本的y刪除。(想想看為什麼可以這樣替換?)

但普通的BST有個很大的問題,就是假設不是預先對所有資料建立好的,而是把元素一個 一個新增到BST的,會造成BST的不平衡,變成skew tree,也就是各個葉子的深淺差距很 大,而最差的情況自然就是當輸入的資料是由小到大或由大到小,全部的點都會在同一條鏈 上,變成一個深度為n的樹。這樣會使得搜尋的複雜度變高,而失去原有的O(lgn)性質。

為了解決這個問題,除了先對要建立成BST的資料random過以外(可以證明random建立的 BST,其表現還算接近O(lgn)),有所謂的平衡樹balanced tree(例如AVL tree, red black

tree),可以保證各個葉子的深度相差不會太大,這樣可以保證資料結構的O(lgn)搜尋性質。

但這些資料結構較為困難,這邊就不介紹。(其實是我自己也不會寫哈哈哈哈……)

Binary Search Tree 二元計算樹

我們在做的四則運算其實可以用tree來表示,例如右圖就是計算式(1+

((2+3)*4))的表示方式。

能夠發現我們平常的表示方法其實是in-order tree walk得到的。但若要 方便計算的話可以把它改以表示為post-order。

例如這個例子中post-order表示就會是:123+4*+

想想看:給定一個in-order的算式,要怎麼表示為post-order呢?

啊啊

其實tree的應用非常多,很多資料結構為了能進行一些O(lgn)的操作,都會使用這樣的樹狀 資料結構,其中很重要的包括heap, segment tree, suffix trie, tournament tree等等……總之,

tree看似基本,其實卻有很多很實用(而且往往超難code = =)的應用!

建中資訊培訓講義 (六) - by kelvin

(4)

總之想要成為資料結構魔人的話,tree無疑是最重要的之一!(嘖嘖,好可怕的志向)

[完]

建中資訊培訓講義 (六) - by kelvin

參考文獻

相關文件

一、君子喻於義,小人喻於利 意思是說君子能夠領悟的是道義,小人能夠領悟的是利益。這裡的“喻”字,有 “領悟”、“明白”的意思。具體含義是指君子與小人價值取向不同,君子於事 必辨其是非,小人於事必計其利害。所謂“利”,是指金錢、財富等物質利益; 所謂“義”,是指道義、正義等超越物質利益之上的道德價值。君子行事按“義

第1~7題包含單選題與非選題。單選題每題7分。非選題依各題配分。 (一)1 ~ 3 題為題組 閱讀下文,回答1-3題。 我叫「喬」,這是同事米爾頓.大衛森對我的稱呼。他是程式設計師,而我是電腦程式。我是萬 用自動機複合體的一部分,與遍布世界的其他部分連成一體。我什麼都知道,幾乎什麼都知道。