Trees--Part I
Lai Ah Fur
definition A tree can be defined recursively as the following:
– An empty structure is an empty tree
– If t1,…,tk are disjoint trees, then the structure whose root has its children the roots of t1,…,tk is also a tree.
– Only structures generated by previous two rules are trees.
A tree is a finite set of one or more node such that:– There is a specially node designated node called the root.
– The remaining nodes are partitioned into n>=0 disjoint sets t1,…,tn,where each of the sets is a tree. We call t1,…,tn the subtrees of the root.
Organize a hierarchical representation of objects.
FOREST: after removing the root, all the subtrees become one of set of the forest
Types: binary tree, binary search tree(BST), AVL tree, Red-Black tree,2-3 tree, 2-3-4 tree, B-tree
Terms:path,length,level,height
Each node has to reachable from the root through a unique sequence of arcs, called a path.
The number of arcs in a path is called the length of the path.
The level of a node is the the length of the path from the root to the node plus 1, which is the number of nodes in the path.
The height (depth) of a nonempty tree is the maximum level of a node in the tree.
The empty tree is a legitimate tree of height 0. A single node is a tree of height 1 ( a node is both the
root and a leaf).
Terms (2) DEGREE PARENT/CHILD/SIBLING ANCESTOR/DESCENDANT TERMINAL NODE (LEAF)/
NON-TERMINAL NODE
(internal node) subtree
A
B C
E G H
J K L
M
D
IF
N O
LEVEL=1
LEVEL=5
Compare the linked list and tree?
Your exercise?
Binary tree
A binary tree is a tree whose node have two children(possible empty), and each children is designated as either a left child or a right child.
A complete binary tree: all nonterminal nodes have both their children, and all leaves are at the same level. There are at most 2i nodes at level i+1.
A full binary tree: The tree which has the height k contains 2k-1 nodes.
Representation of a binary tree
List: (A(B(D()())(E()()))(C()(F()())) Array
link
A
B C
D E F
0 1 2 3 4 5 6 7 8
A B C D E F
EXCERSIZE
LIST? ARRAY?
(RULE?)A
B C
D E F
G H I
J
Binary search tree
Also called ordered binary tree For each node n of the tree, all values in its
left subtree are less than value v stored in n, and all values stored in the right subtree are greater than v.
Binary-search-tree (BST)BST property: Let x be a node in a BST.– If y is a node in the left subtree of x, then key[y] < key[x].– If y is a node in the right subtree of x, then key[x] < key[y].
Tree construction: Worst case: O(n2); average case: O(nlgn), where n is the # of nodes
Operations Search, Minimum, Maximum, Predecessor, Successor, Insert, Delete can be performed in O(h) time, where h is the height of the tree.
˙Worst case: h = -(n); balanced BST: h = -(lgn).˙Can we guarantee h = -(lgn)? Balance search trees!!
二元樹的特性
(1)二元樹中 ,階度 (level) 為 i的最大節數是 2i-
1 , i 0≧證明:利用歸納法證明步驟1:樹根是唯一階度為 l 的節點,因此階度
i=l 時最大的節點為 2i-l=2l-l=l步驟2:對所有 j , l j<I≦ ,階度 j 上最大的節數
為 2j-l 。步驟3:由步驟 2 知, i-l 階度上最大的節數為
2i-2 ,而二元樹中每一節的分支度均≦ 2 ,因此階度 i 上最大的節數等於 2 乘以階度 i-l 上的最大節數,也就是 2i-l 。
(2) 若二元樹的深度為 k ,則此二元樹最多的節數目為 2k-l 。
證明:深度為 k 的二元樹,其最多的節數目等於階度 l 到階度 k 中各階度最大節數的和,亦即
2i-1=20+21+……+2k-2+2k-1
=(2k-1)/(2-1)=2k-1
(3)任何一個非空二元樹 T,若終端節點總數為 n0,且分支度等於 2的節點數是 n2,則 n0=n2+1 。
證明: 設 n1 是分支度 (degree) 等於 1 的節點數, n 是總節數,因
為 T 中有節點的分支度都≦ 2 ,所以n=n0+n1+n2--------------------------------------(1) 二元樹中,除了樹根以外,每一個節點都有一個分支
(branch) 指向它,令 B 是分支數。因此 B=n-1---------------------------------------(2)而所有的分支 ( 分支總數 ) 都是由分支度等於 1 或 2 的節點指,故 B=n1+2n2--------------------------------------(3) 由 (2) 與 (3) 得n-1=n1+2n2 代入 (1) 式得n0+n1+n2-1=n1+2n2
移項理可得n0=n2+1
BST node
public class IntBSTNode { protected int key;protected IntBSTNode left, right; public IntBSTNode() { left = right = null; } public IntBSTNode(int el) { this(el,null,null); }public IntBSTNode(int el, IntBSTNode lt,
IntBSTNode rt) { key = el; left = lt; right = rt; } public void visit() { System.out.print(key + "
"); } }
Tree traversal
Tree traversal is the process of visiting each node in the tree exactly one time.
Linearizing a tree For a tree with n nodes, there are n!
different traversals. Most of them are useless.
Breadth-first traversal Depth-first traversal
Breadth-first traversal
Visit each node starting from the lowest (or highest) level and move down (or up) level by level, visit nodes on each level from left to right (or from right to left).
Four possibilities– Result:A,B,C,D,E,F,G,H,I,J
Implementation :– needs a queue.
A
B C
D E F
G H I
J
Top-down, left to right, Breadth-first traversalpublic void breadthFirst() { IntBSTNode p = root; Queue queue = new Queue(); if (p != null) { queue.enqueue(p); while (!queue.isEmpty()) { p = (IntBSTNode) queue.dequeue(); p.visit(); if (p.left != null) queue.enqueue(p.left); if (p.right != null) queue.enqueue(p.right); } } }
Depth-first traversal
Three tasks:– V:visiting a node
– L:traversing the left subtree
– R:traversing the right subtree
Six possible ordered depth-first traversals:– VLR VRL LVR RVL LRV RLV
Preorder traversal: VLR Inorder traversal: LVR Postorder traversal: LRV
EXAMPLE
PREORDER:VLR– A,B,D,G,E,H,C,F,I,J
INORDER: LVR– GDBEHACFJI
POSTORDER: LRV– GDHEBJIFCA
A
B C
D E F
G H I
J
EXCERCISE
PREORDER:VLR– ?
INORDER: LVR– ?
POSTORDER: LRV– ?
A
B C
D E F
G I J
N
H
LLK
Preorder (RECURSIVE)
protected void preorder(IntBSTNode p) {
if (p != null) { p.visit(); preorder(p.left); preorder(p.right); } }
Preorder (ITERATIVE)public void iterativePreorder() { IntBSTNode p = root; Stack travStack = new Stack();if (p != null) { travStack.push(p); while (!travStack.isEmpty()) { p = (IntBSTNode) travStack.pop(); p.visit(); if (p.right != null) travStack.push(p.right); if (p.left != null) travStack.push(p.left);
// left child pushed after right ,to be on the top of the
Stack; } } }
Inorder (RECURSIVE)
protected void inorder(IntBSTNode p) {
if (p != null) { inorder(p.left); p.visit(); inorder(p.right); } }
Inorder(iterative)public void iterativeInorder() { IntBSTNode p = root; Stack travStack = new Stack(); while (p != null) { while(p != null) { // stack the right child (if any) if (p.right != null) // and the node itself when going travStack.push(p.right); // to the left; travStack.push(p); p = p.left; } p = (IntBSTNode) travStack.pop(); // pop a node with no
left child while (!travStack.isEmpty() && p.right == null) { // visit it
and all p.visit(); // nodes with no right child; p = (IntBSTNode) travStack.pop(); } p.visit(); // visit also the first node with a right child (if
any); if (!travStack.isEmpty()) p = (IntBSTNode) travStack.pop(); else
p = null; // is over} }
Postorder (RECURSIVE)
protected void postorder(IntBSTNode p) { if (p != null)
{ postorder(p.left); postorder(p.right); p.visit(); } }
public void iterativePostorder() { IntBSTNode p = root; Stack travStack = new Stack(); //trace 用堆疊output = new Stack(); // 結果堆疊if (p != null) { // left-to-right postorder = right-to-left preorder LRV
travStack.push(p); while (!travStack.isEmpty()) { p = (IntBSTNode) travStack.pop(); output.push(p); if (p.left != null) travStack.push(p.left); if (p.right != null)
travStack.push(p.right); } //whilewhile (!output.isEmpty()) { p = (IntBSTNode) output.pop(); p.visit(); } } }
Postorder (iterative-1)
public void iterativePostorder() { BSTNode p = root,q=root; Stack travStack = new Stack(); while ((p != null) { for(;p.left!=null ;p=p.left) travStack.push(p); while ((p != null)&& (p.right==null ||
p.right==q)) { p.visit(); q=p; //q has been visited If (travStack.isEmpty()) return ; p= (BSTNode) travStack.pop(); }//while travStack.push(p); p=p.right; }//while }
Postorder (iterative-2)
q
P
Both has visited
search
protected IntBSTNode Search(IntBSTNode p, int el)
{ while (p != null) if (el == p.key) return p; else if (el < p.key) p = p.left; else p = p.right; return null; }
INSERT (BST)
15
4 15
4
2015
NULL
(A)insert 15 (B)insert 4 (c) insert 20
15
4 20
1715
4 20
17
19 15
4 20
17
19(D)insert 17
(E)insert 19 (F)
CREATE a BST USING INSERT
Given the following integer sequence, please create the BST: 50,67,23,45,87,99,34,12,44,90,100,10,11,35,111
insertpublic void insert(int el) {IntBSTNode p = root,prev = null; while (p != null) { // find a place for inserting new node;
prev = p; if (p.key < el) p = p.right; else p = p.left; } if (root == null) // tree is empty; root = new IntBSTNode(el);else if (prev.key < el) prev.right = new IntBSTNode(el); else prev.left = new IntBSTNode(el); }
Delete a node
Delete by merging Delete by copying
Delete 16
Free the spaceDelete 20
Free the space
Delete by merging
Make one tree out of the two subtrees of the node, then attach it to the node’s parent
How to merge these subtrees?– Find in the left subtree the node with the greatest value
and make it a parent of the right subtree
– Find in the right subtree the node with the lowest value and make it a parent of the left subtree
Drawback: increase the height of the tree (It is certainly far from perfect!)
Delete by merging
Delete node
node left
node right
node
Rightmost node of the left subtree
Root
node left
node right
node left
Root
node right
node left
Delete by merging (example)
Delete 15
Delete 15
Details of deleting by merging
Delete by copying
Proposed by Hilbard & Knuth If the node has 2 children, it can be replaced
with its immediate predecessor (or successor)
This algorithm doesn’t increase the height of the tree, but it may cause the unbalanced problem.
Delete by copying (example)A
B C
D E F
G I J
N
H
MLK
Delete BOther?
A
M C
D E F
G I J
N
H
LK P
P
Details of deleting by copying
Deleting node (1)
prev
node
p
prev
node
p
prev
nodep
Case 1
Case 2 if (node.right == null) //node has no right child; node = node.left; else if (node.left == null) //no left child for
node; node = node.right; if (p == root) root = node; else if (prev.left == p) prev.left = node; else prev.right = node;
Deleting node (2)prev
nodep
previous
tmp
if (node.right != null &&node.right != null){// node has both children; BSTNode tmp = node.left; BSTNode previous = node; // 1. while (tmp.right != null) { // 2. find the rightmost position in the left subtree of node; previous = tmp; // tmp = tmp.right; //} node.key = tmp.key; // 3. overwrite the reference // of the key being deleted; if (previous = = node) // if node's left child's right subtree is null
previous.left = tmp.left;; else previous.right = tmp.left; }…….
prev
nodep
previous
tmp
exercise Delete 16, 請用三種
做法 insert 28 Design the class of
the node of this BST (java)
20
16 25
10 17 30
5 18 40
35
15
73 13
Balancing a tree Stream of data: 5,1,9,8,7,0,2,3,4,6 Array of sorted data:0,1,2,3,4,5,6,7,8,9
4
1
0 2
4 4
1
public void balance(BaseObject data[], int first, int last) { if (first <= last) { int middle = (first + last)/2; insert(data[middle]); balance(data,first,middle-1); balance(data,middle+1,last); } }
Drawback: All data must be put in an array beforethe tree can be created.
4
1
0 2
3
7
5 8
6 9
二元樹的計數 (Counting Binary Tree) n 個節可排成多少個不
同的二元樹?(1) n=0 或者 n=1 ,祇有
一種二元樹(2) n=2 時,存在兩種不
同的二元樹。(3) n=3 時,存在五種不
同的二元樹。
二元樹的計數 n 個節點所能排成不同二元樹的個數可以
列公式求出:
中序順序及前序順序決定一個二元樹例:有一個前序順序為: ABCDEFGHI 中序
順序為: BCAEDGHFI 則其相對應的二元樹為何?
解:前序追蹤的第一個字元 A 一定是樹根,再從中序追蹤知在 A 之前的節點 BC 是左子樹,在 A 之後的節點 EDGHFI 是右子樹,由此我們可以大略的建立出其相對應的二元樹形狀。
再由前序追蹤的第二個字元 B 知其為樹根,從中序追蹤知 B 的左子樹是空的,右子樹是C ,因此其進一步結果如下:
同理類推,整個二元樹如下所示:
一對中序順序 (inorder sequence) 及前序順序(preorder sequence) 可唯一決定一個二元樹。
一對中序順序 (inorder sequence) 及後序順序(postorder sequence) 亦可唯一決定一個二元樹。
一對前序順序 (preorder sequence) 及後序順序(postorder sequence)無法決定唯一一個二元樹
exercise
有一個前序順序為: ABDEFG 中序順序為: BAEDGF 則其相對應的二元樹為何?
a
b d
e f
g
中序順序及後序順序決定一個二元樹
INORDER: GDBEHACFJI POSTORDER: GDHEBJIFCA
QUIZ
有一個後序順序為: CBGHEJFDA 中序順序為: BCAGEHDJF 則其相對應的二元樹為何? PREORDER? Represent the tree using list and array?
有一個前序順序為: ABDGCEHIF 中序順序為: DGBAHEICF 則其相對應的二元樹為何? POSTORDER?
COMPARE : BST AND HEAP
樹化為二元樹的方法 一般採用“ leftmost-child-next-right-
sibling 方式將樹化為二元樹,其步驟如下:
(1) 將節點的所有兄弟用平行線連接起來。
(2) 刪除掉所有與子點間的鏈結,祇保留與最左子間的鏈結。
(3) 順時針旋轉 45 度。
樹化為二元樹範例
樹林 (forest) 化為二元樹的方法 將樹林中的每一個樹利 leftmost-child–
next-right-sibling 法化為二元樹。– 利用上述< i >< ii >方法將各二元樹合併
成一個二元樹。 另法:首先將各樹的樹根由左而右連接
起來。利用 leftmost-child-next-right-sibling 法將其化為二元樹。
例:有三個樹的樹林如下,將其化為二元樹表示之。
解:首先將各樹的樹根連接起來如下所示:接著,利用 leftmost-child–next-right-sibling 方法轉換
樹林 (forest) 化為二元樹
THREADED TREE Trees whose nodes use threads are called threaded trees Threads are reference to the predecessors and successors
of the node according to an inorder traversal.( 與 java 的thread 不同 ,which is a light weight process)
Four referencce fields are needed for each node in the tree, but by overloading existing reference fields, left or right references which are references to children can also be used as references to predecessors or successors – Left reference is either a reference to the left child or to
the predecessor– right reference is either a reference to the right child or
to the successor
Element of the node
(inorder traversal only use successor)
Left predecessor key successor rightLeft predecessor key successor right
•由 predecessor 決定 left 是否為 predecessor reference 或 left reference
Node of the threaded tree (only 處理successor)
class IntThreadedTreeNode {protected int key;protected boolean successor;//only for successor referenceprotected IntThreadedTreeNode left, right;public IntThreadedTreeNode( ) { left = right = null; successor = false;}public IntThreadedTreeNode(int el) { this(el,null,null);}public IntThreadedTreeNode(int el, IntThreadedTreeNode lt,IntThreadedTreeNode rt) { key = el; left = lt; fight = rt; successor = false;}public void visit( ) { System out print(key + “ “);}}
Insertion of the threaded tree (only 處理successor)public void threadedInsert(int el) {IntThreadedNode newNode = new IntThreadedNode(el);If (root == null) { // tree is empty root = newNode; return;}IntThreadedNode p = root, prev = null;while (p != null) { // find a place to insert newNode; prev = p; if (el < p.key) p = p.left; else if (!p.Successor) // go to the right only if it is p = p.right; // a descendant, not a successor; else break; // don't follow successor link; } if (el < prev.key) { // if newNode is left child of prev.left = newNode; // its parent, the parent newNode.Successor = true;// also becomes its successor; newNode.right = prev; } else if (prev.Successor) { // if parent of the newNode newNode.Successor = true;// is not the rightmost node, prev.Successor = false; // make parent's successor newNode.right = prev.right; // newNode’s successor, prev.right = newNode;} else prev.right = newNode; // otherwise it has no successory;} //
Insertion of the threaded tree: Insert 35
60
40
15
77
88 NullNull
35Null newnode
prev
Inorder of the threaded treepublic class IntThreadedTree {private IntThreadedNode root;public IntThreadedTree( ) { root = null;}protected void threadedInorder( ) {IntThreadedNode prev, p = root;if (p != null) { //process only nonempty trees; while (p.left != null) // go to the leftmost node; p = p.left;while (p !=null) { p.visit( ); prev = p; p = p.right; //go to the right node and only if (p != null && !prev.successor) // if it is a descendant while (p.left != null) // go to the leftmost node, p = p.left; // otherwise visit the successor;
}// while (p !=null) } }
Example of THREADED TREE
60
40
15
77
88 NullNull
Dangling
BINARY EXPRESSION TREE (3+4)*(5+6/7)
*
+
3 4
+
5 /
6 7
由中序式建立算式樹輸入運算式的中序式,建立算式樹 由運算式的中序式來建立算式樹時,必須利用中序式轉換為後序式的過程中,來建立算式樹,而且要使用一個指標堆疊,假設此堆疊為 sopd,則建立算式樹的演算法如下:
1. 在中序轉換為後序的過程中,如果要輸出一個運算元時,則建立一個新節點,儲存此運算元,其左右兩指標欄設定為 NULL ,並且將節點指標 push置入指標堆疊 sopd中。
2. 在中序轉換為後序的過程中,如果要輸出一個運算子時
– 若此運算子之 priority<=Sopr 則由 Sopr pop 出運算子且建立新節點,儲存此運算子,並且由指標堆疊 sopd 中 pop 彈出一個指標,存入新節點的右指標欄,再由 sopd中彈出一個指標,存入新節點的左指標欄,然後將新節點指標再 push 置入 sopd堆疊中。
– 否則直接將此運算子 push 到 Sopr
4*3+5*8/2-7
null 4 null null 3 null
(1)~(3)
Sopd *Sopr
*
null 4 null null 3 null
Sopd+
Sopr
null 5 null
(4)~(5)
4*3+5*8/2-7
*
null 4 null null 3 null
Sopd*+
Sopr
null 5 null
null 8 null
*
null 4 null null 3 null
Sopd
null 5 null null 8 null
*
null 2 null
/+
Sopr
(6)
(7)~(8)
*
null 4 null null 3 null
Sopd
null 5 null null 8 null
* null 2 null
+
Sopr
(9)-1
4*3+5*8/2-7
/
*
null 4 null null 3 null
Sopd
-Sopr
(9)-2~(10)
4*3+5*8/2-7
null 5 null null 8 null
* null 2 null
/
+ null 7 null
*
null 4 null null 3 null
Sopd
Sopr
(9)-24*3+5*8/2-7
null 5 null null 8 null
* null 2 null
/
+ null 7 null
-
(11)
由前序式建立算式樹 如果輸入是運算元,則建立節點儲存運算元,停止往下建樹。
如果輸入是運算子,則建立節點儲存運算子,並且以遞迴方式先往下建左子樹,再建右子樹。
-+* 4 3 / * 5 8 2 7
*
null 4 null null 3 null
+
-
Algorithm for 前序式建立算式樹Expnode createtree (Expnode p){ p=new Expnode(); p.key= 輸入內容 ;// 依序 if (operands(p.key)) p.left=p.right=null; else if (operators(p.key)) { p.left=createtree(p.left); p.right=createtree(p.right); } return (p); }//please complete the previous algorithm
由後序式建立算式樹 如果輸入是運算元,則建立節點儲存,
停止往下建樹。 如果輸入是運算子,則建立節點儲存,
並且以遞迴方式先往右邊建子樹,再往左邊建子樹。
4 3 * 5 8 * 2 / + 7 -
null 5 null null 8 null
* null 2 null
/
+ null 7 null
- (1)(2)
(3)-(4)
算式樹求值 以 postorder 方式追蹤算式樹 , 每當追蹤
到一個 operator 及二個 operand, 進行運算– (1) 追蹤到 operand, push into operand_stack– (2) 追蹤到 operator, 由 operand_stack pull 二
個 operand, 進行運算 , 運算 result push into operand_stack
算式樹求值 example
null 5 null null 8 null
*
null 2 null
-
*
null 2 null null 12 null
/
null 2 null
85
*
40
5,8 12,2 21240
/6
40
-
34
Operand_stack
Exercise II:建立 binary算式樹 ,求值 Input: 後序式 / 中序式 / 前序式 建立算式樹 算式樹求值 印出其他 order Option: draw this tree