36C. Nondestructive Functions on Trees

As for linked lists, we can separate functions on trees into nondestructive functions, which do not alter a tree, and destructive functions, which do. Key rules to remember for defining either of those kinds of functions are:

  1. Every tree is either an empty tree or a nonempty tree. Consequently, most algorithms have a case for an empty tree and at least one case for a nonempty tree.

  2. Every nonempty tree has a left subtree and a right subtree. Function cases for nonempty trees typically call the same function on the two subtrees.


Example: Counting the number of nodes in a tree

A simple example of a nondestructive function is numNodes(T ), which yields the number of nodes in tree T and employs the following facts.

  1. If T is empty then T has 0 nodes.

  2. If T  is nonempty then T  has (1) its root (one node), (2) all of the nodes in the left subtree and (3) all of the nodes in the right subtree. All of those nodes need to be counted.

  int numNodes(ConstTree T)
  {
    if(T == NULL)
    {
      return 0;
    }
    else
    {
      return 1 + numNodes(T->left) + numNodes(T->right);
    }
  }

Example: Replacing each value by its cube

Nondestructive function cubes(T ) returns a tree that looks like T  except that each number is replaced by its cube. For example, if T is

then cubes(T ) yields the following tree, without modifying T.

  int cube(const int x)
  {
    return x*x*x;
  }

  Tree cubes(ConstTree T)
  {
    if(T == NULL)
    {
      return NULL;
    }
    else 
    {
      return new Node(cube(T->item), cubes(T->left), cubes(T->right));
    }
  }

Example: Getting a mirror image of a tree

A mirror image of a tree is obtained by swapping the left and right subtrees of each node. For example, a mirror image of tree

is

Here is a nondestructive definition of mirror. Instead of altering the nodes in an existing tree, it builds a new tree. Notice that, in the case of a nonempty tree t, this function concentrates on building the root of mirror(t), installing the correct subtrees into that root.

  // mirror(t) returns a mirror image of tree t.

  Tree mirror(ConstTree t)
  {
    if(t == NULL) 
    {
      return NULL;
    }
    else 
    {
      return new Node(t->item, mirror(t->right), mirror(t->left));
    }
  }

Exercises

  1. Write a definition of function numLeaves(T ), which returns the number of leaves in tree T. Answer

  2. Write a definition of a function sum(T ) that returns the sum of all of the numbers in a given tree. If T is empty then sum(T ) is 0. Answer

  3. Write a definition of function nonneg(T ), which produces the tree that results by replacing each negative number in T by 0, and leaves nonnegative numbers as they were. For example, if T is the left-hand tree below then nonneg(T ) is the right-hand tree.

    This function must not modify T. The result tree should be made out of new nodes.

    Answer

  4. Read the definition of the height of a tree. Write a function height(T ) that returns the height of tree T. Answer