Basic Algorithms: Lecture 8

================ Start Lecture #8 ================

OperationArrayList
size, isEmptyO(1)O(1)
atRank, rankOf, elemAtRankO(1)O(n)
first, last, before, afterO(1)O(1)
replaceElement, swapElementsO(1)O(1)
replaceAtRankO(1)O(n)
insertAtRank, removeAtRankO(n)O(n)
insertFirst, insertLastO(1)O(1)
insertAfter, insertBeforeO(n)O(1)
removeO(n)O(1)
Asymptotic complexity of the methods for both the array and list based implementations

2.2.3 Sequences

Define a sequence ADT that includes all the methods of both vector and list ADTs as well as

• atRank(r): Return the position of the element at rank r.
• rankOf(p): Return the rank of the element at position p.

Sequences can be implemented as either circular arrays, as we did for vectors, or doubly linked lists, as we did for lists. Neither clearly dominates the other. Instead it depends on the relative frequency of the various operations. Circular arrays are faster for some and doubly liked lists are faster for others as the table to the right illustrates.

Iterators

An ADT for looping through a sequence one element at a time. It has two methods.

• hasNext: Test whether there are elements left in the iterator
• nextObject: Return and remove the next object in the iterator

When you create the iterator it has all the elements of the sequence. So a typical usage pattern would be

```create iterator I for sequence S
while I.hasNext
process I.nextObject
```

2.3 Trees

The tree ADT stores elements hierarchically. There is a distinguished root node. All other nodes have a parent of which they are a child. We use nodes and positions interchangeably for trees.

The definition above precludes an empty tree. This is a matter of taste some authors permit empty trees, others do not.

Some more definitions.

• Nodes with the same parent are called siblings.

• A node without children is called external by the authors. Also common is to call such nodes leaves.

• A node with children is called internal.

• An ancestor of a node is either the node itself or an ancestor of the parent of the node. This says that the ancestors include the node, the node's parent, the parent's parent, ... up to the root.

• A node v is a descendent of a node u if u is an ancestor of v.

• The subtree rooted at v is the tree consisting of all the descendents of v.

• A tree is ordered if there is a linear ordering of the children of each node. That is, there is a first child, a second child, etc.

• A binary tree is one in which all nodes have at most two children.

• A proper binary tree is one in which no node has exactly one child. This means that all internal nodes have two children.

• In a binary tree we label each child as either a left child or as a right child.

• The subtree routed at the left child of v is called the left subtree of v.

• Similarly we define the right subtree of v.

• The depth of a node is the number of ancestors not including the node itself.

• The height of a node is the length of a longest path to a leaf.

• The height of a tree is the height of the root.

We order the children of a binary tree so that the left child comes before the right child.

There are many examples of trees. You learned or will learn tree-structured file systems in 202. However, despite what the book says, for Unix/Linux at least, the file system does not form a tree (due to hard and symbolic links).

These notes can be thought of as a tree with nodes corresponding to the chapters, sections, subsections, etc.

Games like chess are analyzed in terms of trees. The root is the current position. For each node its children are the positions resulting from the possible moves. Chess playing programs often limit the depth so that the number of examined moves is not too large.

An arithmetic expression tree

The leaves are constants or variables and the internal nodes are binary arithmetic operations (+,-,*,/). The tree is a proper ordered binary tree (since we are considering binary operators). The value of a leaf is the value of the constant or variable. The value of an internal node is obtained by applying the operator to the values of the children (in order).

Evaluate an arithmetic expression tree on the board.

Homework: R-2.2, but made easier by replacing 21 by 10. If you wish you can do the problem in the book instead (I think it is harder).

2.3.1 The Tree Abstract Data Type

We have three accessor methods (i.e., methods that permit us to access the nodes of the tree.

• root(): Return the root of the tree.
• parent(v): Return the parent of the node v. Error if v is the root.
• children(v): Return an iterator of the children of v (in order if the tree is ordered).

We have four query methods that test status.

• isInternal(v): Tests if v is internal.
• isLeaf(v): Tests if v is a leaf.
• isExternal(v): Same as isLeaf(v).
• isRoot(v): Tests if v is the root.

Finally generic methods that are useful but not related to the tree structure.

• size(): Return the number of nodes
• elements(): Return an iterator of all the elements stored in nodes (in no particular order).
• positions: Return an iterator of all the nodes of the tree.
• swapElements(v,w): Swap the elements in nodes v and w.
• replaceElement(v,e): Replace with e and return the element stored at v.
Allan Gottlieb