Chapter Stacks and Queues Stacks Stacks Using Dynamic Arrays Queues Circular Queues Using Dynamic Arrays A Maze Problem Evaluation of Expressions Multiple Stacks and Queues C-C Tsai P. Stacks A stack is an ordered list in which insertions and deletions are made at one end called the top. A stack is also known as a First-In-Last-Out (FILO) or Last-In-First-Out (LIFO) list. A top B A top C B A top D C B A top E D C B A top D C B A top push(b) push(c) push(d) push(e) pop C-C Tsai P.
Stack of Cups A stack is a linear list, one end is called top and other end is called bottom. Additions to and removals from the top end only. Example: Add a cup to the stack. top E top F E D D C C bottom B A bottom B A C-C Tsai P. Application: Stack frame of function call A program places an activation record or a stack frame on top of the system stack when it invokes a function. fp: a pointer to current stack frame system stack before a is invoked system stack after a is invoked C-C Tsai P.4
Example of function call #include <stdio.h> main() { int x; x = fact(5); int fact(int n) { if (n>) return n*fact(n-); else return ; X =? invoke fact(5) invoke fact(4) invoke fact() invoke fact() invoke fact() return from fact() = return from fact() = return from fact() = 6 return from fact(4) = 4 return from fact(5) = X = C-C Tsai P.5 Abstract Data Type for Stack ADT Stack is objects: a finite ordered list with zero or more elements. functions: for all stack Stack, item element, max_stack_size positive integer Stack CreateS(max_stack_size) ::= create an empty stack whose maximum size is max_stack_size Boolean IsFull(stack, max_stack_size) ::= if (number of elements in stack == max_stack_size) return TRUE else return FALSE Stack Push(stack, item) ::= if (IsFull(stack)) stackfull else insert item into top of stack and return Boolean IsEmpty(stack) ::= if(stack == CreateS(max_stack_size)) return TRUE else return FALSE Element Pop(stack) ::= if(isempty(stack)) return else remove and return the item on the top of the stack. C-C Tsai P.6
Stacks Using Dynamic Arrays Stack using static array Stack CreateS() ::= #define MAX_STACK_SIZE typedef struct { int key; /* other fields */ element; element stack[max_stack_size]; int top = -; Boolean IsEmpty(Stack) ::= top < ; Boolean IsFull(Stack) ::= top >= MAX_STACK_SIZE -; C-C Tsai P.7 Implementation of Push and Pop void Push(element item) { /* add an item to the global stack */ if (top >= MAX_STACK_SIZE-) stackfull( ); stack[++top] = item; a b c d e element Pop() 4 { /* delete and return the top element from the stack */ if (top == -) return stack_empty( ); /* returns an error key */ return stack[top--]; void StackFull() /* for Static Array */ { fprintf(stderr, Stack is full, cannot add element. ); exit(exit_failure); top C-C Tsai P.8 4
Stack Using Dynamic Array Stack CreateS() ::= typedef struct { int key; /* other fields */ element; element *stack; MALLOC(stack, sizeof(*stack)); int capacity = ; int top = -; Boolean IsEmpty(Stack) ::= top< ; Boolean IsFull(Stack) ::= top >= capacity-; C-C Tsai P.9 Array Doubling for StackFull() Dynamic Array Doubling : When stack is full, double the capacity using REALLOC void StackFull() { REALLOC(stack, *capacity*sizeof(*stack); capacity *= ; Let final value of capacity be k, k > Number of pushes is at least k- + Total time spent on array doubling is Σ <=i=k i, This is O( k ) So, although the time for an individual push is O(capacity), the time for all n pushes remains O(n). C-C Tsai P. 5
Queues A queue is an ordered list in which all insertions take place at one end called rear and all deletions take place at the opposite end called front. A queue is also known as a First-In-First-Out (FIFO) or Last-In- Last-Out (LILO) list. Example: Bus stop queue: Bus Stop front rear rear C-C Tsai P. Queues A queue is an ordered list in which all insertions take place at one end called rear and all deletions take place at the opposite end called front. A queue is also known as a First-In-First-Out (FIFO) or Last-In- Last-Out (LILO) list. Example: Bus stop queue: Bus Stop rear front rear C-C Tsai P. 6
Queues A queue is an ordered list in which all insertions take place at one end called rear and all deletions take place at the opposite end called front. A queue is also known as a First-In-First-Out (FIFO) or Last-In- Last-Out (LILO) list. Example: Bus stop queue: Bus Stop front rear C-C Tsai P. Queues A queue is an ordered list in which all insertions take place at one end called rear and all deletions take place at the opposite end called front. A queue is also known as a First-In-First-Out (FIFO) or Last-In- Last-Out (LILO) list. Bus stop queue: Bus Stop front rear C-C Tsai P.4 7
Representation of a Queue A queue may be implemented with a linear array and two pointers: front and rear. Initially, front = rear = -. Example: A rear front B A Add(B) rear front C B A Add(C) rear front D C B A Add(D) rear front D C B Delete rear front C-C Tsai P.5 Application: Job Scheduling front rear Q[] Q[] Q[] Q[] Comments - - - - - J J J J J J J J J queue is empty Job is added Job is added Job is added Job is deleted Job is deleted C-C Tsai P.6 8
Abstract Data Type of Queue ADT Queue is objects: a finite ordered list with zero or more elements. functions: for all queue Queue, item element, max_ queue_ size positive integer Queue CreateQ(maxQueueSize) ::= create an empty queue whose maximum size is max_queue_size Boolean IsFullQ(queue, maxqueuesize) ::= if(number of elements in queue == max_queue_size) return TRUE else return FALSE Queue AddQ(queue, item) ::= if (IsFullQ(queue)) queuefull else insert item at rear of queue and return queue Boolean IsEmptyQ(queue) ::= if (queue ==CreateQ(maxQueueSize)) return TRUE else return FALSE Element DeleteQ(queue) ::= if (IsEmptyQ(queue)) return C-C Tsai else remove and return the item at front of queue. P.7 Queues Using Dynamic Arrays Queue using static array Queue CreateQ(maxQueueSize) ::= # define MAX_QUEUE_SIZE typedef struct { int key; /* other fields */ element; element queue[max_queue_size]; int rear = -; int front = -; Boolean IsEmptyQ(queue) ::= front == rear Boolean IsFullQ(queue) ::= rear == MAX_QUEUE_SIZE- C-C Tsai P.8 9
Implementation of AddQ and DeleteQ void AddQ(element item) { /* add an item to the queue */ if (rear == MAX_QUEUE_SIZE-) queuefull(); queue [++rear] = item; element DeleteQ() { /* remove element at the front of the queue */ if (front == rear) return queueempty( ); /* return an error key */ return queue [++front]; C-C Tsai P.9 Circular Array for Queues Problem: Two pointers only increments, never decrements. We eventually fall off the right end of the array. This problem can be solved by periodically moving the elements to the left, to make room on the right end. We could use a circular array plus two pointers to implement a queue. The front index always points one position counterclockwise from the first element in the queue. The rear index points to the current end of the queue. [] [] [] [] J J [] [4] [] J [4] [] [5] [] [5] front = front = rear = rear = C-C Tsai P.
Circular Array for Queues (Cont d) Though there are MAX_Queue_SIZE slots in the circular array, we can store at most MAX_Queue_SIZE - elements in the circular array at any instant. Examples of full queues: [] [] [] [] J J J8 J9 [] J J4 [4][] J7 [4] J5 J6 J5 [] [5] [] [5] front = rear = 5 front =4 rear = C-C Tsai P. Implementation of Circular Array void AddQ(element item) { /* add an item to the queue */ rear = (rear +) % MAX_QUEUE_SIZE; if (front == rear) queuefull(); /* print error and exit */ queue[rear] = item; element DeleteQ() { /* remove front element from the queue and put it in item */ element item; if (front == rear) return queueempty( ); /* return an error key */ front = (front+) % MAX_QUEUE_SIZE; return queue[front]; C-C Tsai P.
Circular Queues Using Dynamic Allocated Arrays Problem: Add an element to a full queue, we must first increase the size of this array using a function such as REALLOC. As with dynamically allocated stacks, we use array doubling. Example: Full queue: 98 5 84 77 56 8 6 front rear Doubling queue: 98 5 84 77 56 8 6 front rear C-C Tsai P. Implementation of Doubling Circular Array void AddQ(element item) { /* add an item to the queue */ rear = (rear +) % capacity; if (front == rear) queuefull(); /* double capacity */ queue[rear] = item; C-C Tsai P.4
A Mazing Problem How to solve the maze problem? In the real world, touch the wall. In the computer, try all paths systematically. entrance : blocked path : through path exit C-C Tsai P.5 Allowable Moves in Maze There are eight possible moves for the current position. NW [row-][col-] N [row-][col] NE [row-][col+] W [row][col-] SW [row+][col-] [row][col] S [row+][col] E [row][col+] SE [row+][col+] Name Dir move[dir].vert move[dir].horiz N NE E SE S SW W NW 4 5 6 7 - - - - - - C-C Tsai P.6
Representation of a Maze A possible implementation typedef struct { next_row = row + move[dir].vert; short int vert; next_col = col + move[dir].horiz; short int horiz; offsets; /*array of moves for each direction */ offsets move[8]; Alternative implementation using stack to keep pass history #define MAX_STACK_SIZE /*maximum stack size*/ typedef struct { short int row; short int col; short int dir; element; element stack[max_stack_size]; C-C Tsai P.7 Implementation: Use Stack Initialize a stack to the maze s entrance coordinates and direction to north; while (stack is not empty) { /* move to position at top of stack */ <row, col, dir> = delete from top of stack; while (there are more moves from current position) { <next_row, next_col > = coordinates of next move; dir = direction of move; if ((next_row == EXIT_ROW)&& (next_col == EXIT_COL)) success; if (maze[next_row][next_col] == && mark[next_row][next_col] == ) { /* legal move and haven t been there */ mark[next_row][next_col] = ; /* save current position and direction */ add <row, col, dir> to the top of the stack; row = next_row; col = next_col; dir = north; O(mp) where m and p are the number printf( No path found\n ); of rows and columns C-C Tsai P.8 4
Initial State A Mazing Example Mark (,) 4 (,,) Second State Mark (,) (,) 4 (,,) (,,) C-C Tsai P.9 A Mazing Example (Cont d) Third State 4 (,,) (,,) (,,) Mark (,) (,) (,) Forth State 4 (,,5) (,,) (,,) Mark (,) (,) (,) (,) C-C Tsai P. 5
A Mazing Example (Cont d) Fifth State 4 (4,,) (,,5) (,,) (,,) Mark (,) (,) (,) (,) (4,) Sixth State 4 (4,,) (4,,) (,,5) (,,) (,,) Mark (,) (,) (,) (,) (4,) (4,) C-C Tsai P. Analysis of path There are 8 directions of movements. We assume there is an outer border. So an m * n maze is represented as an (m + ) * (n + ) binary array. m*p The size of the maze determines the computing time of path. Since each position within the maze is visited no more than once, the worst case complexity of the algorithm is O(mp). C-C Tsai P. 6
Maze Search Function void path (void) /* output a path through the maze if such a path exists */ { int i, row, col, next_row, next_col, dir, found = FALSE; element position; mark[][] = ; top =; stack[].row = ; stack[].col = ; stack[].dir = ; while (top > - &&!found) { position = delete(&top); row = position.row; col = position.col; dir = position.dir; while (dir < 8 &&!found) { /*move in direction dir */ next_row = row + move[dir].vert; next_col = col + move[dir].horiz; if (next_row==exit_row && next_col==exit_col) found = TRUE; else if (!maze[next_row][next_col] &&!mark[next_row][next_col] { mark[next_row][next_col] = ; position.row = row; position.col = col; position.dir = ++dir; add(&top, position); row = next_row; col = next_col; dir = ; else ++dir; (,) (m,p) (m+)*(p+) 7 N 6 W E 5 S 4 C-C Tsai P. Maze Search Function (Cont d) if (found) { printf( The path is :\n ); printf( row col\n ); for (i = ; i <= top; i++) printf( %d%5d, stack[i].row, stack[i].col); printf( %d%5d\n, row, col); printf( %d%5d\n, EXIT_ROW, EXIT_COL); else printf( The maze does not have a path\n ); C-C Tsai P.4 7
Lee s Routing start pin end pin Label all reachable squares unit from start. C-C Tsai P.5 Lee s Wire Router start pin end pin Label all reachable unlabeled squares units from start. C-C Tsai P.6 8
Lee s Wire Router start pin end pin Label all reachable unlabeled squares units from start. C-C Tsai P.7 Lee s Wire Router start pin end pin Label all reachable unlabeled squares 4 units from start. C-C Tsai P.8 9
Lee s Wire Router start pin end pin 4 4 4 4 4 Label all reachable unlabeled squares 5 units from start. C-C Tsai P.9 Lee s Wire Router start pin end pin 5 4 5 4 4 4 4 5 5 5 5 Label all reachable unlabeled squares 6 units from start. C-C Tsai P.4
Lee s Wire Router start pin end pin 6 5 6 4 5 4 4 4 4 5 5 5 5 6 6 6 6 6 6 End pin reached. Traceback. C-C Tsai P.4 Lee s Wire Router start pin end pin 6 5 6 4 5 4 4 4 4 5 5 5 6 5 6 6 6 6 6 End pin reached. Traceback. C-C Tsai P.4
Evaluation of Expressions X = a / b - c + d * e - a * c Assume that a = 4, b = c =, d = e = Interpretation : ((4/)-)+(*)-(4*)= + 9-8= Interpretation : (4/(-+))*(-4)*=(4/)*(-)*=-.66666 How to generate the machine instructions corresponding to a given expression? precedence rule + associative rule C-C Tsai P.4 Precedence Hierarchy In any programming language, a precedence hierarchy determines the order in which we evaluate operators. Operators with highest precedence are evaluated first. With right associative operators of the same precedence, we evaluate the operator furthest to the right first. Expressions are always evaluated from the innermost parenthesized expression first. C-C Tsai P.44
Precedence Hierarchy for C Token Operator Precedence Associativity ( ) [ ] ->. function call array element struct or union member 7 left-to-right -- ++ increment, decrement 6 left-to-right -- ++! - - + & * sizeof decrement, increment logical not one s complement unary minus or plus address or indirection size (in bytes) 5 right-to-left (type) type cast 4 right-to-left * / % mutiplicative Left-to-right.The precedence column is taken from Harbison and Steele..Postfix form C-C Tsai.Prefix form P.45 + - binary add or subtract left-to-right << >> shift left-to-right > >= relational left-to-right < <= ==!= equality 9 left-to-right & bitwise and 8 left-to-right ^ bitwise exclusive or 7 left-to-right bitwise or 6 left-to-right && logical and 5 left-to-right logical or 4 left-to-right?: conditional right-to-left = += -= assignment right-to-left /= *= %= <<= >>= &= ^= =, comma left-to-right C-C Tsai P.46
Infix and Postfix Notation Infix Postfix +*4 4*+ a*b+5 ab*5+ (+)*7 +7* a*b/c ab*c/ (a/(b- c+d))*(e-a)*c abc-d+/ ea- *c* a/b- c+d*e- a*c ab/c- de*+ac*- Postfix: no parentheses, no precedence Two questions:. How to evaluate an expression in postfix notation?. How to change infix notation into postfix notation? C-C Tsai P.47 Evaluating Postfix Expressions Evaluation process Make a single left-to-right scan of the expression. Place the operands on a stack until an operator is found. Remove, from the stack, the correct numbers of operands for the operator, perform the operation, and place the result back on the stack. Example: 6 / 4 * + Token Stack Top [] [] [] 6 6 6 / 6/ 6/ - 4 6/- 6/- 4 6/- 4 * 6/- 4* + 6/-+4* C-C Tsai P.48 4
Function to Evaluate a Postfix Expression Assumptions: operators: +, -, *, /, % operands: single digit integer #define MAX_STACK_SIZE /* maximum stack size */ #define MAX_EXPR_SIZE /* max size of expression */ typedef enum{lparen, rparen, plus, minus, times, divide, mod, eos, operand precedence; int stack[max_stack_size]; /* global stack */ char expr[max_expr_size]; /* input string */ int eval(void) { /* evaluate a postfix expression, expr, maintained as a global variable, \ is the the end of the expression. The stack and top of the stack are global variables. get_token is used to return the token type and the character symbol. Operands are assumed to be single character digits */ precedence token; char symbol; int op, op; int n = ; /* counter for the expression string */ int top = -; token = get_token(&symbol, &n); C-C Tsai P.49 Function to Evaluate a Postfix Expression (Cont d) while (token!= eos) { if (token == operand) add(&top, symbol- ); /* stack insert */ else { /* remove two operands, perform operation, and return result to the stack */ op = delete(&top); /* stack delete */ op = delete(&top); switch(token) { case plus: add(&top, op+op); break; case minus: add(&top, op-op); break; case times: add(&top, op*op); break; case divide: add(&top, op/op); break; case mod: add(&top, op%op); token = get_token (&symbol, &n); return delete(&top); /* return result */ C-C Tsai P.5 5
Getting Token precedence get_token(char *symbol, int *n) { /* get the next token, symbol is the character representation, which is returned, the token is represented by its enumerated value, which is returned in the function name */ *symbol =expr[(*n)++]; switch (*symbol) { case ( : return lparen; case ) : return rparen; case + : return plus; case - : return minus; case / : return divide; case * : return times; case % : return mod; case \ : return eos; default : return operand; /* no error checking, default is operand */ C-C Tsai P.5 Infix to Postfix Two-pass algorithm: Fully parenthesize expression a / b -c + d * e -a* c --> ((((a / b) - c) + (d * e)) - a * c)) All operators replace their corresponding right parentheses. ((((a / b) - c) + (d * e)) - a * c)) Delete all parentheses. ab/c-de*+ac*- C-C Tsai P.5 6
Example: Infix to Postfix The orders of operands in infix and postfix are the same. a + b * c, * > + Token a + b * c eos Stack [] [] [] + + + * + * Top Output C-C Tsai P.5 - - a a ab ab abc abc*+ Example: Infix to Postfix Token a * ( b + c ) * d eos a * (b +c) * d Stack [] [] [] * * ( * ( * ( + * ( + * * match ) * = * * * Top - Output a a a ab ab abc abc+ abc+* abc+* d abc+* d* C-C Tsai P.54 7
Rules () Operators are taken out of the stack as long as their in-stack precedence is higher than or equal to the incoming precedence of the new operator. () ( has low in-stack precedence (isp), and high incoming precedence (icp). ( ) + - * / % eos isp 9 icp 9 C-C Tsai P.55 Precedence-based postfix() The left parenthesis is placed in the stack whenever it is found in the expression, but it is unstacked only when its matching right parenthesis is found. We remove an operator from the stack only if its isp is greater than or equal to the icp of the new operator. precedence stack[max_stack_size]; /* isp and icp arrays -- index is value of precedence lparen, rparen, plus, minus, times, divide, mod, eos */ static int isp [ ] = {, 9,,,,,, ; static int icp [ ] = {, 9,,,,,, ; void postfix(void) { /* output the postfix of the expression. The expression string, the stack, and top are global */ char symbol; precedence token; int n = ; int top = ; /* place eos on stack */ stack[] = eos; C-C Tsai P.56 8
Precedence-based postfix() for (token = get _token(&symbol, &n); token!= eos; token = get_token(&symbol, &n)) { if (token == operand) printf ( %c, symbol); else if (token == rparen ){ /*unstack tokens until left parenthesis */ while (stack[top]!= lparen) print_token(delete(&top)); delete(&top); /*discard the left parenthesis */ else{ /* remove and print symbols whose isp is greater than or equal to the current token s icp */ while(isp[stack[top]] >= icp[token] ) print_token(delete(&top)); add(&top, token); while ((token = delete(&top))!= eos) print_token(token); print( \n ); f(n)=θ(g(n)) iff there exist positive constants c, c, and n such that c g(n) f(n) c g(n) for all n, n n. f(n)=θ(g(n)) iff g(n) is both an upper and lower bound on f(n). C-C Tsai P.57 Analysis of postfix() Let n be the number of tokens in the expression. θ(n) time is spent extracting tokens and outputting them. Time is spent in the two while loops. The total time spent here is θ(n) as the number of tokens that get stacked and unstacked is linear in n. So, the complexity of function postfix() is θ(n). C-C Tsai P.58 9
Infix and Prefix Notation () evaluation () transformation Infix a*b/c a/b-c+d*e-a*c a*(b+c)/d-g Prefix /*abc -+-/abc*de*ac -/*a+bcdg C-C Tsai P.59 Multiple Stacks and Queues When we only need two stacks, we can put the two stacks on the two ends of an array. When we need more than two stacks, we can: Use a fixed partition for each stack. Use a variable partition for each stack. When stack is full, then Find a larger, free space. Move the related stacks around. C-C Tsai P.6
Implementation of Multiple Stacks Initially, boundary[i]=top[i]. [ m/n ] [ m/n ] m- boundary[ ] boundary[] boundary[ ] boundary[n] top[ ] top[ ] top[ ] All stacks are empty and divided into roughly equal segments. C-C Tsai P.6 Implementation of Multiple Stacks #define MEMORY_SIZE /* size of memory */ #define MAX_STACKS /* max number of stacks plus */ element memory[memory_size]; int top[max_stacks]; int boundary[max_stacks]; int n; /* number of stacks entered by the user */ top[] = boundary[] = -; for (i = ; i < n; i++) void push(int i, element item) top[i] =boundary[i] =(MEMORY_SIZE/n)*i; { /* add an item to the ith stack */ boundary[n] = MEMORY_SIZE-; if (top[i] == boundary [i+]) stackfull(i); memory[++top[i]] = item; element pop(int i) { /* remove top element from the ith stack */ if (top[i] == boundary[i]) return stackempty(i); return memory[top[i]--]; C-C Tsai P.6
When Stack is Full If there is space available (in memory), it should shift the stacks so that space is allocated to the full stack. Determine the least, j, stack i < j < n, such that there is free space between stacks j and j+. If there is such a j, then move stacks stack i +, stack i +,, j one position to the right. This creates a space between stack i and i+. If there is no such j, then look to the left of stack stack i. Find the largest j such that j < stack i and there is space between stacks j and j+. If there is such a j, then move stacks j+, j+,, stack i one position to the left. In the worst case, the function, stackfull, has a time complexity of O(MEMORY_SIZE). C-C Tsai P.6 When Stack is Full (Cont d) Find j, stack_no < j < n such that top[j] < boundary[j+] or, j < stack_no b[] t[] b[] t[] b[i] t[i] t[i+] t[j] b[j+] b[n] b[i+] b[i+] b=boundary, t=top C-C Tsai P.64