CISC 3130
Data Structures
Associative Implementation Containers — I

Overview

The associative containers (set and map) access their elements by their values (as opposed to the sequences — vector, stack, etc — which access their elements by position), and thus the basic operation for associatves is a lookup (basically a search for an element with a particular value).

Using a array list in which we maintain the elements in sorted order allows us to use a binary search for our lookup, which is logarithmic (O(log n)); adding elements, however, to such a sorted vector would involve a linear (O(n)) operation (the new element would have to be inserted at it proper position to maintain sortedness and the rest of the vector to its right slid over one position. The insertion of the n elements (assuming they arrived in random order) would thus be of quadratic cost.

What is needed are lookup AND insertion operations that are both O(log n) or better. There are two basic structures that provide us with such performance: trees (logarithmic), and hash tables (constant if done right).

Binary Trees

A tree is a hierarchical structure of nodes.

Terminology

Recursive Nature of Binary Tree

Binary Tree Nodes - Nodes

class Node<E> {
	Node(Node<E>, E data, Node<E> right) {
		this.data = data;
		this.left = left;
		this.right = right;
	}
	private E data;
	private Node≤E> left, right;
}
Notes

Binary Tree

A binary tree is Represented via a pointer to the root
class BinaryTree {
	…
	Node<E> root;
}

Operations on a Binary Tree

Some Useful Algorithms

Node Count

#nodes in a tree =

int nodeCount(Node<E> root) {
	if (root == null) return 0;
	return nodeCount(root.left) + nodeCount(root.right) + 1
}
		

Leaf Count

#leaves in a tree =

int leafCount(Node<E> root) {
	if (root == null) return 0;
	if (root.left == null && root.right == null) return 1;
	return leafCount(root.left) + leafCount(root.right);
}

Depth of a Tree

#depth of in a tree =
int depth(Node<E> root) {
	if (root == null) return 0
	return max(depth(root.left), depth(root.right)) + 1
}

Finding a Value in the Tree

value is there if

boolean contains(Node<E> root, E data) {
	if (root == null) return false
	if (data == root.data) return true
	return contains(root.left, data) || contains(root.right, data)
}

Finding the Maximum Value in the Tree

Maximum value of a (sub)tree is the maximum of the root's value and the maximum values of the left and right subtrees

int max(Node<E> root) {
	if (root == null) return 0 	// a 'cheat'
	return Math.max(root.data, max(root.left), max(root.right))
}
		
int max(Node<E> root) {		// a delegator of sorts
	if (root == null) throw new Exception("can't take max of empty tree");
	return max2(root);
}

int max2(Node<E> root) {		// private
	int result = root.data;
	if (root.left != null) result = max(result, max2(root.left);
	if (root.right != null) result = max(result, max2(root.right);
	return result;
}
		

Applications of Binary Trees

General Trees

Arbitrary number of children; typically uses a List of pointers to children
class GenTreeNode<E> {
	E data;
	ArrayList<GenTreeNode<E>> children;
}

Binary Search Tree (BST) — An O(log n) Implementation of Searching

Finding a Value in the BST

value is there if

bool contains(Node<E> root, E data) const {
        if (root == null) return false;
        if (data == root.data) return true;
        if (data.compareTo(root.data) < 0) return contains(root.left, data);
        return contains(root.right, data);
}
		

Implementation

class BinarySearchTree {
	…
private:
	BinaryTree binaryTree;
};

Operations on a Binary Search Tree

The first two are in the context of instance methods of the above BinarySearchTree class.

Heaps

<

Code related to this lecture