Do the following problems from the text:
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.
This is to be done on the CS machines (tux.cs.drexel.edu , or one of the CS lab machines
For you to do:
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:
, 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.
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!
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.
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: