Week 08


Recursion1/42

A recursive function is one that calls itself within the program text

int factorial(int n) {  // recursive version      
	int retval;
	if (n == 0) {
		retval = 1;
	} else {
		retval = n * factorial(n-1);
	}
	return retval;

Mathematical definitions often lend themselves directly to a recursive implementation.


... Recursion2/42

Exercise
Use a recursive function to implement Euclid's algorithm for computing the greatest common divisor


gcd(651,378) = gcd(273,378)
             = gcd(273,105)
             = gcd(168,105)
             = gcd(63,105)
             = gcd(63,42)
             = gcd(21,42)
             = gcd(21,21)
             = 21


... Recursion3/42

Most list functions can be implemented recursively

int length(List L) {      // recusive version
	int retval;
	if (L == NULL) {  // base case
		retval = 0;
	} else {          // recursive case
		retval = length(L->next) + 1;
	}
	return retval;
}


... Recursion4/42

// Search for item in sorted int list
// Returns pointer to node, or NULL if not found
Node *search(SortedIntList L, int v) {
	Node *retval;
	if (L == NULL) {
		retval = NULL;
	} else if (v < L->value) { // v can't be in the list       
		retval = NULL;     
	} else if (v > L->value) { // recursive case
		retval = search(L->next, v);
	} else { // found
		retval = L;
	}
	return retval;
}

Exercise    Write recursive functions to insert/delete a list element

Node *insert(SortedIntList, int);
Node *delete(SortedIntList, int);


Data Structures5/42

Structured data objects

An element can be


... Data Structures6/42

Typical operations on a data structure

A specific collection of operations defines one type of data structure.


... Data Structures7/42

Data structures we have seen so far:

Other major classes of data structure:


... Data Structures8/42

[Diagram:Pic/structures-small.png]


Implementing Complex Structures9/42

So far, we have looked at linear structures:

typedef struct node {
	ValueType value;
	struct node *next;
	// might also have *prev
} Node;
typedef Node *List;

One implementation of complex structures generalises this:

typedef struct node {
	ValueType value;
	struct node *neighbours[];
} Node;
typedef Node *Collection;


Binary Search Trees


Binary Trees11/42

Binary trees are a type of dynamic data structure

Problem with searching in linked-lists: Cost of searching in an unsorted list of length n Binary trees can improve this to worst case: log2n

Cost for list with 1000 nodes ...   linear: 1000,   binary tree: 10


... Binary Trees12/42

Trees are branched dynamic data structures

[Diagram:Pic/tree-small.png]


... Binary Trees13/42

Trees can be viewed as a set of nested structures:

[Diagram:Pic/subtrees-small.png]


... Binary Trees14/42

Node level = path length from root to node

Tree height = max path length from root to leaf

[Diagram:Pic/tree-height-small.png]

Height of tree with n nodes: min = log2n ,   max = n-1


... Binary Trees15/42

Trees are used in many contexts, e.g.

[Diagram:Pic/trees-small.png]


... Binary Trees16/42

Search trees have the properties

Balanced trees have the properties


... Binary Trees17/42

Examples of binary search trees:

[Diagram:Pic/binary-search-trees-small.png]

Shape of tree is determined by order of insertion.


Exercise: Binary Search Trees18/42

For each of the following sequences of values:

Sequences:


Representation of Binary Trees19/42

Binary trees consist of Nodes, where each Node contains

typedef struct node {
	int value;
	struct node *left;
	struct node *right;
} Node; 
typedef Node *Tree;

A tree is represented by a pointer to its root node.


Operations on Binary Trees20/42

For the rest of our discussion, we consider binary search trees.

A binary search tree type would typically have operations:

// make an empty tree
Tree emptyTree();

// insert a new value into the tree
Tree insert(Tree t, int v);

// delete a value from the tree
Tree delete(Tree t, int v);

// search for a value 
Node *search(Tree t, int v);


... Operations on Binary Trees21/42

A useful auxiliary operation:

// Make a new Node
Node *newNode(int v) {
	Node *new;
	new = (Node *)malloc(sizeof(Node));
	assert(new != NULL);
	new->value = v;
	new->left = NULL;
	new->right = NULL;
	return new;
}

After creating such a Node it would typically be linked into a Tree.


Searching in Search Trees22/42

Most tree algorithms are best described recursively

Example animation

// Search for item in tree
// Returns pointer to node, or NULL if not found
Node *search(Tree t, int  v) {
	Node *retval;
	if (t == NULL) {
		retval = NULL;
	} else if (v < t->value) {
		retval = search(t->left, v);
	} else if (v > t->value) {
		retval = search(t->right, v);
	} else { // found
		retval = t;
	}
	return retval;
}


Exercise: Count Nodes in a Binary Tree23/42

Implement a function int size(Tree t)


Insertion into Search Trees24/42

// Recursive version
// Insert into appropriate subtree
Tree insert(Tree t, int  v) {
	if (t == NULL)
		t = newNode(v);
	else if (v == t->value) {
		; // no duplicates allowed
	}
	else if (v < t->value) {
		t->left = insert(t->left, v);
	}
	else if (v > t->value) {
		t->right = insert(t->right, v);
	}
	return t;
}


... Insertion into Search Trees25/42


// Non-recursive version
// Find location in tree; attach new leaf
void insert(Tree *t, int  v) {
	char lastTurn;
	Node *curr = *t;
	Node *parent = NULL;

	// empty tree; special case
	if (curr == NULL) {
		*t = newNode(v);
	} else {  // find where new leaf belongs
		while (curr != NULL) {
			if (v == curr->value) {
				; // no duplicates allowed
			} else if (v < curr->value) {
				parent = curr;
				curr = curr->left;
				lastTurn = 'L';
			} else if (v > curr->value) {
				parent = curr;
				curr = curr->right;
				lastTurn = 'R';
			}
		}
		// connect new node
		switch (lastTurn) {
		   case 'L': parent->left = newNode(v); break;
		   case 'R': parent->right = newNode(v); break;
		}
	}
}


Tree Traversal26/42

For many traversals/iterations

For trees, several well-defined visiting orders exist:


... Tree Traversal27/42

Consider visiting an expression tree like:

[Diagram:Pic/tree1-small.png]

NLR: + * 1 3 - * 5 7 9    (useful for building tree)
LNR: 1 * 3 + 5 * 7 - 9    ("natural" order)
LRN: 1 3 * 5 7 * 9 - +    (useful for evaluation)


... Tree Traversal28/42

Inorder traversal:

void TraverseLNR(Tree t) {
	if (t != NULL) {
		TraverseLNR(t->left);
		VISIT(t->value);
		TraverseLNR(t->right);
	}
}

where VISIT is some operation on the value in the Node


Exercise: Tree Traversal29/42

Show NLR, LNR, LRN traversals for the following tree:

[Diagram:Pic/bigtree-small.png]


Tree Traversal30/42

Traversal is a generic operation:

C provides a (limited) mechanism for this via function pointers.

Inorder traversal (with run-time choice of visit operation):

void TraverseLNR(Tree t, void (*visit)(int)) {
	if (t != NULL) {
		TraverseLNR(t->left, visit);
		(*visit)(t->value);
		TraverseLNR(t->right, visit);
	}
}


... Tree Traversal31/42

Consider how we might use the above TraverseLNR()

void show(int v) {
	printf("%d ",v);
}

// ... which might be used as

Tree t;
...
TraverseLNR(t, show);

The second parameter has type pointer to function.


Deletion from BSTs


Deletion from Binary Search Trees33/42

Insertion into a binary search tree is easy:

Deletion from a binary search tree is harder: Example animation


... Deletion from Binary Search Trees34/42

Case 1: value to be deleted is a leaf (zero subtrees)

[Diagram:Pic/del-k-small.png]


... Deletion from Binary Search Trees35/42

Case 1: value to be deleted is a leaf (zero subtrees)

[Diagram:Pic/del1-k-small.png]


... Deletion from Binary Search Trees36/42

Case 2: value to be deleted has one subtree

[Diagram:Pic/del-p-small.png]


... Deletion from Binary Search Trees37/42

Case 2: value to be deleted has one subtree

[Diagram:Pic/del1-p-small.png]


... Deletion from Binary Search Trees38/42

Case 3: value to be deleted has two subtrees

[Diagram:Pic/del-m-small.png]

Replace deleted node by its immediate successor


... Deletion from Binary Search Trees39/42

Case 3: value to be deleted has two subtrees

[Diagram:Pic/del2-m-small.png]


... Deletion from Binary Search Trees40/42


Tree delete(Tree t, int v){
  if (t == NULL) {
    ; // v not found, nothing to do
  } else if (v < t->value) {   // delete v in left subtree
      t->left = delete(t->left, v);
  } else if (v > t->value) {   // delete v in right subtree
      t->right = delete(t->right, v);
  } else {
      // v == t->value, so the node 't' must be deleted
      // code below violates style, just to make logic clear
      Tree n;                                             // temporary
      if (t->left==NULL && t->right==NULL) n=NULL;        // 0 children
      else if (t->left ==NULL)             n=t->right;    // 1 child
      else if (t->right==NULL)             n=t->left;     // 1 child
      else                                 n=joinAtMinimum(t->left,t->right);
      free(t);
      t = n;
  }
  return t;
}


... Deletion from Binary Search Trees41/42

// Joins t1 and t2 with the minimum of t2 as new root
Tree joinAtMinimum(Tree t1, Tree t2){
   Tree nuroot = t2;
   Tree p = NULL;
   while (nuroot->left != NULL) {
       p = nuroot;
       nuroot = nuroot->left;
   }                       // nuroot is the minimum, p is its parent
   if (p != NULL){
       p->left = nuroot->right; // give nuroot's only child to p
       nuroot->right = t2;      // nuroot replaces deleted node
   }
   nuroot->left = t1;           // nuroot replaces deleted node
   return nuroot;
}


Tips for Lab42/42


Quacks, Recursion, Binary Search Trees


Produced: 23 Sep 2015