Stacks, Queues and Resursion
Nov 16, 2014
Stacks, Queues and Resursion
Stack• A stack is also known as a Last-In-First-Out
(LIFO) list.• A stack is a linear structure in which items are
added or removed only at one end the top. • The depth of stack is the number of elements
it contains.• An empty stack has depth zero.
Stack Example
Stack Operations
• Some stack operations:
– push - add an item to the top of the stack– pop - remove an item from the top of the stack– peek (or top) - retrieves the top item without
removing it– empty – returns false if stack is empty(stack is empty,
can't pop)– full- returns true if stack is full(stack is full, can't insert
new element)
Implementing Stack using Array
xxx yyy zzz
STACK
1 2 3 4 5 6 7 8
3TOP8MAXSTK
PUSH()
• This procedure pushes an ITEM onto a stack
PUSH(STACK,TOP,MAXSTK,ITEM)1. IF TOP = MAXSTK, then
Print : Overflow, and Return2. Set TOP = TOP + 1 [Increase TOP by 1]3. Set STACK[TOP]= ITEM [Insert ITEM in new TOP position]4. Return
POP()
• This procedure deletes the top element of stack and assigns it to the variable ITEM
POP(STACK,TOP,ITEM)1. IF TOP = 0, then
Print : Underflow, and Return2. Set ITEM = STACK[TOP] [Assigns TOP element to ITEM]3. Set TOP= TOP - 1 [Decreases TOP by 1]4. Return
Limitations
• Limitations
– The maximum size of the stack must be defined a priori and cannot be changed
– Trying to push a new element into a full stack causes an implementation-specific exception
Question
• Suppose STACK is allocated N=6 memory cells and initially STACK is empty. Find the output of the following procedure.
1. Set A=2 and B=52. Call PUSH(STACK,A)
Call PUSH(STACK,4)Call PUSH(STACK,B+2) Call PUSH(STACK,9) Call PUSH(STACK,A+B)
1. Repeat while TO!= 0Call POP(STACK,ITEM)
End of lopp
Queue• A queue is also known as a First-In-First-Out
(FIFO) list.• A queue is a linear list of elements in which
deletions can take place only at one end, called the front, and insertion can take place only at the other end called the rear.
Queue Operations
Some queue operations:
– insert - add an item into a queue– delete - deletes an item from the queue– empty - returns false if the queue is empty– full-return true if queue is full
Enqueue and Dequeue
• Primary queue operations: Enqueue and Dequeue• Like check-out lines in a store, a queue has a front and a rear. • Enqueue
– Insert an element at the rear of the queue• Dequeue
– Remove an element from the front of the queue
Insert (Enqueue)
Remove(Dequeue) rearfront
Implementing a Queue using Array
AAA BBB CCC DDD
QUEUE
1 2 3 4 5 6 7 8
Front:1, Rear:4
BBB CCC DDD
QUEUE
1 2 3 4 5 6 7 8
Front:2, Rear:4
Implementing a Queue using Array
BBB CCC DDD EEE FFF
QUEUE
1 2 3 4 5 6 7 8
Front:2, Rear:6
CCC DDD EEE FFF
QUEUE
1 2 3 4 5 6 7 8
Front:3, Rear:6
Circular Queue
• Suppose we want to insert an element into the queue after it occupies the last part of the array i.e. REAR=n.
• One way of doing it is simply move the entire queue to the beginning of the array, changing FRONT and REAR, and then inserting the element. This procedure may be very expensive.
• This can be done assuming that the array QUEUE is circular that is the QUEUE[1] comes after QUEUE[N] in the array. Instead if increasing REAR to N+1 we reset REAR=1.
QINSERT• This procedure inserts an element ITEM into a queue.
QINSERT(QUEUE,N,FRONT,REAR,ITEM)
[Queue already fill]1. If FRONT=1 and REAR=N, or if FRONT=REAR+1,then
Write: Overflow, and Return
2. [Find new value of REAR]If FRONT=NULL, then [Queue initially empty]
Set FRONT=1 and REAR=1Else if REAR =N then
Set REAR=1Else
Set REAR=REAR+1[End of if structure]
3. Set QUEUE[REAR]=ITEM [This inserts new element]4. Return
QDELETE• This procedure deletes an element from the queue and assigns it to
the variable ITEM.
QDELETE(QUEUE,N,FRONT,REAR,ITEM)
[Queue already empty]1. If FRONT=NULL, then
Write: Underflow, and Return
2. Set ITEM=QUEUE[FRONT]3. [Find new value of FRONT]
If FRONT=REAR, then [Queue has only one element to start]Set FRONT=NULL and REAR=NULL
Else if FRONT =N thenSet FRONT =1
Else Set FRONT = FRONT +1
[End of if structure]
4. Return
Question• Consider a circular queue with six memory cells.QUEUE: _,A,C,D,_,_ Front=2,REAR=4a) Add Fb) Two letters are deletec) Add K,L and Md) Two letters are deletee) Add Rf) Two letters are deleteg) Add Sh) Two letters are deletei) One letters are delete j) One letters are delete
Double-Ended-QUE
• A deque is a double-ended que• Insertions and deletions can occur at either
end but not in the middle• Implementation is similar to that for que• Deque are not heavily used
Double-Ended-QUE
AAA BBB CCC DDD
DEQUE
1 2 3 4 5 6 7 8
Left:4, Right:7
YYY ZZZ WWW XXX
DEQUE
1 2 3 4 5 6 7 8
Left:7, Right:2
Double-Ended-QUE
• There are two variations of deque– Input-restricted deque
An input restricted deque is a deque which allows insertion at only one end of the list but allows deletion a both end of the list.
– Output-restricted dequeAn outut restricted deque is a deque which allows deletion at only one end of the list but allows insertin a both end of the list.
Question• Consider a circular DEQUE with six memory
cells.DEQUE:_,A,C,D,_,_ LEFT=2, RIGHT=4a) Add F to the rightb) Two letters are deleted from the rightc) K,L, and M are added to the leftd) One letter on the left is deletede) R is added to the left f) S is added to the rightg) T is added to the right
Priority Queues
• A priority queue is a collection of elements such that each element has been assigned a priority and such that the order in which the elements are deleted and processed comes from the following rules:1. An element of higher priority is processed before
any element of lower priority.2. Two elements of the same priority are processed
according to the order in which they were add to the queue.
Priority Queues Operations
• insert -Add a new item to the queue.
• delete- Remove and return an item from the queue. The item that is returned is the one with the highest priority.
• empty- Check whether the queue is empty.
Polish Notation
• Polish notation named after Polish mathematician Jan Lukasiewiez, refers to the notation in which the operator symbol is placed before its operands.
• Reverse Polish notation refers to the notation in which operator symbol is placed after its operands.
Evaluation of expression
• An expression is made up of operands, operators, and delimiters.
A/B-C+D*E-A*C• Arithmetic operators
+,-,*,/, %, and unary minus• Relational operators
<,<=,==,<.,>=, >, &&, ||, and !.
Priority of operators
• To fix the order of evaluation, each operator is assigned a priority.
• The C++ rule is that for all priorities, evaluation of operators of the same priority will proceed left to right.
A/B*C will be evaluated as (A/B)*C.X=A/B-C+D*E-A*C will be evaluated asX=(((A/B)-C)+(D*E))-(A*C).
Priority of operators
Postfix notation
• A compiler accepts an expression and produces correct code by reworking the expression into a form called postfix notation.
• The conventional way of writing an expression is called infix- the operators come in-between the operands
• Infix A*B/C has postfix AB*C/.Infix: A/B-C+D*E-A*CPostfix: AB/C-DE*+AC*-
Infix to Postfix: Example
A.3 + 5 * 6 – 7 * (8 + 5)=3 5 6 * + 7 8 5 + * – B.A/B-C+D*E-A*C =AB/C-DE*+AC*-C.5+ 3 + 4 + 1=5 3 + 4 + 1 +D.(5 + 3) * 10= 5 3 + 10 * E. (b * b – 4 * a * c) / (2 * a) =b b * 4 a * c * -2 a
* /
Evaluating Arithmetic Expression
• The computer usually evaluate an arithmetic expression written in infix notation in two steps:1. First it converts the expression to postfix
notation, and
2. Then it evaluates the postfix expression.
Evaluation of Postfix Expression
• This algorithm finds the VALUE of an arithmetic expression P written in postfix notation.
1. Add aright parenthesis “)” at the end of P[This acts as a sentinel]2. Scan P from left to right and repeat step 3 and 4 for each element of P
until the sentinel “)” is encountered.3. If an operand is encountered, put it on STACK4. If an operator Θ is encountered, then
a) Remove the top two elements of STACKb) Evaluate A Θ Bc) Place the result of B back on STACK
[End of If structure]E[End of step 2 loop]
5. Set VALUE equal to the top element on STACK6. Exit
Transforming Infix Expression into Postfix Expression
• Suppose Q is an arithmetic expression written in infix notation. This algorithm finds the equivalent postfix expression P.POLISH(Q,P)
1. Push “(“ onto STACK, and add “)” to the end of Q2. Scan Q from left to right and repeat Step 3 to 6 for each element of Q until the STACK is empty3. If an operand is encountered, add it to P4. If a left parenthesis is encountered, push it onto STACK5. If operator Θ is encountered, then
a) Repeatedly pop from STACK and add to P each operator which has the same precedence as higher precedence than Θb) Add Θ operator to STACK
[End of if structure]6. If a right parenthesis is encountered, then
a) Repeatedly pop from STACK and add to P each operator until a left parenthesis is encountered.b) Remove the left parenthesis [Do not add the left parenthesis to P]
[End of if structure][End of step 2 loop]Exit
Quicksort
Quick sort is an algorithm of divide and conquer type. That is the problem of sorting a set is reduced to the problem of sorting two smaller sets.
Example• Suppose A is the following list of 12 numbers
44,33,11,55,77,90,40,60,99,22,88,66• The reduction step of quick sort algorithm finds the final position of one of the
numbers; in this we use the first number 44. Beginning with the last number, 66, scan the list from right to left, comparing each number with 44 and stopping at the first number less than 44. the number is 22. Interchange 44 and 22 to obtain the list.
22,33,11,55,77,90,40,60,99,44,88,66• Beginning with 22, next scan the list in opposite direction, from left to right,
comparing each number with 44 and stopping at the first number greater than 44. The number is 55. Interchange 44 and 55 to obtain the list.
22,33,11,44,77,90,40,60,99,55,88,66• Beginning this time with 55, now scanning the list in original direction that is from
right to left, until meeting the first number less than 44.It is 40. Interchange 44 and 40 to obtain the list.
22,33,11,40,77,90,44,60,99,55,88,66
22,33,11,40,44,90,77,60,99,55,88,66
Example• Beginning with 77, scan the list from right to left seeking a number less than 44.
We do not meet such number before meeting 44. This mean all the numbers have been scanned and compared with 44. All the numbers less tan 44 now form the sub list to the left of 44, and all numbers greater than 44 form the sub list to the right of 44 as shown below22,33,11,40, 44, 90,77,60,99,55,88,66
First sublist Second sublist
• Thus 44 is correctly placed in its final position, and the task of sorting the original list A has been reduced to the task of sorting each of the above sub list.
• The above reduction step is repeated with each sub list containing 2 or more elements. Since we can process only one sub list at a time, we must be able o keep track of some sub lists for future processing. This is accomplished by processing two stacks, called LOWER and UPPER, to temporarily old such sub list. That is the address of the first and last elements of each sub list, called its boundary values, are pushed onto the stacks LOWER and UPPER, respectively; and the reduction set is applied to a sub list only after its boundary values are removed from the stack.
Quick sort AlgorithmThis algorithm sorts an array A with N elements1. [Initialize] TOP=NULL2. [Push boundary values of A onto stack when A has 2 or more elements]
If N>1, then TOP=TOP+1, LOWER[1]=1 and UPPER[1]=N3. Repeat Step 4 to 7 while TOP!= NULL4. [Pop sub list from stack]
Set BEG=LOWER[TOP], END=UPPER[TOP]TOP=TOP-1
5. Call QUICK(A,N,BEG,END,LOC)6. [Push left sub list onto stacks when it has 2 or more elements]
If BEG<LOC-1 thenTOP=TOP+1, LOWER[TOP]=BEG, UPPER[TOP]=LOC-1
[End of If structure]7. [Push right sub list onto stacks when it has 2 or more elements]
If LOC+1 < END thenTOP=TOP+1, LOWER[TOP]= LOC+1, UPPER[TOP]= END
[End of If structure][End of Step 3 loop]
8. Exit
A is an array with N elements. Parameters BEG and END contain the boundary values of the sub list of A to which this procedure applies. LOC keeps track of the position of the first element A[BEG] of the sub list during the procedure. The local variables LEFT and RIGHT will contain the boundary values of the list of elements that have nor been scanned.QUICK(A,N,BEG,END,LOC)
1. [Initialize] Set LEFT=BEG, RIGHT= END and LOC=BEG2. [Scan from right to left]
a) Repeat while A[LOC]< A[RIGHT] and LOC!=RIGHTRIGHT=RIGHT-1
[End of Loop]b. If LOC=RIGHT, then Returnc. If A[LOC]>A[RIGHT], then
i. [Interchange A[LOC] and A[RIGHT], ] TEMP=A[LOC], A[LOC]=A[RIGHT] A[RIGHT]=TEMP
ii. Set LOC=RIGHTiii. Go to step 3[End of If Structure]
3. [Scan from left to right]a) Repeat while A[LEFT]< A[LOC] and LEFT!=LOC
LEFT=LEFT+1[End of Loop]
b. If LOC=LEFT, then Returnc. If A[LEFT]>A[LOC], then
i. [Interchange A[LEFT] and A[LOC], ] TEMP=A[LOC], A[LOC]=A[LEFT] A[LEFT]=TEMP
ii. Set LOC=LEFTiii. Go to step 2[End of If Structure]
Question
• Suppose S is the following list of alphabetic character:
S=DATASTRUCTURES
Use quick sort algorithm to find the final position of the first character D
CAADSTRUTTURES
Quicksort Analysis
• Assume that keys are random, uniformly distributed.
• What is best case running time?– Recursion:
1. Partition splits array in two sub-arrays of size n/2 2. Quicksort each sub-array
– Depth of recursion tree? O(log2n)
– Number of accesses in partition? O(n)
Quicksort Analysis
• Assume that keys are random, uniformly distributed.
• Best case running time: O(n log2n)
Quicksort Analysis
• Best case running time: O(n log2n)
• Worst case running time? O(n2)
Recursion• A function is said to be recursively defined, if a function
containing either a Call statement to itself or a Call statement to a second function that may eventually result in a Call statement back to the original function.
• A recursive function must have the following properties:
1. There must be certain criteria, called base criteria for which the function does not call itself.
2. Each time the function does call itself(directly or indirectly), the argument of the function must be closer to a base value.
Recursion
• In some problems, it may be natural to define the problem in terms of the problem itself.
• Recursion is useful for problems that can be represented by a simpler version of the same problem.
• Example: the factorial function6! = 6 * 5 * 4 * 3 * 2 * 1
We could write:6! = 6 * 5!
Example 1: factorial function
In general, we can express the factorial function as follows:
n! = n * (n-1)!
The factorial function is only defined for positive integers. So we should be a bit more precise:
if n<=1, then n! = 1
if n>1, then n! = n * (n-1)!
factorial function
The C++ equivalent of this definition:int fac(int numb){
if(numb<=1)
return 1;
else
return numb * fac(numb-1);
}
recursion means that a function calls itself
factorial function• Assume the number typed is 3, that is, numb=3. fac(3) :
int fac(int numb){if(numb<=1)
return 1;else return numb * fac(numb-1);
}
3 <= 1 ? No.fac(3) = 3 * fac(2)fac(2) :
2 <= 1 ? No.
fac(2) = 2 * fac(1)fac(1) :
1 <= 1 ? Yes.return 1
fac(2) = 2 * 1 = 2return fac(2)
fac(3) = 3 * 2 = 6return fac(3)
fac(3) has the value 6
factorial functionFor certain problems (such as the factorial function), a recursive solution often leads to short and elegant code. Compare the recursive solution with the iterative solution:
Iterative solution int fac(int numb){
int product=1; while(numb>1){
product *= numb; numb--;
} return product; }
Recursive solution
int fac(int numb){if(numb<=1)
return 1;else return numb*fac(numb-1);
}
Recursion
To trace recursion, recall that function calls operate as a stack – the new function is put on top of the caller
We have to pay a price for recursion:– calling a function consumes more time and memory than adjusting
a loop counter. – high performance applications (graphic action games, simulations
of nuclear explosions) hardly ever use recursion.
In less demanding applications recursion is an attractive alternative for iteration (for the right problems!)
Recursion
If we use iteration, we must be careful not to create an infinite loop by accident:
for(int incr=1; incr!=10;incr+=2) ...
int result = 1;while(result >0){ ... result++;}
RecursionSimilarly, if we use recursion we must be careful not to create an infinite chain of function calls:
int fac(int numb){
return numb * fac(numb-1);}
Or: int fac(int numb){
if (numb<=1) return 1; else return numb * fac(numb+1);}
No termination No termination conditioncondition
Recursion
We must always make sure that the recursion bottoms out:
• A recursive function must contain at least one non-recursive branch.
• The recursive calls must eventually lead to a non-recursive branch.
Recursion
• Recursion is one way to decompose a task into smaller subtasks. At least one of the subtasks is a smaller example of the same task.
• The smallest example of the same task has a non-recursive solution.
Example: The factorial functionn! = n * (n-1)! and 1! = 1
Example 2: Fibonacci numbers• Fibonacci numbers:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
where each number is the sum of the preceding two.
• Recursive definition:– F(0) = 0;– F(1) = 1;– F(number) = F(number-1)+ F(number-2);
Example 3: Fibonacci numbers//Calculate Fibonacci numbers using recursive function.//A very inefficient way, but illustrates recursion well
int fib(int number){if (number == 0) return 0;if (number == 1) return 1;return (fib(number-1) + fib(number-2));
}
Trace a Fibonacci Number • Assume the input number is 4, that is, num=4:fib(4):
4 == 0 ? No; 4 == 1?No.fib(4) = fib(3) + fib(2)fib(3):
3 == 0 ? No; 3 == 1? No.fib(3) = fib(2) + fib(1) fib(2):
2 == 0? No; 2==1? No.fib(2) = fib(1)+fib(0)
fib(1): 1== 0 ? No; 1 == 1? Yes. fib(1) = 1;
return fib(1);
int fib(int num){
if (num == 0) return 0;if (num == 1) return 1;return
(fib(num-1)+fib(num-2));}
Trace a Fibonacci Number
fib(0): 0 == 0 ? Yes. fib(0) = 0; return fib(0);
fib(2) = 1 + 0 = 1; return fib(2);
fib(3) = 1 + fib(1) fib(1):
1 == 0 ? No; 1 == 1? Yes fib(1) = 1; return fib(1);
fib(3) = 1 + 1 = 2; return fib(3)
Trace a Fibonacci Numberfib(2):2 == 0 ? No; 2 == 1? No.fib(2) = fib(1) + fib(0)fib(1):
1== 0 ? No; 1 == 1? Yes. fib(1) = 1;
return fib(1); fib(0): 0 == 0 ? Yes. fib(0) = 0; return fib(0);
fib(2) = 1 + 0 = 1; return fib(2);
fib(4) = fib(3) + fib(2) = 2 + 1 = 3; return fib(4);
Fibonacci number w/o recursion//Calculate Fibonacci numbers iteratively//much more efficient than recursive solution
int fib(int n){int f[n+1]; f[0] = 0; f[1] = 1;
for (int i=2; i<= n; i++) f[i] = f[i-1] + f[i-2];return f[n];
}
Example 3: Binary Search
– Search for an element in an array• Sequential search• Binary search
– Binary search• Compare the search element with the middle
element of the array• If not equal, then apply binary search to half of
the array (if not empty) where the search element would be.
Binary Search with Recursion
// Searches an ordered array of integers using recursionint bsearchr(const int data[], // input: array int first, // input: lower bound int last, // input: upper bound int value // input: value to find )// output: index if found, otherwise return –1
{ int middle = (first + last) / 2; if (data[middle] == value) return middle; else if (first >= last) return -1; else if (value < data[middle]) return bsearchr(data, first, middle-1, value); else return bsearchr(data, middle+1, last, value);}
Binary Searchint main() {
const int array_size = 8;
int list[array_size]={1, 2, 3, 5, 7, 10, 14, 17};
int search_value;
cout << "Enter search value: ";
cin >> search_value;
cout << bsearchr(list,0,array_size-1,search_value)
<< endl;
return 0;
}
Binary Search w/o recursion// Searches an ordered array of integersint bsearch(const int data[], // input: array int size, // input: array size int value // input: value to find ){ // output: if found,return // index; otherwise, return -1
int first, last, upper; first = 0; last = size - 1;while (true) {
middle = (first + last) / 2; if (data[middle] == value) return middle; else if (first >= last) return -1; else if (value < data[middle]) last = middle - 1; else first = middle + 1; }}
Example 4: Towers of Hanoi
– Only one disc could be moved at a time– A larger disc must never be stacked above a
smaller one– One and only one extra needle could be used for
intermediate storage of discs
A B C
3
2
1
Towers of Hanoi
• From the moves necessary to transfer one, two, and three disks, we can find a recursive pattern - a pattern that uses information from one step to find the next step.
• If we want to know how many moves it will take to transfer 64 disks from post A to post C, we will first have to find the moves it takes to transfer 63 disks, 62 disks, and so on.
a) The initial state; b) move n - 1 disks from A to C
c) move one disk from A to B; d) move n - 1 disks from C to B
Towers of Hanoi• The recursive pattern can help us generate more numbers to find
an explicit (non-recursive) pattern. Here's how to find the number of moves needed to transfer larger numbers of disks from post A to post C, when M = the number of moves needed to transfer n-1 disks from post A to post C:
• for 1 disk it takes 1 move to transfer 1 disk from post A to post C;• for 2 disks, it will take 3 moves: 2M + 1 = 2(1) + 1 = 3 • for 3 disks, it will take 7 moves: 2M + 1 = 2(3) + 1 = 7• for 4 disks, it will take 15 moves: 2M + 1 = 2(7) + 1 = 15• for 5 disks, it will take 31 moves: 2M + 1 = 2(15) + 1 = 31• for 6 disks... ?
Towers of Hanoi
Number of Disks (n) Number of Moves
1 21 - 1 = 2 - 1 = 1 2 22 - 1 = 4 - 1 = 3 3 23 - 1 = 8 - 1 = 7 4 24 - 1 = 16 - 1 = 15 5 25 - 1 = 32 - 1 = 316 26 - 1 = 64 - 1 = 63
• So the formula for finding the number of steps it takes to transfer n disks from post A to post C is:
2 n - 1
Binomial coefficient
• Given a non-negative integer n and an integer k, the binomial coefficient is defined to be the natural number
• Example: