1 stacks (walls & mirrors - chapter 6). 2 overview the adt stack array implementation of a stack...

Post on 21-Dec-2015

228 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

1

Stacks

(Walls & Mirrors - Chapter 6)

2

Overview

• The ADT Stack

• Array Implementation of a Stack

• Linked-List Implementation of a Stack

• Application Domain: Algebraic Expressions

• Application Domain: Search

3

The ADT Stack

• A stack is a linear list, for which all insertions and deletions occur at one end of the list. Stacks exhibit a Last In First Out (LIFO) property.

• This is like a cafeteria tray (or plate) dispenser.

IN OUT

4

The ADT Stack (Cont’d.)

Recall, that an abstract data type (ADT) is a type of data, defined by the programmer, where the implementation details are hidden from the user, and access to the data is provided through an interface to a specific set of operations.

5

The ADT Stack (Cont’d.)

• For an ADT stack, these operations include:

– Create an empty stack

– Destroy a stack

– Determine whether a stack is empty

– Add a new item to the stack

– Remove the item added most recently

– Retrieve a copy of the item added most recently

• As long as these operations are supported, you don’t need to be concerned with whether the stack is implemented as an array, a linked list, or something else.

6

ADT Stack: Public Member Functions

Stack( );

is the default constructor, which creates a new, empty stack.

Stack( const Stack &oStack );

is the copy constructor, which makes a copy of oStack.

~Stack( );

is the destructor, which destroys a stack.

bool isEmpty( ) const;

returns true if the stack is empty, otherwise returns false.

7

ADT Stack: Public Member Functions

bool push( StackItemType newItem );

adds newItem to the top of a stack. Returns true if push succeeds, otherwise returns false.

bool pop( );

removes the item at the top of a stack. Returns true if pop succeeds, otherwise returns false.

bool pop( StackItemType &stackTop );

removes the item at the top of a stack and returns it in stackTop. Returns true if pop succeeds, otherwise returns false.

8

ADT Stack: Public Member Functions

bool getTop( StackItemType &stackTop ) const;

retrieves a copy of the item at the top of a stack, leaving the stack unchanged. Returns true if getTop succeeds, otherwise returns false.

9

ADT Stack: Array Implementation

const int MAX_STACK = 20;// maximum Stack size

typedef int StackItemType; // items in Stack are int’s

class Stack

{

public:

// declarations of public member functions

private:

StackItemType items[MAX_STACK]; // array of Stack items

int top; // index to top of Stack

};

10

ADT Stack: Member Function Definitions

Stack::Stack( ) : top( -1 ) { } // default constructor, which

// creates a new Stack and

// initializes top to -1

Stack::Stack( const Stack &oStack ) // copy constructor,

: top( oStack.top ) // which copies oStack

{ // to a new Stack

for( int i = 0; i <= oStack.top; i++ )

items[i] = oStack.items[i];

}

11

ADT Stack: Member Function Definitions

// destructor

Stack::~Stack( ) { }

// returns true if Stack is empty, otherwise returns false

bool Stack::isEmpty( ) const

{

return top < 0;

}

12

ADT Stack: Member Function Definitions

// adds newItem to the top of a Stack. Returns true if

// add succeeds, otherwise returns false.

bool Stack::push( StackItemType newItem )

{

if( top >= MAX_STACK - 1 ) return false;

items[ ++top ] = newItem;

return true;

}

13

ADT Stack: Member Function Definitions

// removes the item at the top of a Stack. Returns true if

// removal succeeds, otherwise returns false.

bool Stack::pop( )

{

if( isEmpty( ) ) return false;

top--;

return true;

}

14

ADT Stack: Member Function Definitions

// removes the item at the top of a Stack and returns it in

// stackTop. Returns true if removal succeeds, otherwise

// returns false.

bool Stack::pop( StackItemType &stackTop )

{

if( isEmpty( ) ) return false;

stackTop = items[top];

top--;

return true;

}

15

ADT Stack: Member Function Definitions

// retrieves a copy of the item at the top of a Stack and returns

// it in stackTop. Returns true if retrieval succeeds

// otherwise returns false.

bool Stack::getTop( StackItemType &stackTop ) const

{

if( isEmpty( ) ) return false;

stackTop = items[top];

return true;

}

16

ADT Stack: Linked-List Implementationtypedef int StackItemType; // items in Stack are int’s

class Stack

{ public:

// declarations of public member functions - same as for array!

private:

struct StackNode // linked-list of items in Stack

{

StackItemType item;

StackNode *next;

};

StackNode *topPtr; // pointer to top of Stack

};

17

ADT Stack: Member Function Definitions

// default constructor, which creates a new Stack and // initializes topPtr to NULL

Stack::Stack( ) : topPtr( NULL ) { }

18

ADT Stack: Member Function Definitions

Stack::Stack( const Stack &oStack ) // copy constructor,{ // which copies oStack if( oStack.topPtr = = NULL ) // to a new Stack topPtr = NULL; else

{ // copy top of Stack

topPtr = new StackNode; // assert is true if

assert( topPtr != NULL ); // allocation succeeds

topPtr -> item = oStack.topPtr -> item;

// copy rest of Stack }}

19

ADT Stack: Member Function Definitions

// copy rest of Stack

StackNode *pnew = topPtr;

for( StackNode *porig = oStack.topPtr -> next;

porig != NULL; porig = porig -> next )

{ pnew -> next = new StackNode;

assert( pnew -> next != NULL ); // assert is true if

pnew = pnew -> next; // allocation succeeds

pnew -> item = porig -> item;

}

pnew -> next = NULL;

20

ADT Stack: Member Function Definitions

// destructor pops Stack until empty

Stack::~Stack( ){ while( pop( ) );}

// returns true if Stack is empty, otherwise returns false

bool Stack::isEmpty( ) const{ return topPtr = = NULL;}

21

ADT Stack: Member Function Definitions

// adds newItem to the top of a Stack. Returns true if

// add succeeds, otherwise returns false.

bool Stack::push( StackItemType newItem ){

StackNode *pnode = new StackNode;

if( pnode = = NULL ) return false;

pnode -> item = newItem;

pnode -> next = topPtr;

topPtr = pnode;

return true;}

22

ADT Stack: Member Function Definitions

// removes the item at the top of a Stack. Returns true if// removal succeeds, otherwise returns false.

bool Stack::pop( )

{

if( isEmpty( ) ) return false;

StackNode *pnode = topPtr;

topPtr = topPtr -> next;

pnode -> next = NULL;

delete pnode; // removed node is deallocated

return true;

}

23

ADT Stack: Member Function Definitions

// removes the item at the top of a Stack and returns it in// stackTop. Returns true if removal succeeds, otherwise // returns false.

bool Stack::pop( StackItemType &stackTop )

{ if( isEmpty( ) ) return false;

stackTop = topPtr -> item;

StackNode *pnode = topPtr;

topPtr = topPtr -> next;

pnode -> next = NULL;

delete pnode; // removed node is deallocated

return true;

}

24

ADT Stack: Member Function Definitions

// retrieves the item at the top of a Stack and returns it in// stackTop. Returns true if retrieval succeeds, otherwise // returns false.

bool Stack::getTop( StackItemType &stackTop ) const

{

if( isEmpty( ) ) return false;

stackTop = topPtr -> item;

return true;

}

25

Application: Recognizing Balanced Parentheses

Problem: Determine whether the parentheses in a given string are balanced.

Examples:

Balanced Not Balanced

( a b ) ( c d ) ( a b

( ( a b c d ) ) ( a b ) )

( ( a b ) ( c d ) ) ) a b (

26

Recognizing Balanced Parentheses

Strategy:

• Scan a string, pushing each “(” onto a stack.

• When a “)” is found, pop one “(” from the stack.

• The parentheses are balanced if

– “(” can be popped from the stack for every “)” found, and

– the stack is empty when the entire string has been processed.

27

Recognizing Balanced Parentheses

typedef char StackItemType; // returns true if str[ ] has

bool isBalanced( const char str[ ] ) // balanced parentheses,

{ // else returns false

Stack parendStack; bool match = true;

for( char *pstr = str; match && *pstr != ‘\0’; pstr++ )

{ if( *pstr = = ‘(’ )

parendStack.push( ‘(’ );

else if( *pstr = = ‘)’ )

{ match = parendStack.pop( ); } } return match && parendStack.isEmpty( );}

28

Application: Recognizing Strings in a Language

Problem: Define a recognizer for the following language:

{ w$wR : w is a string of characters that does not contain ‘$’,

wR is the reverse of w }

Examples:

In Language Not In Language

A$A A$B

AB$BA A$B$B$A

ABC$CBA A$BA

29

Recognizing Strings in a Language

Strategy:

• Scan a string, pushing each character onto a stack until ‘$’ is found.

• When ‘$’ is found, skip it. For each character remaining in the string, pop a character from the stack and compare.

• The string is in { w$wR } if

– each character remaining in the string matches, in order, a character popped from the stack, and

– the stack is empty when the entire string has been processed.

30

Recognizing Strings in a Language

typedef char StackItemType;

bool inLanguage( const char str[ ] )

{

Stack charStack;

// push chars from str[ ] onto charStack until ‘$’ is found

// skip over ‘$’

// match remaining chars in str[ ] with those on charStack

return charStack.isEmpty( );

}

31

Recognizing Strings in a Language

// push chars from str[ ] onto charStack until ‘$’ is found char *pstr;

for( pstr = str; *pstr != ‘$’; pstr++ )

{ if( *pstr = = ‘\0’ ) return false;

charStack.push( *pstr ); }// skip over ‘$’

pstr++;

// match remaining chars in str[ ] with those on charStack

for( char stackTop; *pstr != ‘\0’; pstr++ )

{ if( !charStack.pop( stackTop ) ) return false;

if( stackTop != *pstr ) return false; }

32

Application: EvaluatingPostfix Expressions

Problem: Compute the value of a given postfix expression.

Examples:

Infix Postfix Value

2 * ( 3 + 4 ) 2 3 4 + * 14

2 * 3 + 4 2 3 * 4 + 10

( 2 + 3 ) * 4 2 3 + 4 * 20

33

Evaluating Postfix Expressions

Strategy:

• Scan a string, pushing each number onto a stack.

• When an operator is found

– pop two numbers from the stack,

– apply the operator, and

– push the result back onto the stack.

• When the entire string has been processed, the result will be the only thing left on the stack.

34

Evaluating Postfix Expressions

2 push( 2 ) 23 push( 3 ) 3 2+ pop( arg2 ) 2

pop( arg1 ) emptyresult = arg1 + arg2push( result ) 5

4 push( 4 ) 4 5* pop( arg2 ) 5

pop( arg1 ) emptyresult = arg1 * arg2push( result ) 20

Scan Process Stack (top is leftmost)

Evaluating 2 3 + 4 * :

35

Evaluating Postfix Expressions

In the C++ program segment that follows, we make the following simplifying assumptions:

• All input is syntactically correct (no error checking)

• The only operators are +, –, *, /

• No unary operators

• Input operands are non-negative, single-digit integers

• However, the value of the processed expression is an integer that may be either positive or negative and contain multiple digits.

36

Evaluating Postfix Expressions

We also assume that the following functions are available:

• bool isDigit( char chr ) returns true if character chr is a member of the set

{ ‘0’, ‘1, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ },

otherwise returns false.

• int Value( char digit ) returns an integer representing the value of the character digit.

• int Eval( int arg1, char op, int arg2 ) returns an integer representing the value of arg1 op arg2.

37

Evaluating Postfix Expressions// value of postfix expression in str[ ] is placed on top of intStack

typedef int StackItemType;

Stack intStack; char *pstr; int arg1, arg2;

for( pstr = str; *pstr != ‘\0’; pstr++ )

{

if( isDigit( *pstr ) ) intStack.push( Value( *pstr ) );

else // *pstr is an operator

{ intStack.pop( arg2 );

intStack.pop( arg1 );

intStack.push( Eval( arg1, *pstr, arg2 ) );

}

}

38

Converting Infix to Postfix: Strategy

Strategy (5 rules):

1) Initialize char postfixExpr[ ] to the empty string. When an operand is found in the input, append it to the end of postfixExpr[ ].

2) When a ‘(’ is found in the input, push it onto a Stack.

3) When a ‘)’ is found in the input, pop operators off the Stack and append them, in order, to the end of postfixExpr[ ], until a matching ‘(’ is popped from the Stack.

39

Converting Infix to Postfix: Strategy

Strategy (cont’d.):

4) When an operator is found in the input:

a) If the Stack is empty, push the operator onto the Stack.

b) Otherwise, pop operators of greater or equal precedence from the Stack and append them to postfixExpr[ ], stopping when:

• the Stack is empty, or

• a ‘(’ is found on the Stack, or

• an operator of lower precedence is found on the Stack.

Then, push the operator onto the Stack.

40

Converting Infix to Postfix: Strategy

Strategy (cont’d.):

5) When the end of the input is reached, pop all operators from the Stack and append them, in order, to the end of postfixExpr[ ].

Example:

Use the preceding algorithm to convert the following to postfix:

A – (B + C * D) / E

41

Infix to Postfix: A – (B + C * D) / E

Step Input Stack(top at left)

postfixExpr[ ] Rule #

1. A A 1 2. – – A 4 a 3. ( ( – A 2 4. B ( – A B 1 5. + + ( – A B 4 b 6. C + ( – A B C 1 7. * * + ( – A B C 4 b 8. D * + ( – A B C D 1 9. ) – A B C D * + 3

42

Infix to Postfix: A – (B + C * D) / E

Step Input Stack(top at left)

postfixExpr[ ] Rule #

9. ) – A B C D * + 310. / / – A B C D * + 4 b11. E / – A B C D * + E 112. A B C D * + E / – 5

43

Infix to Postfix: Observations

• Operands stay in the same order:

A + B becomes A B + not B A +

• An operator will move only to the right relative to its operands:

A + B becomes A B + not + A B

• Parentheses are removed

(A + B) * C becomes A B + C *

See Chapter 6 of Walls & Mirrors for detailed pseudocode corresponding to this algorithm

44

Search for a Path Between Two Cities

Problem: Given the origin and destination of each flight made by an airline, determine whether a sequence of these flights will allow one to travel from a desired origin to a desired destination.

Example:

Q X R P

Y

W S T

Z

45

Search for a Path Between Two Cities

3 Files Are Provided As Input:

• Cityfile – names of the cities served by the airline.

• Flightfile – pairs of city names, representing the origin and destination of each airline flight.

• Requestfile – one pair of city names, representing a request to fly from some origin to some destination.

Output Either:

• “Desired path exists” or

• “Desired path does not exist”

46

Search for a Path: Strategy

Strategy: Use backtracking to perform an exhaustive search of all (non-looping) sequences of flights that originate from the desired origin.

We explore the basic idea behind this strategy on the next slide. Let O denote the desired origin and D denote the desired destination.

47

Search for a Path: Basic Idea1) Select a flight (O C1) originating from O.

2) If C1 = D, we are done: there is a direct flight O D. Otherwise, select a flight (C1 C2).

3) If C2 = D, we are done: there is a path O C1 D. Otherwise, select a flight (C2 C3).

4) Continue in this manner until a city Ck is reached with no departing flights. Backtrack to the city Ck-1 from which we departed to arrive at Ck.

5) Now, select a flight (Ck-1 Cm) originating from Ck-1 that is different from (Ck-1 Ck). If Cm = D, we are done: there is a path O C1 C2 . . . Ck-1 D. Otherwise, select a flight (Cm Cm+1), and continue as above.

48

Search for a Path: Loop Avoidance

How do we avoid looping (or cycling) through the same cities? In the given example,

how do we avoid a path such as

W S T W S T . . . ?

Q X R P

Y

W S T

Z

49

Search for a Path: Loop Avoidance

Mark a city when it is visited. When choosing the next city to visit, restrict consideration to unmarked cities.

W S T backtrack to S backtrack to W Y

Q X R P

Y

W S T

Z

50

Search for a Path: BacktrackingHow do we implement an exhaustive search with backtracking?

Consider two approaches:

1) Use a stack to hold the path under consideration from the desired origin, O, to the most recently visited city.

After step (5) of our “Basic Idea” the stack would contain

O C1 C2 . . . Cm Cm+1

2) Alternatively, implement a recursive solution, where the nested recursive calls to a search function store the path implicitly.

Let’s focus first on the stack-based solution.

51

Search for a Path: Example

W

QX

R P

Y

S T

ZContents of stack in a search

for a path from P to Z (top is at left) P R P X R P R P P W P S W P T S W P S W P W P Y W P Z Y W P

52

Search for a Path: Stack-Based Solutionbool findPath( int origCity, int destCity )

{ pathStack.push( origCity ); visited[ origCity ] = true;

while( !pathStack.isEmpty( ) )

{ pathStack.getTop( curCity );

if( curCity = = destCity ) return true;

if( /* no flights exist from curCity to any unvisited city */ )

pathStack.pop( );

else

{ /* set nextCity to any unvisited neighbor of curCity */

pathStack.push( nextCity ); visited[ nextCity ] = true; }

}

return false;

}

53

Search for a Path: Storing the FlightsHow do we store the flights made by the airline?

Use an Adjacency List: an array of linked lists, where each element of the array corresponds to a city from which a flight could originate. The value of each array element is a pointer to a linked list of destinations that are accessible by a direct flight from the originating city.

TSRQP ZYXW

T W S RR X X

ZYW

NULL NULL

NULLNULL

NULL

NULL

NULL

NULL

NULL

Array:

L Li in sk te sd

54

Search for a Path: Recursive Solution

bool findPath( int origCity, int destCity )

{

visited[ origCity ] = true;

if( origCity = = destCity ) return true;

while( /* there is an unvisited neighbor of origCity */ )

{

/* set nextCity to any unvisited neighbor of curCity */

if( findpath( nextCity, destCity ) ) return true;

}

return false;

}

55

Search for a Path: Observations

• A recursive solution is sometimes conceptually simpler and can be expressed more concisely than a non-recursive, stack-based solution.

• Any recursive solution can be implemented non-recursively using a stack. In fact, this is how recursion is implemented by the compiler.

56

Search for a Shortest Path• Our strategy of using backtracking to perform an

exhaustive search is guaranteed to find a path from a given origin to a given destination if one exists.

• This strategy is not guaranteed to find the shortest path if the search is stopped when the first successful path is found.

• If the search is allowed to continue until all paths from a given origin have been considered, then one can pick the shortest path, if desired.

• When, due to an extremely large number of candidate paths, an exhaustive search is not possible, heuristics are sometimes used to make a “good guess” at the shortest path without searching through all the possibilities.

top related