Download presentation
Presentation is loading. Please wait.
1
Chapter (3) - Lists, Stacks, and Queues
Objectives: Introduce the concept of Abstract Data Types (ADTs), and show how they are supported by C++. How to efficiently perform operations on lists. Introduce the stack ADT and its use in implementing recursion. Introduce the queue ADT and its use in operating systems and algorithm design. Questions: What is ADT? Is ADT “how” to implement the set of operations or just the set itself? What are the examples of ADT?
2
ADT is a set of operations
ADT is a set of operations. ADTs are mathematical abstractions NOT how to implement the set of operations. Objects such as lists, sets, and graphs, along with their operations, can be viewed as ADT. Note that integers, reals, and Booleans are data types and have operations associated with them. ADTs might have some operations associated with them. Examples of these operations are: union, intersection, size, and complement. Basic idea is to write the implementation of these operations, once in the program and then any other program that needs to perform an operation on the ADT can do so by calling the appropriate function.
3
There is no rule which says what operations must be supported for each ADT. THAT IS A DESIGN DECISION. The List ADT: Contiguous Lists Linked Lists A list is a dynamic data structure, length of the list changes with use a1, a2, a3, …,an represents the general definition of a list of size n. The special list of size 0 represent a null list. A list is an ordered collection of elements position of element ai in a list is i. An array, by comparison, is a static data structure ordered by subscript Array size is constant and defined at compile time (partial exception: dynamically created arrays)
4
Set of Operations for Lists
Print_List Print the contents of a list. Make_Empty Initialize the list. Find Return the position of the first occurrence of requested element. Insert Insert an element at a position in the list. Remove Remove an element from a position in the list. Find_Kth Return the element at position k. Next Take a position as argument and returns the position of the successor. Previous Take a position as argument and returns the position of the predecessor.
5
Simple Array Implementation
A List can be implemented using an array. Although the array can be created dynamically, we still need an estimate of the size. This usually results in a high overestimate. With an array implementation, printList and find can be carried in linear time which is as good as can be expected. The findKth takes constant time. However, insertion and deletion are expensive. In average about ½ of the list may need to be moved around.
6
Examples: List: 34, 12, 52, 16, 12 Find(52) : might return 2 Insert(x,3) : might make the list 34, 12, 52, x, 16, 12 Remove(52) : might turn the list into 34, 12, x, 16,12 Interpretation of what a function will do is entirely up to the programmer.
7
{ Simple Array Implementation of Lists Contiguous Implementation
Using struct typedef char entry typedef struct list { int Count; ListEntry Entry[MAXLIST]; } List; { Count List ListEntry
8
{ Simple Array Implementation of Lists Contiguous Implementation
Using a simple class (Not the best equivalent) Class List { public: int Count; ListEntry Entry[MAXLIST]; }; { Count List ListEntry
9
Advantages Disadvantages Allows easy random access to elements
Efficient use of memory for small list elements Disadvantages Inefficient use of memory for large list elements Insert and Delete operations are expensive: O(n) complexity
10
Insert Process: 2. Insert New element 1. Move all subsequent elements one cell forward (one at a time) 3. Increment Number of elements Similarly for delete … What can we do ? Ensure the list is not stored contiguously.
11
Dynamic Lists - Linked List
The link list consists of a series of structures, which are not necessarily adjacent in memory. Each structure contains the element and a pointer to a record containing its successor. We call this the Next pointer. The last cell’s Next pointer contains 0, and it is called null pointer. The NULL symbol is defined in <iostream>. Note the definition of a pointer …. If a P is a pointer to a record, then the value stored in P is interpreted as the location, in the main memory, where a structure can be found.
12
Example: List containing five elements in memory locations: 1000, 800, 712, 992, and 692.
13
Questions? How do we insert at the front of the list? How do we delete from the front of the list? (It changes the start of the list) Do we need to keep track of the pointer before and after the one we are removing?
14
The header or dummy node
The header will always be at position 0. L Empty list with header The list ADT in our textbook (commonly) is implemented as three separate classes: one class is the list itself (List), another represents the node (ListNode), and the third represents the position (ListItr).
15
Type declaration for linked list node
template <class Object> class List; // Incomplete declaration. class ListItr; // Incomplete declaration. class ListNode { ListNode( const Object & theElement = Object( ), ListNode * n = NULL ) : element( theElement ), next( n ) { } Object element; //stored element ListNode *next; //link to the next node friend class List<Object>; // friend class friend class ListItr<Object>; // friend class };
16
Iterator class for linked list
template <class Object> class ListItr { public: ListItr( ) : current( NULL ) { } bool isPastEnd( ) const { return current == NULL; } void advance( ) { if( !isPastEnd( ) ) current = current->next; } const Object & retrieve( ) const //returns the element stored in the current { if( isPastEnd( ) ) throw BadIterator( ); return current->element; } private: ListNode<Object> *current; // Current position ListItr( ListNode<Object> *theNode ) // constructor .. private : current( theNode ) { } friend class List<Object>; // Grant access to constructor }; Iterator class for linked list
17
List class Interface template <class Object> class List {
public: List( ); List( const List & rhs ); ~List( ); bool isEmpty( ) const; void makeEmpty( ); ListItr<Object> zeroth( ) const; //returns iterator corresponding to header ListItr<Object> first( ) const; // returns iterator corresponding to first void insert( const Object & x, const ListItr<Object> & p ); ListItr<Object> find( const Object & x ) const; ListItr<Object> findPrevious( const Object & x ) const; void remove( const Object & x ); const List & operator=( const List & rhs ); private: ListNode<Object> *header; //pointer to the heather node }; List class Interface
18
template <class Object>
Function to print a list template <class Object> void printList( const List<Object> & theList ) { if( theList.isEmpty( ) ) cout << "Empty list" << endl; else { ListItr<Object> itr = theList.first( ); for( ; !itr.isPastEnd( ); itr.advance( ) ) cout << itr.retrieve( ) << " "; } cout << endl;
19
/** * Construct the list */ template <class Object> List<Object>::List( ) { header = new ListNode<Object>; } * Copy constructor List<Object>::List( const List<Object> & rhs ) *this = rhs;
20
/** * Destructor */ template <class Object> List<Object>::~List( ) { makeEmpty( ); delete header; } * Test if the list is logically empty. * return true if empty, false otherwise. bool List<Object>::isEmpty( ) const return header->next == NULL;
21
/** * Make the list logically empty. */ template <class Object> void List<Object>::makeEmpty( ) { while( !isEmpty( ) ) remove( first( ).retrieve( ) ); } * Return an iterator representing the header node. ListItr<Object> List<Object>::zeroth( ) const return ListItr<Object>( header );
22
/** * Return an iterator representing the first node in the list. * This operation is valid for empty lists. */ template <class Object> ListItr<Object> List<Object>::first( ) const { return ListItr<Object>( header->next ); } * Insert item x after p. void List<Object>::insert( const Object & x, const ListItr<Object> & p ) if( p.current != NULL ) p.current->next = new ListNode<Object>( x, p.current->next );
23
/** * Return iterator corresponding to the first node containing an item x. * Iterator isPastEnd if item is not found. */ template <class Object> ListItr<Object> List<Object>::find( const Object & x ) const { /* 1*/ ListNode<Object> *itr = header->next; /* 2*/ while( itr != NULL && itr->element != x ) /* 3*/ itr = itr->next; /* 4*/ return ListItr<Object>( itr ); }
24
/** * Return iterator prior to the first node containing an item x. */ template <class Object> ListItr<Object> List<Object>::findPrevious( const Object & x ) const { /* 1*/ ListNode<Object> *itr = header; /* 2*/ while( itr->next != NULL && itr->next->element != x ) /* 3*/ itr = itr->next; /* 4*/ return ListItr<Object>( itr ); }
25
currentNext current …….. currentNextNext OldNode /**
* Remove the first occurrence of an item x. */ template <class Object> void List<Object>::remove( const Object & x ) { ListItr<Object> p = findPrevious( x ); if( p.current->next != NULL ) ListNode<Object> *oldNode = p.current->next; p.current->next = p.current->next->next; // Bypass deleted node delete oldNode; } currentNext current …….. currentNextNext OldNode
26
Common Errors Misdirected pointer (failure to initialize the variables), ex: Current_Pos is NULL. Make sure that the pointer is not null. Improper use of new to get a new cell. Note: declaring a pointer to a structure does not create the structure, it only gives enough space to hold the address where the structure might be. Ex: P = new Node; has the system create a new object of type Node in accordance with the constructor for Node, Then P is set to point to the new structure. Ex: If you want to use a pointer variable to access existing nodes, there is no need of new node.
27
Improper allocation of new locations
Improper allocation of new locations. If you never delete from a linked list, the number of calls to new should be equal to size of the list + 1 for header. Any less, results in a non working program Any more, results in wasting space. Loosing track of the memory locations that are supposed to be deleted. Ex: When you delete a cell you usually move the pointer from one cell to another. In such cases it is important to keep the address of the disposed memory location in as a temporary variable so later you go back remove it.
28
List copy routine: operator = and copy constructor
template <class Object> const List<Object> & List<Object>::operator=(const List<Object> & rhs) { if( this != &rhs ) makeEmpty( ); ListItr<Object> ritr = rhs.first(); ListItr<Object> itr = zeroth(); for( ; !ritr.isPastEnd( ); ritr.advance( ), itr.advance() ) insert(ritr.retrieve( ), itr); } return *this; // Copy constructor List<Object>::List(const List<Object> &rhs) header = new ListNode<Object>; *this = rhs;
29
Doubly Linked Lists The major downside of the linked list is that all order to the list is in one direction. The only way to get to the node previous to the current node is to start at the head and traverse until you arrive at node - 1, this is an O(n) complexity operation. To fix the problem, add an extra field containing a pointer to the previous cell to the data structure. This costs an extra link and requires twice as much work to insert or delete a cell. On the other hand, it makes your life easier when you are deleting or inserting, because the previous and next are both at hand.
30
Struct Implementation of doubly linked list
typedef struct listnode { ListEntry Entry; struct listnode *Next; struct listnode *Previous; } ListNode typedef struct list int Count; ListNode *Current; Position CurentPos; } List
31
Deleting a node: Inserting a node: A2
32
Circularly Linked lists
A popular convention is to have the last cell keep a pointer back to the first. This can be done with or without header. It also can be done using doubly linked list: the first cell’s previous pointer points to the last cell, and the last’s next pointer points to the first cell. Inserting and deleting nodes can be done in a similar fashion as in the linked lists.
33
Example (1) - The Polynomial ADT
Where, active exponents are non-negative. If most coefficients are non-zero we can use a simple array to store the coefficients. Then we could write a routine to perform the addition, subtraction, multiplication, differentiation, and other operations. Example: Multiply the following polynomials: P1(x) = 10x x and P2(x)= 3x x x + 5 Simple Example: P1 = Ax4 + Bx + 3 and P2 = Cx4 + Dx2 + 4 P1 + P2 = (A + C) x4 + (0 + 0)x3 + (D+0)x2 + (B+0) x + (3+4) x0 Same for Multiplication, Do you see any problem ?
34
3.20: Class specification for array implementation of the polynomial ADT
class Polynomial { public: Polynomial( ); void zeroPolynomial( ); Polynomial operator+( const Polynomial & rhs ) const; Polynomial operator*( const Polynomial & rhs ) const; void print( ostream & out ) const; private: static const int MAX_DEGREE = 100; //or enum { MAX_DEGREE = 100 }; vector<int> coeffArray; int highPower; };
35
Polynomial Class functions
Polynomial::Polynomial( ) : coeffArray( MAX_DEGREE + 1 ) { zeroPolynomial( ); } void Polynomial::zeroPolynomial( ) for( int i = 0; i <= MAX_DEGREE; i++ ) coeffArray[ i ] = 0; highPower = 0; Polynomial Polynomial::operator+( const Polynomial & rhs ) const Polynomial sum; sum.highPower = max( highPower, rhs.highPower ); for( int i = sum.highPower; i >= 0; i-- ) sum.coeffArray[ i ] = coeffArray[ i ] + rhs.coeffArray[ i ]; return sum;
36
Polynomial Class functions
Polynomial Polynomial::operator*( const Polynomial & rhs ) const { Polynomial product; product.highPower = highPower + rhs.highPower; if( product.highPower > MAX_DEGREE ) cerr << "operator* exceeded MAX_DEGREE" << endl; for( int i = 0; i <= highPower; i++ ) for( int j = 0; j <= rhs.highPower; j++ ) product.coeffArray[ i + j ] += coeffArray[ i ] * rhs.coeffArray[ j ]; return product; } void Polynomial::print( ostream & out ) const for( int i = highPower; i > 0; i-- ) out << coeffArray[ i ] << "x^" << i << " + "; out << coeffArray[ 0 ] << endl;
37
Polynomial Class functions
ostream & operator<<( ostream & out, const Polynomial & rhs ) { rhs.print( out ); return out; } int max( int a, int b ) return a > b ? a : b;
38
Polynomial Class – Main function
int main( ) { Polynomial p; Polynomial q; p.highPower = 1; //Sets the maimum poser to 1 p.coeffArray[ 0 ] = 1; //sets the coefficient of power 0 to 1 p.coeffArray[ 1 ] = 1; //sets the coefficient of power 1 to 1 q = p + p; // Add the two polynomial p store as q p = q * q; // Multiply q by q store in p q = p + p; // Sum two polynomials p store as q cout << q << endl; //Use overloaded output to display return 0; }
39
Polynomial ADT - Array Implementation
P1: anxn + an-1xn-1 + an-2 xn-2 + an-3 xn-3 + … + a2x2 + a1 x+ a0 P2: bnxn + bn-1xn-1 + bn-2 xn-2 + bn-3 xn-3 + … + b2x2 + b1 x+ b0 P1 P2 Sum Multiplication an bn (an + bn)xn (an * bn)x2n an-1 bn-1 (an-1 + bn-1) xn-1 (an-1 * bn-1) x2(n-1) … … a2 b2 (a2 + b2)x2 (a2 * b2)x4 a1 b1 (a1+ b1)x (a1+ b1)x2 a0 b0 a0 + b0 a0 + b0 Many of these terms maybe zero. But regardless of the size of each individual polynomial, you have to allocate two arrays of size n the largest power of the two polynomials.
40
As the above table shows, you started with n terms for P1 and n terms for P2 (note once again that only one maybe of size n) and you end up getting : An array of n elements when you did the addition, and An array of 2n elements when you did the multiplication.
41
An Alternative Solution to Polynomial ADT using Singly Linked List
The nodes are stored in decreasing order of exponents. The following example is shown below. P1(x) = 10x x and P2(x)= 3x x x + 5 One can immediately notice a problem in using the above method. This problem appears when you multiply two polynomials. As shown previously, when you multiply two terms of the same order, n, the result will become of order 2n.
42
The Radix Sort This sort is also known as card sort. If we have n integers in the range 1 to m (or 0 to m-1), We can use a method know as the bucket sort to sort them. In order to do this, we keep an array called Count of size m, which is initialized to zero. Process is: If you read ai then increment (by one) Count[ai]. After all the input is read, scan the Count, and print the sorted list. This algorithm takes and O(m+n) algorithm.
43
Example: 8, 9, 3, 4, 2, 6, 4, 8, 2, 5, create C[9-2], thus, C[7]
Example: 8, 9, 3, 4, 2, 6, 4, 8, 2, 5, create C[9-2], thus, C[7]. Index for numbers are, C[ai -2] Count[ ] index: 6, 7, 1, 2, 0, 4, 2, 6, 0, 3 Read: Count: C[6]++ C[7]++ C[1]++ C[2]++ C[0]++ C[4]++ C[2]++ Read: Count: C[6]++ C[0]++ C[3}++ We can sort now, C[0] has kept two for counts, thus the list begins with 2 2 Then C[1]=1: 3, C[2]= 2, thus, 4 4, C[3]=1: 5, C[4] = 1: 6, C[5]=0, C[6] = 2: 8 8, C[7] = 1: 9 The final results is:
44
Radix Sort - Example Suppose we have 10 numbers ranging between 0 to 999 that we would like to sort. If we use the method used in the previous example, then we will have too many buckets. What can we do to improve the process ? We make several passes to bucket sort in a different manner. We bucket sort with the least significant “digit”. Of course, more than one number could fall into the same bucket, and, unlike the bucket sort these numbers may be different, so we keep them in a list. Example: 64, 8, 216, 512, 27, 729, 0, 1, 343, 125
45
First Pass: Looking at Ones
46
Second Pass: Looking at Tens
Third Pass: Looking at hundreds O(p(n+b)) : p is number of passes, n is the number of elements to sort, and b is the number of buckets. What could go wrong ?
47
Stack (LIFO) ADT The Stack data structure all insertions and deletions of entries occur at one end called the top of the stack Operations : Push and Pop Implementing the stack as an array as a linked list Stack, the example We need an ADT that will accept a line of input and will then output in reverse Build a stack Push each character onto the stack as it is read. Pop the characters off in reverse order Original: Keep up the good work Backward: krow doog eht pu peeK
48
Stack class interface – array implementation
template <class Object> class Stack { public: explicit Stack( int capacity = 10 ); bool isEmpty( ) const; bool isFull( ) const; const Object & top( ) const; void makeEmpty( ); void pop( ); void push( const Object & x ); Object topAndPop( ); private: vector<Object> theArray; int topOfStack; };
49
Simple Example: Stack, the example We need an ADT that accepts a line of input and then outputs the line in reverse Build a stack Push each character onto the stack as it is read. Pop the characters off from the stack
50
// ReverseRead: read one line of input and write it backward.
g e h t p u K // ReverseRead: read one line of input and write it backward. // Pre: The user supplies one line of input // Post: The line has been printed backward using a stack void ReverseRead(void) { StackEntry item; Stack stack; // initialize the stack to be empty CreateStack(&stack); while(!StackFull(&stack) && (item = getchar()) != ‘\n’) Push(item, &stack);
51
//Now we have the stack loaded, let’s empty it
pop k r o w d g e h t p u K //Now we have the stack loaded, let’s empty it while(!StackEmpty(&stack)) { Pop(&item, &stack); putchar(item); }// End of while not end of stack putchar(‘\n’); }// End of ReverseRead
52
Linked List Implementation of Stacks
Push : Insert at the front of the list Pop : Delete the element at the front of the list. Top : Merely examines the element at the front of the list, returning its value. The delete and new are expensive. One way to avoid some of this expense is to use a second stack which is initially empty. Thus, when we need to delete a cell from the first stack, it is merely placed on the second stack. Then, when new cells are needed for the first stack, the second stack is checked first.
53
An Array Implementation of Stacks
This method avoids pointer and is a more popular solution. The only problem is that we need to declare an array size ahead of time. Generally, this is not a problem, because in typical applications, even if there are quite a few stack operations, the actual number of elements in the stack at any time never gets too large. We can declare the array to be large enough without wasting too much space. Using array implementation, with each stack we have the top of the stack (tos), which is -1 for an empty stack (initialized to -1). As we push an element, x, onto stack, we increment tos and then set Stack[tos] = x. To pop an element, x, in one routine, we set the return value to Stack[tos] and then decrement tos.
54
The push and pop operations are performed in a very fast constant time.
On some machines, Pushes and Pops (of integers) can be written in one machine instruction, operating on a register with auto-increment and auto-decrement addressing. Most modern machines have stack operations as part of the instruction set reinforces the idea that the stack is probably the most fundamental data structure in computer science, after array. Error testing is a major drawback to this method.
55
The linked list implementation carefully checked for errors
The linked list implementation carefully checked for errors. In this method a Pop on empty stack or a push on a full stack will overflow the array bounds and lead to undesirable results. A check for these conditions on the array implementation would likely take as much time as the actual stack manipulation. One may omit the error checks and create a large enough stack to avoid overflow and make sure that Pop never attempts to Pop an empty stack. This results in large codes. For an array implementation, a stack class will be defined. In such cases, instead of picking a maximum stack size, we allow the user to specify the size.
56
Stack class interface – linked list implementation
private: struct ListNode { Object element; ListNode *next; ListNode( const Object & theElement, ListNode * n = NULL ) : element( theElement ), next( n ) { } }; ListNode *topOfStack; template <class Object> class Stack { public: Stack( ); Stack( const Stack & rhs ); ~Stack( ); bool isEmpty( ) const; bool isFull( ) const; const Object & top( ) const; void makeEmpty( ); void pop( ); void push( const Object & x ); Object topAndPop( ); const Stack & operator=( const Stack & rhs );
57
/*** Construct the stack.
*/ template <class Object> Stack<Object>::Stack( ) { topOfStack = NULL; } /*** Copy constructor. Stack<Object>::Stack( const Stack<Object> & rhs ) *this = rhs;
58
/*** Destructor. */ template <class Object> Stack<Object>::~Stack( ) { makeEmpty( ); } /** * Test if the stack is logically full. * Return false always, in this implementation. bool Stack<Object>::isFull( ) const return false;
59
/*** Test if the stack is logically empty.
* Return true if empty, false otherwise. */ template <class Object> bool Stack<Object>::isEmpty( ) const { return topOfStack == NULL; } /*** Make the stack logically empty. void Stack<Object>::makeEmpty( ) while( !isEmpty( ) ) pop( );
60
/. Get the most recently inserted item in the stack
/*** Get the most recently inserted item in the stack. Return the most recently inserted item in the stack or throw an exception if empty. */ template <class Object> const Object & Stack<Object>::top( ) const { if( isEmpty( ) ) throw Underflow( ); return topOfStack->element; } /*** Remove the most recently inserted item from the stack. * Exception Underflow if the stack is empty.*/ void Stack<Object>::pop( ) ListNode *oldTop = topOfStack; topOfStack = topOfStack->next; delete oldTop;
61
/** Return and remove the most recently inserted item
* from the stack. */ template <class Object> Object Stack<Object>::topAndPop( ) { Object topItem = top( ); pop( ); return topItem; } /*** Insert x into the stack */ void Stack<Object>::push( const Object & x ) topOfStack = new ListNode( x, topOfStack );
62
/*** Deep copy */ template <class Object> const Stack<Object> & Stack<Object>:: operator=( const Stack<Object> & rhs ) { if( this != &rhs ) makeEmpty( ); if( rhs.isEmpty( ) ) return *this; ListNode *rptr = rhs.topOfStack; ListNode *ptr = new ListNode( rptr->element ); topOfStack = ptr; for( rptr = rptr->next; rptr != NULL; rptr = rptr->next ) ptr = ptr->next = new ListNode( rptr->element ); }
63
Balancing Symbols A missing brace may result in hundred lines of error without identifying the real error. A useful tool is to check and make sure all braces, brackets, and parentheses must correspond to their left counterparts. Example: The sequence [()] is legal, but [(]) is wrong. It is to check this using stack, here is how: 1) make an empty stack, 2) read characters until end of file, 3) if the character is an open anything, push it onto the stack, 4) if it is a close anything, then: if the stack is empty report error otherwise pop the stack, and
64
if it doesn’t correspond, report error, otherwise proceed
5) check to see if the popped character correspond to the opening symbol, if it doesn’t correspond, report error, otherwise proceed 6) if at the end the stack is not empty report error. Example: if( x == 0 ){ x = 3*(y - z); { Reading: i f ( x == 0 ) { x = 3* ( y - z ) ; { Read ( Read ) Read { Read ) Read { ( { Stack is not empty: ERROR { { ( { Stack Push ( Pop ( Push { Push ( Pop( Push {
65
Postfix Expressions Suppose you want to compute the cost of a shopping trip. If the items purchased are: $4.99, 5.99, and 6.99, then the total cost + 6% tax may be computed as: * 1.06 Depending on the type of your calculator, there are two possible answers: (6.99*1.06) = OR ( ) * 1.06 =
66
To make the matter worse, if the first and the last items are taxable, then we may write:
4.99* *1.06 = ( ( (4.99*1.06) ) ) *1.06 = OR (4.99*1.06) (6.99*1.06) = The second method is a typical method of solving such problems. We could write that sequence as: * * + This notation is known as postfix or reverse Polish notation and it will evaluate as: a1 = 4.99*1.06, a1 = a , a2 = 6.99*1.06, and at last answer is = a1 + a2.
67
Implementation of Polish notation using stacks Here is the steps:
1) When a number is seen, push it onto stack 2) When an operator is seen, the operator is applied to the two numbers (symbols) poped from stack 3) Push the result back onto the stack. Example : * * is evaluated as follows: 1st 3 2 5 6 2nd + pop 3 2 3+2 5 6 3rd push 5 5 6 4th 8 5 6 5th * pop 8 5 8*5 5 6 6th push 40 40 5 6 7th + pop 40 5 40+5 6 8th push 45 45 6 9th 3 45 6 10th + pop 3 45 3+45 6 11th push 48 48 6 12th * pop 48 6 48*6 48 6 13th push 288 288
68
Infix to Postfix Conversion
We can use an stack to convert an expression in standard form (known as infix). Here is how (Note that we only allow a limited number of operations, +, *, (, ) ). 1) If you see an operand, put onto output immediately. 2) If you see an operator, put it in the stack 3) If you see a left parenthesis put it in the stack 4) If you see a right parenthesis pop the stack, write the symbols until you see a left parenthesis. 5) If you see any other symbol (‘+’, ‘*’, ‘(‘), then pop entries from the stack until you find an entry of lower priority. Exception: never remove ‘(‘ from the stack except when processing a ‘)’. Thus, ‘+’ has lowest priority and ‘(‘ highest. 6) If you read the end of input, pop the stack until it is empty, writing symbols onto the output.
69
Translate the last line, do you get the original one :
Example: Number in parentheses represent the order of push Suppose we want to convert the infix expression a + b * c + (d * e + f) * g Read a + b * c ( d e f ) g Stack + Stack * + Stack * ( + Stack + ( Stack + Stack * + Stack (1) (2) (5) (6) (3) (7) (1) (4) (4) (3) (1) (3) (3) (2) This is an operator, pop higher priority operator(s) out, output them, push + (3) This is an operator, pop higher priority operator(s) out, output them, push + Output a a b a b c a b c * + a b c * + d a b c * + d e a b c * + d e * a b c * + d e * f a b c * + d e * f + a b c * + d e * f + g a b c * + d e * f + g * + (4) (5) (6) Start popping the stack till u gets its pair, pop that too. (7) O(n) Translate the last line, do you get the original one :
70
The Queue ADT Like stacks queues are lists. With a queue insertion is done at one end, and deletion is performed at the front (the other end). LILO. The basic operations in a queue are Enqueue. This operator inserts an element at the end of the list (called rear), Dequeue deletes (and passes back) the element at the start of the list (known as the front).
71
As with stacks, any list implementation is legal for queues
As with stacks, any list implementation is legal for queues. Like stacks, both the linked list and array implementations give fast O(1) running times for every operation. Here we look into array implementation of queues. A queue in some intermediate state
72
Enqueue Operation: To enqueue X, we will: 1) increment Q_Size, 2) increment Q_Rear, 3) then set Q_Array[Q_Rear] = X. Dequeue Operation: To dequeue X, we will: 1) set the return value to Q_Array[Q_Front], 2) decrement Q_Size, 3) then increment Q_Front. Other strategies are also possible. Potential problem: after 10 Enqueues, the queue appears to be full, since Q_Rear is now 10. The next Emqueue would be in a nonexistent position. However, there might only be a few elements in the queue, because several elements may have been dequeued. Queues usually stay small.
73
Solution: Whenever the Q_Front or Q_Rear gets to the end of the array, it is wrapped around to the beginning. The following figures show the queue using circular array implementation.
74
Class interface for queue – array implementation
template <class Object> class Queue { public: explicit Queue( int capacity = 10 ); bool isEmpty( ) const; bool isFull( ) const; const Object & getFront( ) const; void makeEmpty( ); Object dequeue( ); void enqueue( const Object & x ); private: vector<Object> theArray; int currentSize; int front; int back; void increment( int & x ); }; Class interface for queue – array implementation
75
/*** Construct the queue.
*/ template <class Object> Queue<Object>::Queue( int capacity ) : theArray( capacity ) { makeEmpty( ); } /*** Test if the queue is logically empty. * Return true if empty, false otherwise. bool Queue<Object>::isEmpty( ) const return currentSize == 0;
76
/*** Test if the queue is logically full.
* Return true if full, false otherwise. */ template <class Object> bool Queue<Object>::isFull( ) const { return currentSize == theArray.size( ); } /*** Make the queue logically empty */ void Queue<Object>::makeEmpty( ) currentSize = 0; front = 0; back = -1;
77
/** * Get the least recently inserted item in the queue. * Return the least recently inserted item in the queue * or throw Underflow if empty. */ template <class Object> const Object & Queue<Object>::getFront( ) const { if( isEmpty( ) ) throw Underflow( ); return theArray[ front ]; }
78
/** * Return and remove the least recently inserted item from the queue. * Throw Underflow if empty. */ template <class Object> Object Queue<Object>::dequeue( ) { if( isEmpty( ) ) throw Underflow( ); currentSize--; Object frontItem = theArray[ front ]; increment( front ); return frontItem; }
79
/*** Insert x into the queue. Throw Overflow if queue is full */
template <class Object> void Queue<Object>::enqueue( const Object & x ) { if( isFull( ) ) throw Overflow( ); increment( back ); theArray[ back ] = x; currentSize++; } /** * Internal method to increment x with wraparound.*/ void Queue<Object>::increment( int & x ) if( ++x == theArray.size( ) ) x = 0;
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.