1 / 75

Trees, Part 1 - PowerPoint PPT Presentation

Trees, Part 1. CSCI 2720 University of Georgia Spring 2007. Tree Properties and Definitions. Composed of nodes and edges Node Any distinguishable object Edge An ordered pair <u,v> of nodes Illustrated by an arrow with tail at u and head at v u = tail of <u,v>

I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.

PowerPoint Slideshow about ' Trees, Part 1' - austin

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.

- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript

Trees, Part 1

CSCI 2720

University of Georgia

Spring 2007

• Composed of nodes and edges

• Node

• Any distinguishable object

• Edge

• An ordered pair <u,v> of nodes

• Illustrated by an arrow with tail at u and head at v

• u = tail of <u,v>

• v = head of <u,v>

• Root

• A distinguished node, depicted at the top of the tree

• A single node, with no edges, is a tree. The root of the tree is its unique node.

• Let T1, …, Tk (k >=1) be trees with no nodes in common, and let r1, …,rk be the roots of those trees, respectively. Let r be a new node. Then there is a tree T consisting of the nodes and edges of T1, …, Tk , the new node r and the edges <r,r1>,…,<r,rk>. The root of T is r and T1, …,Tk are called the subtrees of T.

• r is called the parent of r1, …, rk

• r1, …, rk are the children of r

• r1, …, rk are the siblings of one another

• Node v is a descendant of u if

• u = v or

• v is a descendant of a child of u

• A path exists from u to v.

Before:

The trees T1, …, Tk, with roots r1, …, rk

After:

new tree T rooted at r with subtreesT1, …, Tk,

• You can get from u to v by following a sequences of edges down the tree, starting with an edge whose tail is u, ending with an edge whose head is v, and with the head of each edge but the last being the tail of the next.

• Every node is a descendant of itself.

• If v is a descendant of u, then u is an ancestor of v.

• Properdescendants:

• The descendants of a node, other than the node itself.

• Proper ancestors

• The ancestors of a node, other than the node itself.

• Leaf

• a node with no children

• Num_nodes = Num_edges + 1

• Every tree has exactly one more node than edge

• Every edge, except the root, is the head of exactly one of the edges

• Height of a node in a tree

• Length of the longest path from that node to a leaf

• Depth of a node in a tree

• Length of the path from the root of the tree to the node

root

root

leaf

leaf

leaf

leaf

leaf

leaf

u

v

A path from u to v. Node u is the ancestor of node v.

• w has depth 2

• u has height 3

• tree has height 4

u

w

• Ordered v. unordered

• Binary tree

• Empty v non-empty

• Full

• Perfect

• Complete

• Have a linear order on the children of each node

• That is, can clearly identify a 1st, 2nd, …, kth child

• An unordered tree doesn’t have this property

• An ordered tree with at most two children for each node

• If node has two child, the 1st is called the left child, the 2nd is called the right child

• If only one child, it is either the right child or the left child

Two trees are not equivalent …One has only a left child; the other has only a right child

• Convenient to define an empty binary tree, written , for use in this definition:

• A binary tree is either  or is a node with left and right subtrees, each of which is a binary tree.

• Has no nodes with only one child

• Each node either a leaf or it has 2 children

• # leaves = #non-leaves + 1

• A full binary tree in which all leaves have the same depth

• Perfect binary tree of height h has:

• 2h+1 -1 nodes

• 2h leaves

• 2h -1 non-leaves

• Interesting because it is “fully packed”, the shallowest tree that can contain that many nodes. Often, we’ll be searching from the root. Such a shallow tree gives us minimal number of accesses to nodes in the tree.

• Perfect binary tree requires that number of nodes be one less than a power of 2.

• Complete binary tree is a close approximation, with similar good properties for processing of data stored in such a tree.

• Complete binary tree of height h:

• Perfect binary tree of height h-1

• Add nodes from left at bottom level

• A complete binary tree of height 0 is any tree consisting of a single node.

• A complete binary tree of height 1 is a tree of height 1 with either two children or a left child only.

• For height h >= 2, a complete binary tree of height h is a root with two subtrees satisfying one of these two conditions:

• Either the left subtree is perfect of height h-1 and the right is complete of height h-1 OR

• The left is complete of height h-1 and the right is perfect of height h-2.

• Forest

• A finite set of trees

• In the case of ordered trees, the trees in the forest must have a distinguishable order as well.

• Parent(v)

• Children(v)

• FirstChild(v)

• RightSibling(v)

• LeftSibling(v)

• LeftChild(v)

• RightChild(v)

• isLeaf(v)

• Depth(v)

• Height(v)

• Return the parent of node v, or  if v is the root.

• Return the set of children of node v (the empty set, if v is a leaf).

• Return the first child of node v, or  if v is a leaf.

• Return the right sibling of v, or  if v is the root or the rightmost child of its parent.

• Return the left sibling of v, or  if v is the root or the leftmost child of its parent

• Return the left child of node v;

• Return  if v has no left or right child.

• Return the right child of node v;

• return  if v has no right child

• Return true if node v is a leaf, false if v has a child

• Return the depth of node v in the tree.

• Return the height of node v in the tree.

• Binary tree associated with an arithmetic expression

• internal nodes: operators

• external nodes: operands

• Example: arithmetic expression tree for the infix expression:

• (2 × (a − 1) + (3 × b))

• Note: infix expressions require parentheses

• ((2 x a) – (1+3) * b) is a different expression tree

function Evaluate(ptr P): integer

/* return value of expression represented by tree with root P */

if isLeaf(P) return Label(P)

else

xL <- Evaluate(LeftChild(P))

xR <- Evaluate(RightChild(P))

op <- Label(P)

return ApplyOp(op, xL, xR)

- Evaluate (A)

Evaluate(B)

Evaluate(D) => 2

Evaluate(E)

Evaluate(H)=> a

Evaluate(I) => 1

ApplyOp(-,a,1) => a-1

ApplyOp(x,2,(a-1)) => (2 x (a-1))

Evaluate(C)

Evaluate(F) => 3

Evaluate(G) => b

ApplyOp(x,3,b) => 3 x b

ApplyOp(+,(2 x (a-1)),(3 x b)) =>

(2 x (a-1)) + (3 x b)

A

B

C

F

G

E

D

H

I

• Traversal = any well-defined ordering of the visits to the nodes in a tree

• PostOrder traversal

• Node is considered after its children have been considered

• PreOrder traversal

• Node is considered before its children have been considered

• InOrder traversal (for binary trees)

• Consider left child, consider node, consider right child

procedurePostOrder(ptr P):

foreach childQ of P, in order, do

PostOrder(Q)

Visit(P)

PostOrder traversal gives:

2, a, 1, -, x, 3, b, x, +

Nice feature: unambiguous … doesn’t need parentheses

-- often used with scientific calculators

-- simple stack-based evaluation

procedurePostOrderEvaluate(e1, …, en):

forifrom 1 to ndo

ifei is a number, then push it on the stack

else

• Pop the top two numbers from the stack

• Apply the operator ei to them, with the right operand being the first one popped

• Push the result on the stack

procedurePreOrder(ptr P):

Visit(P)

foreach childQ of P, in order, do

PreOrder(Q)

PreOrder Traversal:

+, x, 2, -, a, 1, x, 3, b

Nice feature: unambigous, don’t need parentheses

-- Outline order

procedureInOrder(ptr P):

// P is a pointer to the root of a binary tree

if P =  then return

else

InOrder(LeftChild(P))

Visit(P)

InOrder(RightChild(P))

InOrder Traversal:

2 x a – 1 + 3 x b

• Use binary trees to store data that is linearly ordered

• A linear order is a relation < such that for any x, y, and z

• If x and y are different then either x<y or y<x

• If x<y and y < z then x< z

• An inorder traversal of a binary search tree yields the labels on the nodes, in linear order.

50

60

30

70

20

40

Inorder traversal yields: 20, 30, 40, 50, 60, 70

A

C

B

F

D

E

A, B, C, D, E, F

• General Binary Tree

• General Ordered Trees

• Complete Binary Trees

Binary Trees:“Natural Representation”

• LC : contains pointer to left child

• RC: contains pointer to right child

• Info: contains info or pointer to info

• LeftChild(p), RightChild(p): (1)

• Parent(p): not (1) unless we also add a parent pointer to the node

LC info RC

• Parent(p): not (1) unless we also add a parent pointer to the node

• Can use xor trick to store parent-xor-left,

• Parent-xor-right, and use only two fields

• Binary tree: array of 2 ptrs to 2 children

• Ternary tree: array of 3 ptrs to 3 children

• K-ary trees: k pointers to k children

• What if there is no bound on the number of children?

• Use binary tree representation of ordered tree:

First Info Right

Child Sibling

Binary tree rep

Example

A

A

B

B

C

D

C

E

D

F

E

F

G

H

I

G

H

I

Complete Binary Tree: Implicit representation

Binary Search Trees, example

50

70

30

20

40

60

0 1 2 3 4 5

[50 | 30 | 70 | 20 | 40| 60]

• isLeaf(i) :

• 2i + 1 >= n

• LeftChild(i) :

• 2i + 1 (none if 2i + 1 >= n)

• RightChild(i) :

• 2i + 2 (none if 2i + 2 >= n)

• LeftSibling(i):

• i-1 (none if i=0 or i is odd)

• RightSibling(i):

• i+1 (none if i=n-1 or i is even)

• Parent(i):

• floor((i-1)/2) (none if i=0)

• Depth(i):

• floor (lg(i+1))

• Height(i):

• ceil(lg((n+1)/(i+1))) - 1

• Stack-based Traversals

• Scanning in Constant Space

• Level-Order Traversal

procedure Traverse(ptr P)

//traverse binary tree with root P

if P !=  then

PreVisit(P)

Traverse(LC(P))

InVisit(P)

Traverse(RC(P))

PostVisit(P)

Provide functions for Previsit(P),Invisit(P), and/or PostVisit(P)

Some can be null (do-nothing) functions

Implements all 3 traversal (inorder, preorder, postorder) … just depends on which functions have “real” bodies

Stack-based Traversals

procedure Traverse(ptr P)

//traverse binary tree with root P

if P !=  then

PreVisit(P)

Traverse(LC(P))

InVisit(P)

Traverse(RC(P))

PostVisit(P)

O(n) time to visit all nodes

Height of the stack is proportional to the height of the tree

If structure is large, height of stack can be problematic

For pre-order and inorder, one of the calls to Traverse is tail-recursive & we can eliminate it

Stack-based Traversals

procedure Traverse(ptr P)

//traverse binary tree with root P

if P !=  then

PreVisit(P)

Traverse(LC(P))

InVisit(P)

Traverse(RC(P))

PostVisit(P)

procedure Traverse(ptr P)

//traverse binary tree with root P

While P !=  do

PreVisit(P)

Traverse(LC(P))

InVisit(P)

P <- RC(P)

- demo difference in stack heights for small example

Stack-based Traversals

Stack during recursive in-order traversal

D

E

B

F

C

A

C

F

A

C

B

E

D

A

B

C

D

E

F

Depends on “max left height” -- proportional to the height of the corresponding ordered tree

OrdHt(P):

if P = 

0

if LC(P) == 

OrdHt(RC(P))

Otherwise

Max(1 + OrdHt(LC(P)), OrdHt(RC(P)))

Example: calculate ordered height for medium-sized BT

Height of stack for “improved” algorithm

• Use same ideas from link-inversion traversal of list

• Stores contents of stack in tree itself

• Use tag bit to indicate whether to follow LC or RC at this point

tag LC info RC

• Tag also indicates:

• When recursive invocation finishes

• Which of the two recursive calls in the body caused that invocation

• Where in the body the execution should continue

// initially Q points to root

P <- 

repeat forever

// descend to left afap

while Q !=  do

PreVisit(Q)

Tag(Q) <- 0

descend_to_left

// ascend from right afap

while P !=  and Tag(P) == 1 do

ascend_from_right

PostVisit(Q)

if P == then return

else

ascend_from_left

InVisit(Q)

Tag(Q) <- 1

descend_to_right

ascend_from_left

descend_to_right

Ascend_from_right

Ascend/ Descend functions

• Demo on small - midsized tree

• Actually, don’t really need to store the tag bits in the nodes themselves … can just place them in a stack at the time the algorithm runs!

• Can visit each node in a tree, at least once, using only a few temporary variables

• Catch: not a standard traversal order

• In fact - visits each node three times

• “walks around” the tree, keeping nodes and edges to its left

50, 30, 20, 20, 20, 30, 40, 40, 40, 30, 50, 70, 60, 60, 60, 70, 70, 50

50, 30, 20, 40, 70, 60

20 30 40 50 60 70

20 40 30 60 70 50

Trees, example

50

70

30

20

40

60

procedure ConstantSpaceScan(ptr Q):

 is a distinguished value

Initially Q points to root

P <- 

While Q !=  do

If Q !=  then

Visit(Q)

else

P <-> Q

• Inorder traversal is a common operation

• Require extra memory to store a stack

• Can’t start from an arbitrary node … need to start at the root

• Want to make it easier to find inorder successor of a node

• Inorder predecessor would be nice too!

100

200 | 40 | 300

40

200

30

50

300

400 | 30 | 

 | 50 | 500

400

500

10

60

 | 10 | 600

 | 60| 

600

20

 | 20 | 

Hmmmm … that’s quite a few null pointers!

In fact, in binary tree with n nodes, there are 2n pointers .. And n+1 of these are  … maybe we can make use of this memory ….

• If a node has no right child, store a pointer to the node’s inorder successor

• If a node has no left child, store a pointer to the node’s inorder predecessor

• Need to keep a threat bit to distinguish between “regular” pointers and threads

100

200 | 40 | 300

40

200

30

50

300

400 | 30 | 

 | 50 | 500

400

500

10

60

 | 10 | 600

 | 60| 

600

20

 | 20 | 

On board .. Sketch out threaded equivalent of this tree ….

function InorderSuccessor(ptr N): ptr

//return the inorder successor of N or  if none exists

P <- RC(N)

if P =  then return 

else if P is not a thread then

while LC(P) is not a thread or  do

P <- LC(P)

return P

// Make node Q the inorder successor of node P

if RC(Q) is not a thread then

100

200 | 40 | 300

200

300

400 | 30 | 

 | 50 | 500

400

500

 | 10 | 600

 | 60| 

600

 | 20 | 

Procedure LevelOrder(ptr R):

//R is a ptr to the root of the tree

L <- MakeEmptyQueue()

Enqueue(R, L)

Until IsEmptyQueue(L)

P <- Dequeue(L)

Visit(P)

Foreach child Q of P, in order, do

Enqueue(Q,L)