- •Data Structures and Algorithms Topics for the 1st part of the course Prepared by Dr. Alex Ossyka
- •- Representation of objects in computer memory :
- •Iterative implementation (by loop)
- •1St print: 5
- •2Nd print: 9 3d print: 7
- •2Nd print: 5
- •1St print: 9 3d print: 7
- •Balanced Trees
- •Height-Balanced Binary Search Trees
Iterative implementation (by loop)
int fac(int n)
{ int f = 1;
for(int i = 1; i <= n; i++)
f = f*i;
return f;
}//factorial_iterative
Recursive implementation:
int fac(int n)
{ if(n == 0) //if() statement is used to stop recursion
return 1;
else
return n*fac(n-1);
}//factorial_recursive
Both functions are called in the same way:
void main()
{ int n = 3, x;
x = fac(n);
}
Tracing execution of the call of the recursive function: int k = fac(3);
main()
How does a computer process numerous calls and returns?
Data (parameters and other local variables) for each running function are created and pushed on the top of a Program Stack (PS) by the OS, in the activation frame for a current running function.
When a new call is made then the contents of the CPU registers (including the address of the next machine instruction) are also saved in the top activation frame.
When a running function is over, the return to the calling function is made. The top activation frame is popped from the PS. The new top activation frame provides all necessary data to continue execution of the function that made the call.
call1 return4
fac(3) = 3*fac(2) = 6
fac(2) = 2*fac(1) = 2
call2 return3
call3 return2
fac(1) = 1*fac(0) = 1
fac(0) = 1
call4 return1
Sequence of PS contents for the above tracing:
PS |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
f(0) |
|
|
|
|
|
|
call1 |
|
call2 |
|
call3 |
f(1) |
call4 |
f(1) |
ret1 |
f(1) |
ret2 |
|
ret3 |
|
|
|
|
f(2) |
|
f(2) |
|
f(2) |
|
f(2) |
|
f(2) |
|
|
push |
f(3) |
push |
f(3) |
push |
f(3) |
push |
f(3) |
pop |
f(3) |
pop |
f(3) |
pop |
main |
|
main |
|
main |
|
main |
|
main |
|
main |
|
main |
|
main is f(3) is f(2) is f(1) is f(0) is f(1) is f(2) is
running running running running running running running
PS |
|
|
|
|
|
|
ret4 |
|
|
|
|
f(3) |
pop |
|
main |
|
main |
f(3) is main is
running running
Example: Tracing of the call int k = F(3); of the following recursive function:
main() { int k = F(3);}
int F(int n)
{ if(n < 2)
return n;
Else
return 2*F(n-1) + 3*F(n-2) + 4;
}//F()
void main() { int k = F(3); }
F(2) = 2*F(1) + 3*F(0) + 4 = 6;
F(1) = 1;
F(1) = 1;
F(0) = 0;
F(3) = 2*F(2) + 3*F(1) + 4 = 19;
Call1 return5
c2 r3 c5 r4
c3 r1 c4 r2
Java recursive functions for linked lists.
printList() for Linear Singly Linked List and Linear Singly Linked List classes
iterative solution:
public void printList()
{ Node p = first;
while(p!=null)
{ System.out.print(“, “ + p.data);
p = p.next;
}//while
System.out.println();
}//printList() iterative
Recursive implementation of printList() (for linked lists) by two functions:
//public function just calls the private function. The private function does the whole job.
public void printList()
{ print_recursive(first);
System.out.println();
} //printList()
private void print_recursive(Node p)
{ if(p!=null) { System.out.print(“, “ + p.data);
print_recursive(p.next);
}//if
}//print_recursive
T
#5000
7
#6000
10
null
racing of the call A.printList(); for the given obgect A of the class LinkedList.A :
first #5000 #6000
main()
A.printList()
Call1 return4
print_recursive(#5000) print: #5000.data (=7) (1st )
call2 return3
print_recursive(#5000.next) #6000 print:
#6000.data (=10) (2nd )
print_recursive(#6000.next) null
call4 return1
Printing a linked list (LinkedList class) in reverse order:
public void print_reverse()
{ print_reverse_recursive(first);
System.out.println();
}//print_reverse
private void print_reverse_recusive(Node p)
{ if(p!=null)
{ print_reverse_recusive(p.next);
System.out.print(", " + p.data);
}//if
}//print_reverse_recusive
Tracing the call A.print_reverse() for the above given LSLL object A:
main()
A.print_reverse()
Call1 return4
print_reverse_recursive(#5000) print: #5000.data (=7) (2nd)
call2 return3
print_reverse_recursive(#5000.next) #6000
print: #6000.data (=10) (1st)
print_recursive_recursive(#6000.next) null
call4 return1
Recursive function search() for linked lists (LSLL or LDLL classes)
public boolean search(int item)
{ return search_recursive (item, first); }//search
private boolean search_recursive(int item, Node p)
{ if(p==null) return false;
else if(p.data == item) return true;
else return search_recusive(item, p.next);
}//search_recursive
Topic 10. Introduction to trees.
Informally, a graph is a set of points (vertices, nodes) and a set of lines (edges) connecting points. It is not important if lines are straight or curved, long or short.
A
tree
is a connected graph that contains no cycles.
Examples of trees:
T1 T2 T3
E xamples graphs that are not trees:
G1 (has a cycle) G2 (has a cycle) G3 (is disconnected)
A tree may have a root node (the1st node to appear in tree). In such a case, edges become directed:
father
node son node
T
he
nodes belong to their levels. Level
(root
node) A 0 A is
father for B, C, and D.
B C D 1 C is father F and G.
E F G 2 F and G are son nodes (sons) of C, and so on.
A leaf is a node without son nodes. In our example, E, F, G, and D are leaf nodes (leaves).
A
binary
tree
is a rooted tree in which any node has not more than 2 sons (0, 1, or
2).
Examples:
A Root nodes are here X
B C Y
D E F Z U
B_T1 B_T2
In a binary tree each son is either left son or right son. For example, in B_T1 , B is left son and C is right son of root node A. The direction of edges may be not shown (like in B_T2 ). But it is clear from a tree shape.
Most functions for binary trees are recursive because a binary tree has a recursive nature.
A recursive definition of a binary tree: A binary tree is a set of nodes which is:
empty;
if not empty it is partitioned (divided) into 3 subsets:
One node which is called the root of a binary tree;
A set of nodes which is a binary tree itself and is called the left subtree;
A set of nodes which is a binary tree itself and is called the right subtree.
For example, B_T1 = {A, B, C, D, E, F} = {root = {A}, left subtree1 = {B, D, E}, right subtree1 = {C, F}}.
Left subtree1 = {root1 = {B}, left subtree2 = {D}, right subtree2 = {E}}.
Right subtree1 = {root2 = {C}, left subtree3 = {}, right subtree3 = {F}}. And so on. Recursive decomposition of a subtree ends when it becomes an empty set (like in case of left subtree3).
Operations for binary trees.
There are different types of binary trees: binary search trees, balanced binary trees, etc.
Some operations can be used for any type of binary trees: isEmpty, isFull, makeEmpty, size, traversal of a binary tree. These operations are included into the base class BinaryTree.
Operations addItem, deleteItem, retrieve must be special for each type a binary tree. These operations are included into classes like BinarySearchTree. Such classes inherit the base class BinaryTree.
Representation of binary trees in computer memory.
A binary tree will be represented in memory by linked nodes as follows:
r
8
oot
in memory root (reference)
5
4
9
7
8
5
null
4
null
9
null
null
7
null
null
public class TNode { left data right
protected int data;
TNode left, right;
public TNode( int d)
{ data = d; left = right = null; } //constructor
}//class TNode
Topic 11. Traversals of Binary Trees.
Traversal is visiting each node exactly one time and processing the data of each node.
Three types of traversals:
Preorder traversal: visit_process(root) preorder(left subtree) preorder(right subtree);
Inorder traversal: inorder(left subtree) visit_prcess(root) inorder(right subtree);
postorder traversal: postorder(left subtree) postorder(right subtree) visit_prcess(root);
r
7
Preorder sequence: 7, 5, 6, 8, 2, 9
Inorder sequence: 6, 5, 8, 7, 2, 9
Postorder sequence: 6, 8, 5, 9, 2, 7
Here each node was visited for printing (= processing) data.
oot
5
2
6
8
9
In a Java code each traversal algorithm (preorder, inorder and postorder) is presented by 2 functions: public and protected (recursive). Tracing of these functions is done inside the code text.
Java code of the class BinaryTree
Prepared by Dr. Alex Ossyka
//////////////////////////////////////////////////////////////////////////////////////////////////
package binarytree;
// author Dr. Alex Ossyka
public class TNode {
protected int data;
protected TNode left, right;
//*****************************************
public TNode(int item)
{ data = item;
left = right = null;
}//constructor
}//class TNode. It is the type of a node used in binary trees
////////////////////////////////////////////////////////////////////////////////////////////////////////
package binarytree;
public class BinaryTree {
protected TNode root;
//***********************
public BinaryTree()
{ root = null; }// constructor
//***********************
public boolean isEmpty()
{ return root == null; }
//***********************
public boolean isFull()
{ TNode p = new TNode(0);
return p == null;
}
//**********************
public void makeEmpty()
{ root = null; }
//**********************
//preorder, inorder and postorder traversals for printing data on nodes.
public void preorder()
{ System.out.println("preorder sequence of nodes:");
preorder_rec(root);
System.out.println(";");
}//preorder
protected void preorder_rec(TNode p)
{ if(p != null)
{ System.out.print(", " + p.data);
preorder_rec(p.left);
preorder_rec(p.right);
}//if
}//preoder_rec
/* tracing the call: A.preorder(). Given object BinaryTree A including objects n1, n2, n3 of TNode class.
&n1
A: root Note: &n1 denotes the address of the node n1, etc.
5
&n2
&n3
n1:
null
9
null
null
7
null
n2: n3:
main()
After return8, it is printed: , 5, 9, 7
A.preorder()
call1 return8
call2 return7
A.preorder_rec(&n1)
