Sei sulla pagina 1di 71

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

STACK

xxx yyy zzz

1 2 3 4 5 6 7 8

MAXSTK 8
TOP 3
PUSH()
• This procedure pushes an ITEM onto a stack

PUSH(STACK,TOP,MAXSTK,ITEM)
1. IF TOP = MAXSTK, then

Print : Overflow, and Return


1. Set TOP = TOP + 1 [Increase TOP by 1]
2. Set STACK[TOP]= ITEM [Insert ITEM in new TOP position]
3. 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 Return


1. Set ITEM = STACK[TOP] [Assigns TOP element to ITEM]
2. Set TOP= TOP - 1 [Decreases TOP by 1]
3. 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=5
2. 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!= 0
Call 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

Remove Insert
(Dequeue) front rear (Enqueue)
Implementing a Queue
Front:1, Rear:4
using Array
QUEUE

AAA BBB CCC DDD

1 2 3 4 5 6 7 8

Front:2, Rear:4 QUEUE

BBB CCC DDD

1 2 3 4 5 6 7 8
Implementing a Queue
using Array
Front:2, Rear:6 QUEUE

BBB CCC DDD EEE FFF

1 2 3 4 5 6 7 8

Front:3, Rear:6 QUEUE

CCC DDD EEE FFF

1 2 3 4 5 6 7 8
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
1. [Find new value of REAR]
If FRONT=NULL, then [Queue initially empty]
Set FRONT=1 and REAR=1
Else if REAR =N then
Set REAR=1
Else
Set REAR=REAR+1
[End of if structure]
1. Set QUEUE[REAR]=ITEM [This inserts new element]
2. 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
1. Set ITEM=QUEUE[FRONT]
2. [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 then
Set FRONT =1
Else
Set FRONT = FRONT +1
[End of if structure]
1. Return
Question
• Consider a circular queue with six
memory cells.
QUEUE: _,A,C,D,_,_ Front=2,REAR=4
a) Add F
b) Two letters are delete
c) Add K,L and M
d) Two letters are delete
e) Add R
f) Two letters are delete
g) Add S
h) Two letters are delete
i) 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
Left:4, Right:7 DEQUE

AAA BBB CCC DDD

1 2 3 4 5 6 7 8

Left:7, Right:2 DEQUE

YYY ZZZ WWW XXX

1 2 3 4 5 6 7 8
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 deque
An 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=4
a)Add F to the right
b)Two letters are deleted from the right
c)K,L, and M are added to the left
d)One letter on the left is deleted
e)R is added to the left
f) S is added to the right
g)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 as
X=(((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*C
Postfix: AB/C-DE*+AC*-
Infix to Postfix: Example
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 STACK
4. If an operator Θ is encountered, then
a) Remove the top two elements of STACK
b) Evaluate A Θ B
c) Place the result of B back on STACK
[End of If structure]
E[End of step 2 loop]
1. Set VALUE equal to the top element on STACK
2. 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 Q
2. Scan Q from left to right and repeat Step 3 to 6 for each element of Q until the STACK is empty
3. If an operand is encountered, add it to P
4. If a left parenthesis is encountered, push it onto STACK
5. 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 below
22,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 Algorithm
This algorithm sorts an array A with N elements
1. [Initialize] TOP=NULL
2. [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]=N
3. Repeat Step 4 to 7 while TOP!= NULL
4. [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 then
TOP=TOP+1, LOWER[TOP]=BEG, UPPER[TOP]=LOC-1
[End of If structure]
5. [Push right sub list onto stacks when it has 2 or more elements]
If LOC+1 < END then
TOP=TOP+1, LOWER[TOP]= LOC+1, UPPER[TOP]= END
[End of If structure]
[End of Step 3 loop]
5. 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=BEG


2. [Scan from right to left]
a) Repeat while A[LOC]< A[RIGHT] and LOC!=RIGHT
RIGHT=RIGHT-1
[End of Loop]
b. If LOC=RIGHT, then Return
c. 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=RIGHT
iii. 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 Return
c. 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=LEFT
iii. 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
CAADSTRUTTU
RES
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 function
6! = 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) :
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 int fac(int numb){
fac(2) = 2 * 1 = 2 if(numb<=1)
return fac(2) return 1;
fac(3) = 3 * 2 = 6 else
return fac(3) return numb * fac(numb-1);
}
fac(3) has the value 6
factorial function
For 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
Recursive solution
solution
int fac(int numb){ int fac(int numb){
if(numb<=1) int product=1;
return 1; while(numb>1){
else product *= numb;
return numb*fac(numb-1);numb--;
} }
return product;
}
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++;
}
Recursion
Similarly, 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); No termination
} condition
Or:
int fac(int numb){
if (numb<=1)
return 1;
else
return numb * fac(numb+1);
}
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 function


n! = 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
int fib(int num)
{
• Assume the input number is 4, that is, num=4:if (num == 0) return 0;
if (num == 1) return 1;
fib(4): return
4 == 0 ? No; 4 == 1? No. (fib(num-1)+fib(num-2));
}
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);
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 Number
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);
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 recursion
int 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 Search
int 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 integers
int 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

1
2
3
A B C
– 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
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 = 31
6 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:

Potrebbero piacerti anche