HW 4

1 Theory

Do the following problems from the text:

  1. A binary tree has n nodes. What is the maximum height of the tree? What is the minimum height? Explain your answers.
  2. A tree (not necessarily a binary tree) has n nodes. What are the minimum and maximum possible heights of the tree? Explain your answer.
  3. 4.6
  4. 4.7
  5. Following are several hash functions. None of them are very good as hash functions. Explain why they are not good hash functions.
    1. Hash keys are character strings. The hash function h1(x) computes the length of the string.
    2. The function h2(x) computes a random number r with 1 ≤ r ≤ B, where B is the number of buckets. It returns r.
  6. Design an efficient data structure for representing a subset S of the integers from 1 to n. The operations we wish to perform are these:
    1. Select an integer i from the set and delete it.
    2. Add an integer i to the set.

    A mechanism must be provided to ignore a request to add integer i to S in the case where S alrady contains i. The data structure must be such that the time to select and delete an element and the time to add an element are constant and not dependent on ||S||, the size of S.

    Analyze the time and space complexity of your algorithms.

  7. Write a procedure (in pseudocode!) to increase the number of buckets in a (closed) hash table. Analyze its time and space complexity.

2 Implementation

This is to be done on the CS machines (tux.cs.drexel.edu , or one of the CS lab machines

For you to do:

  1. The TAs have provided functions get_expression and get_next token that tokenize arithmetic expression. The first function, get_expression, returns True unless it reads a blank line or end of input. The second function, get next token, returns False on error or end of expression, but otherwise returns the next token, as a string. (A token is the next high-level thing in the expression, like '+' or '*' or '4718'. You can see how they work by running the example called test.py.

    The tokenizer is very simple and can probably be easily tricked into doing bad things if you feed it bad input. In particular, it doesn't understand decimal points. It does understand parentheses, the four basic arithmetic functions, and positive integers. We will only test w/valid input, as described here.

    Write functions that use the tokenizer to read arithmetic expressions:

    1. (Makefile target: post) postorder

    , each of which expects an expression in the given order. As you read the expressions, you should build a parse tree (algorithm follows). Then for each of these, it should print the expression, from the parse tree, in pre-order, in-order, and post-order. Your program should expect to read lines from stdin, much like test.py does. One expression per line, assume that expressions are correct (valid).

    Output would be something like:

    pre:  + 3 2
    in: 3 + 2
    post: 3 2 +

    for each input expression.

    Parsing a Postfix Expression

    Consider a parse tree. Each subtree is an expression. We build larger expressions from smaller ones. Leaves are also expressions, simple operands.

    So, as you read an operand, wrap it in a single-node tree, and push it onto a stack. When you hit an operator, you will take 2 expressions subtrees from the stack, combine them into a larger expressions tree, and push that tree back onto the stack. (Note that the order of the operands, as they're popped from the stack, is important. Try a couple small examples using subtraction and division to check your algorithm.)

    If the expression is correct, when you run out of tokens for a given expression, there should be exactly one tree left on the stack. Your parse tree for the entire expression.

    Note that you can use almost the same code for all of these, so this isn't as hard as it may sound on first reading!

  2. (Makefile target: (No separate target)) Extra Credit: In the above exercises, also evaluate the arithmetic expressions and print out the answer, so, output from the above targets (for each input expression) would look like this:
    pre:  + 3 2
    in: 3 + 2
    post: 3 2 +
    eval: 5

    Do the evaluation by recursively exploring the parse tree. That is, the value of the expression is the value of the root. The value of the root is dependent on the values of its children. And so forth. Since leaves are always numbers in our parse trees, their values are the values of the numbers represented at the leaves. You just need to handle four operations.

    To convert a string to an int: op1 = int( token ). This will throw an exception if token is not something that can be converted to an integer.

  3. (Makefile target: view) Display your source.

Representing Trees

You can write a simple, dumb class, or you can simply use Python lists as triplets: [ Label, Left Child, Right Child ] .

We will be supplying our own input files to test with. We will simply redirect input, for each given target (so, get the target names correct!).

A sample makefile, along w/sample input files, has been included.

Here are some files for your use: