Trees - Data Structures - Lecture Notes | CSCI 1200, Assignments of Data Structures and Algorithms

Material Type: Assignment; Class: DATA STRUCTURES; Subject: Computer Science; University: Rensselaer Polytechnic Institute; Term: Fall 2008;

Typology: Assignments

Pre 2010

Uploaded on 08/09/2009

koofers-user-3ly
koofers-user-3ly 🇺🇸

10 documents

1 / 4

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
CSCI-1200 Computer Science II Fall 2008
Lecture 17 Trees, Part II
Review from Lecture 16
Binary trees and binary search trees. They have a close tie to recursion.
We use auxiliary TreeNode and tree_iterator classes.
The only member variables of the cs2set class are the root pointer and the size (number of tree nodes).
The iterator class is declared internally, and is effectively a wrapper on TreeNode pointers.
Note that operator* returns a const reference because the keys can’t change.
The increment and decrement operators are missing from the implementation but we will discuss how
they could be added.
The main public member functions just call a private (and often recursive) member function (passing the root
pointer) that does all of the work.
Because the class stores and manages dynamically allocated memory, it must provide a copy constructor, an
operator=, and a destructor in order to work correctly.
Last time we saw basic tree traversal algorithms and implemented begin and find.
Today’s Lecture
cs2set operations: insert, destroy, printing, erase
Tree height
Increment and decrement operations on iterators
Limitations of our implementation
Homework 6 Inverse Word Search Recursion Contest Results
Homework 8 Discussion
17.1 Insert
Move left and right down the tree based on comparing keys. The goal is to find the location to do an insert
that preserves the binary search tree ordering property.
Inserting at an empty pointer location.
Passing pointers by reference ensures that the new node is truly inserted into the tree. This is subtle but
important.
Note how the return value pair is constructed.
17.2 Printing - Two different methods
One outputs one key per line of output based on an in-order traversal.
The second prints the tree sideways rotated counter-clockwise by 90 degrees. This is accomplished by a
“reversed” in-order traversal while keeping track of the tree height. We’ll look at a few examples in class to
get a feel for how this works.
17.3 Exercise
Write the destroy tree member function. This should effectively be a post-order traversal, with a node being
destroyed after its left and right subtrees are destroyed.
pf3
pf4

Partial preview of the text

Download Trees - Data Structures - Lecture Notes | CSCI 1200 and more Assignments Data Structures and Algorithms in PDF only on Docsity!

CSCI-1200 Computer Science II — Fall 2008

Lecture 17 – Trees, Part II

Review from Lecture 16

  • Binary trees and binary search trees. They have a close tie to recursion.
  • We use auxiliary TreeNode and tree_iterator classes.
  • The only member variables of the cs2set class are the root pointer and the size (number of tree nodes).
  • The iterator class is declared internally, and is effectively a wrapper on TreeNode pointers.
    • Note that operator* returns a const reference because the keys can’t change.
    • The increment and decrement operators are missing from the implementation but we will discuss how they could be added.
  • The main public member functions just call a private (and often recursive) member function (passing the root pointer) that does all of the work.
  • Because the class stores and manages dynamically allocated memory, it must provide a copy constructor, an operator=, and a destructor in order to work correctly.
  • Last time we saw basic tree traversal algorithms and implemented begin and find.

Today’s Lecture

  • cs2set operations: insert, destroy, printing, erase
  • Tree height
  • Increment and decrement operations on iterators
  • Limitations of our implementation
  • Homework 6 Inverse Word Search Recursion Contest Results
  • Homework 8 Discussion

17.1 Insert

  • Move left and right down the tree based on comparing keys. The goal is to find the location to do an insert that preserves the binary search tree ordering property.
  • Inserting at an empty pointer location.
  • Passing pointers by reference ensures that the new node is truly inserted into the tree. This is subtle but important.
  • Note how the return value pair is constructed.

17.2 Printing - Two different methods

  • One outputs one key per line of output based on an in-order traversal.
  • The second prints the tree sideways — rotated counter-clockwise by 90 degrees. This is accomplished by a “reversed” in-order traversal while keeping track of the tree height. We’ll look at a few examples in class to get a feel for how this works.

17.3 Exercise

Write the destroy tree member function. This should effectively be a post-order traversal, with a node being destroyed after its left and right subtrees are destroyed.

17.4 Erase

First we need to find the node to remove. Once it is found, the actual removal is easy if the node has no children or only one child. It is harder if there are two children:

  • Find the node with the greatest value in the left subtree or the node with the smallest value in the right subtree.
  • The value in this node may be safely moved into the current node because of the tree ordering.
  • Then we recursively apply erase to remove that node — which is guaranteed to have at most one child.

Exercise: Write a recursive version of erase.

17.5 Height and Height Calculation Algorithm

  • The height of a node in a tree is the length of the longest path down the tree from that node to a leaf node. The height of a leaf is therefore 0. We will think of the height of a null pointer as -1.
  • The height of the tree is the height of the root node, and therefore if the tree is empty the height will be -1.

Exercise: Write a simple recursive algorithm to calculate the height of a tree.

17.6 Tree Iterators, Revisited

  • The increment operator should change the iterator’s pointer to point to the next TreeNode in an in-order traversal — the “in-order successor” — while the decrement operator should change the iterator’s pointer to point to the “in-order predecessor”.
  • Unlike the situation with lists and vectors, these predecessors and successors are not necessarily “nearby” (either in physical memory or by following a link) in the tree, as examples we draw in class will illustrate.
  • There are two common solution approaches:
    • Each iterator maintains a stack of pointers representing the path down the tree to the current node.
    • Each node stores a parent pointer. Only the root node has a null parent pointer.
  • If we choose the parent pointer method, we’ll need to rewrite the insert and erase member functions to correctly adjust parent pointers.
  • Although iterator increment looks expensive in the worst case for a single application of operator++, it is fairly easy to show that iterating through a tree storing n nodes requires O(n) operations overall.

Exercise: Implement an algorithm for finding the in-order successor of a node.

17.7 Limitations of Our BST Implementation

  • The efficiency of the main insert, find and erase algorithms depends on the height of the tree.
  • The best-case and average-case heights of a binary search tree storing n nodes are both O(log n). The worst- case, which often can happen in practice, is O(n).
  • Developing more sophisticated algorithms to avoid the worst-case behavior will be covered in Data Structures and Algorithms.

// ITERATORS iterator begin() const { if (!root_) return iterator(NULL); TreeNode* p = root_; while (p->left) p = p->left; return iterator(p); } iterator end() const { return iterator(NULL); }

private: // REPRESENTATION TreeNode* root_; int size_;

// PRIVATE HELPER FUNCTIONS TreeNode* copy_tree(TreeNode* old_root) { /* Implemented in Lab 10 / } void destroy_tree(TreeNode p) { /* Implemented in Lecture 17 */ }

iterator find(const T& key_value, TreeNode* p) { if (!p) return iterator(NULL); if (p->value > key_value) return find(key_value, p->left); else if (p->value < key_value) return find(key_value, p->right); else return iterator(p); }

std::pair<iterator,bool> insert(const T& key_value, TreeNode*& p) { if (!p) { p = new TreeNode(key_value); this->size_++; return std::pair<iterator,bool>(iterator(p), true); } else if (key_value < p->value) return insert(key_value, p->left); else if (key_value > p->value) return insert(key_value, p->right); else return std::pair<iterator,bool>(iterator(p), false); }

int erase(T const& key_value, TreeNode* &p) { iterator itr = find(key_value); // locate element to remove // Completed in Lecture 17 }

void print_in_order(std::ostream& ostr, const TreeNode* p) const { if (p) { print_in_order(ostr, p->left); ostr << p->value << "\n"; print_in_order(ostr, p->right); } } void print_as_sideways_tree(std::ostream& ostr, const TreeNode* p, int depth) const { if (p) { print_as_sideways_tree(ostr, p->right, depth+1); for (int i=0; i<depth; ++i) ostr << " "; ostr << p->value << "\n"; print_as_sideways_tree(ostr, p->left, depth+1); } } };