stacks & recursion. stack pushpop lifo list - only top element is visible top
TRANSCRIPT
Stacks & Recursion
Stackpush pop
LIFO list - only top element is visible
top
Defining the ADT "Stack"
• Data:– a linear collection of data items in which all
operations occur at one end, called the top• Basic Operations:– construct a stack (usually starts empty)– find out if stack is empty– push: add an item on top of the stack– accessing the topmost item• Top: retrieve the top item of the stack• Pop: remove the top item of the stack
Some Implementation choices• fixed-size array– capacity (max # elements) decided at compile-time• could be too small for expected amount of data• could be too large, so space is wasted
– size (current # elements used)– fast
• dynamic array– capacity decided at run-time– size may be less than or equal to the capacity– uses pointers
• linked list– size changes as needed during program execution– uses pointers
Implementation example
-1
myTop
myArray
76543210
typedef Complx StackElement;const int CAPACITY = 8;
int myTop;StackElement myStack[CAPACITY];
Complx X;push(&myStack,X);
initially empty (myTop is negative)
Nyhoff, ADTs, Data Structures and Problem Solving with C++, Second Edition, © 2005 Pearson Education, Inc. All rights reserved. 0-13-140909-3
6
Using the stack - 1
• Model with an array– Let position 0 be top of stack
• Problem … consider pushing and popping– Requires much shifting
7
Nyhoff, ADTs, Data Structures and Problem Solving with C++, Second Edition, © 2005
Pearson Education, Inc. All rights reserved. 0-13-140909-3
Using the stack - 2
• A better approach is to let position 0 be the bottom of the stack
• Thus our design will include– An array to hold the stack elements– An integer to indicate the top of the stack
Addressing the problem
• Too much or too little space• Whatever size you pick, some day, there will
be too much data• In embedded systems, RAM is a premium• Need a dynamic solution (TBD)– Size changes as needed– No waste– Slower when adding new elements
The "node" concept
• a "node" is one element of a stack• more than just the data– index of next and/or previous node– trivial for array implementations
• each node is a struct– data may be a struct INSIDE the node struct
struct node {int next;Complx data;};
node myStackNode[CAPACITY];
Recursion
An alternative to loops
What is recursion?
• a function calls itself– direct recursion
• a function calls its caller– indirect recursion
ff1
f2
Recursion-1• Alternative to iteration (looping)– often more "elegant" and concise than a loop– sometimes very inefficient– easier to program than loops
• Useful when– a problem can be defined in terms of similar sub-problems– eventually reach a "known" answer (base case)
• Inefficient if duplicate values exist
Head vs. Tail Recursion
• head recursion– requires "deepest" call to complete before any
values are known– current stack state must be preserved
• tail recursion– compiler can "collapse" the stack
Head vs. Tail recursionNote: base case is ALWAYS 1st
tail(3) is: 3 2 1void tail(int n) {
if(n == 0) return;
else printf("tail - n=%i\
n",n);tail(n-1); //
}
head(3) is: 2 3void head(int n){
if(n == 1) return;
elsehead(n-1); //
printf("head - n=%i\n",n);); }
Caveats
• Static data is NOT on the "stack"– never gets re-allocated– same values for EVERY call
• Must have an "exit" – prevent an infinite loop
Outline of a Recursive Function
if (answer is known) provide the answer & exitelse call same function with a smaller version of the same problem
basecase
recursivecase
Factorial (n) - looping
fact (int n){if (n<0) exit(1);
answer=1;for (i=1; i<=n; i++) answer=answer*i; // loop n
timesreturn (answer);
}• This is a simple problem
Factorial (n) – head recursive
definition: Factorial (n) = n * Factorial (n-1)
int Fact (int n){if (n<0) exit(1); if (n == 0 | n==1) return 1; return n * Fact (n-1); // stack must be saved// cannot do the *'s until last value}
Factorial "n" (tail recursive)
tail_fact (n, sofar) // set "sofar" same as "n"{ if (n == 0 | n==1)
return sofar; else // nothing to save on the stack
// because it is in the 2nd parameter
return tail_fact (--n, sofar * n); }
// here's how to run itprintf ("5!=%i",tail_fact(5,5));
Keeping track
• compiler builds code to:– create a new stack pointer and stack space– put local variables & parameters on stack
• each "return" returns control to caller's next instruction (the inst after the call)
• returned value, is in caller's stack space– same as ANY function
• this is called "unwinding"
Recursive Call Tree
int Fact (int n){ if (n == 0 | n==1) return 1; return n * Fact (n-1);}
Fact (4)
3*Fact (2)
4*Fact (3)
2*Fact (1)
24
24
6
2
Fibonacci Series
Fibonacci sequence: 1, 1, 2, 3, 5, 8, 13, 21, ….n= 1 2 3 4
1 for n <= 2fib(n) =
fib(n-2) + fib(n-1) for n>2
for n=4: fib(2)+fib(3)1 + 2 3
Tracing fib(6)
2 1
3 2
4 3
2 1
5
6
4
2 1
3 2
1 1
1
+
Ackermann's function
A(0, n) = n + 1
A(m, 1) = A(m+1, 0)
A(m+1, n+1) = A(m, A(m+1, n))
What Does a Compiler Do?
• Lexical analysis– divide a stream of characters into a stream of
tokens• total = cost + 0.08 * cost;• if ( ( cond1 && ! cond2 ) )
• Parsing– do the tokens form a valid program,– i.e. does it follow the syntax rules?
• Generate object code
BNF (Backus-Naur form)(also called Backus Normal Form)
• a language used to define the syntax rules of a programming language
• consists of– productions – rules for forming some construct
of the language– meta-symbols – symbols of BNF that are NOT
part of the language being compiled– terminals – appear as shown– non-terminals – syntax defined by another
production
BNF Syntax Rules • for a simplified boolean expression – bexpr -> bterm || bexpr | bterm– bterm -> bfactor && bterm | bfactor– bfactor -> !bfactor | (bexpr) | true | false | ident– ident -> alpha { alpha | digit|_}– alpha -> a .. z | A .. Z– digit -> 0 .. 9
• meta-symbols are in red• terminals are in blue• non-terminals are in black• special rules needed for "|" as part of a language
bexpr -> bterm || bterm | bterm• What sequence of tokens is a valid bexpr?• if (there is a valid bterm)
if (nextToken is ||) if (there is a valid bterm) return true else return false else return trueelse return false
• Note: tokenizer must watch for the "||" without stopping at the first "|"