55
Stacks and Queues Stacks and Queues

Data Structures- Part8 stacks and queues

Embed Size (px)

DESCRIPTION

Data Structures CPCS-204

Citation preview

Page 1: Data Structures- Part8 stacks and queues

Stacks and QueuesStacks and Queues

Page 2: Data Structures- Part8 stacks and queues

What is a Stack?What is a Stack? Stack is a data structure in which data is Stack is a data structure in which data is

addedadded and and removedremoved at only one end called the at only one end called the toptop

Examples of stacks are:Examples of stacks are: Stack of booksStack of books Stack of trays in a cafeteriaStack of trays in a cafeteria

Page 3: Data Structures- Part8 stacks and queues

StackStack A Last In First Out (LIFO) data structureA Last In First Out (LIFO) data structure Primary operations: Primary operations: PushPush and and PopPop PushPush

Add an element to the top of the stackAdd an element to the top of the stack PopPop

Remove the element from the top of the stackRemove the element from the top of the stack An exampleAn example

Atop

empty stack: initial state

top

top

top

push (A) push (B)

A

B

pop

A

Page 4: Data Structures- Part8 stacks and queues

Building Stack Step-by-StepBuilding Stack Step-by-Step

2

8

1

7

2

7

2

1

7

2

1

7

2

8

1

7

2

8

1

7

2

top

toptop

toptop

top

Push(8) Push(2)

pop()

pop()pop()

Page 5: Data Structures- Part8 stacks and queues

Stack ErrorsStack Errors Stack OverflowStack Overflow

An attempt to add a new element in an already full An attempt to add a new element in an already full stack is an errorstack is an error

A common mistake often made in stack A common mistake often made in stack implementationimplementation

Stack UnderflowStack Underflow An attempt to remove an element from the empty An attempt to remove an element from the empty

stack is also an errorstack is also an error

Again, a common mistake often made in stack Again, a common mistake often made in stack implementationimplementation

Page 6: Data Structures- Part8 stacks and queues

Applications of StacksApplications of Stacks

Some direct applications:Some direct applications: Page-visited history in a Web browserPage-visited history in a Web browser Undo sequence in a text editorUndo sequence in a text editor Evaluating postfix expressions (e.g., xy+)Evaluating postfix expressions (e.g., xy+)

Some indirect applicationsSome indirect applications Auxiliary data structure for some algorithms (e.g., Auxiliary data structure for some algorithms (e.g.,

Depth First Search algorithm)Depth First Search algorithm) Component of other data structuresComponent of other data structures

Page 7: Data Structures- Part8 stacks and queues

The Stack Abstract Data TypeThe Stack Abstract Data Type Stacks are the simplest of all data structuresStacks are the simplest of all data structures Formally, a stack is an abstract data type (ADT) that Formally, a stack is an abstract data type (ADT) that

supports the following two methods:supports the following two methods: push(push(ee): Insert element ): Insert element ee to the top of the stack to the top of the stack pop(): Remove from the stack and return the top element on pop(): Remove from the stack and return the top element on

the stack; the stack; an error occurs if the stack is empty – what error?an error occurs if the stack is empty – what error?

Additionally, let us also define the following methods:Additionally, let us also define the following methods: size(): Return the number of elements in the stacksize(): Return the number of elements in the stack isEmpty(): Return a Boolean indicating if the stack is emptyisEmpty(): Return a Boolean indicating if the stack is empty top(): Return the top element in the stack, without removing top(): Return the top element in the stack, without removing

it it an error occurs if the stack is emptyan error occurs if the stack is empty

Page 8: Data Structures- Part8 stacks and queues

The Stack Abstract Data TypeThe Stack Abstract Data Type Example 5.3:Example 5.3: The following table shows a series of stack operations The following table shows a series of stack operations

and their effects on an initially empty stack S of integers.and their effects on an initially empty stack S of integers.

OperationOperation OutputOutput Stack ContentsStack Contents

push(5)push(5)push(3)push(3)pop()pop()push(7)push(7)pop()pop()top()top()pop()pop()pop()pop()isEmpty()isEmpty()push(9)push(9)push(7)push(7)push(3)push(3)push(5)push(5)size()size()pop()pop()push(8)push(8)pop()pop()pop()pop()

----33--77555 5 "error“"error“truetrue--- - ----4455--8833

(5)(5)(5, 3)(5, 3)(5)(5)(5, 7)(5, 7)(5)(5)(5)(5)()()()()()()(9) (9) (9, 7)(9, 7)(9, 7, 3)(9, 7, 3)(9, 7, 3, 5)(9, 7, 3, 5)(9, 7, 3, 5)(9, 7, 3, 5)(9, 7, 3)(9, 7, 3)(9, 7, 3, 8)(9, 7, 3, 8)(9, 7, 3)(9, 7, 3)(9, 7)(9, 7)

Page 9: Data Structures- Part8 stacks and queues

A Stack Interface in JavaA Stack Interface in Java

The stack data structure is included as a "built-The stack data structure is included as a "built-in" class in the java.util package of Java. in" class in the java.util package of Java.

Class java.util.Stack is a data structure that Class java.util.Stack is a data structure that stores generic Java objects and includes,among stores generic Java objects and includes,among others, the following methods:others, the following methods: push(), push(), pop()pop() peek() (equivalent to top()), peek() (equivalent to top()), size(), and empty() (equivalent to isEmpty()). size(), and empty() (equivalent to isEmpty()). Methods pop() and peek() throw exception Methods pop() and peek() throw exception

EmptyStackException if they are called on an empty EmptyStackException if they are called on an empty stack. stack.

Page 10: Data Structures- Part8 stacks and queues

The Stack Abstract Data TypeThe Stack Abstract Data Type Implementing an abstract data type in Java involves two Implementing an abstract data type in Java involves two

steps. The first step is the definition of a Java steps. The first step is the definition of a Java Application Application Programming InterfaceProgramming Interface (API), or simply (API), or simply interfaceinterface, which , which describes the names of the methods that the ADT supports describes the names of the methods that the ADT supports and how they are to be declared and used.and how they are to be declared and used.

In addition, we must define exceptions for any error In addition, we must define exceptions for any error conditions that can arise. For instance, the error condition conditions that can arise. For instance, the error condition that occurs when calling method pop() or top() on an empty that occurs when calling method pop() or top() on an empty stack is signaled by throwing an exception of type stack is signaled by throwing an exception of type EmptyStackException, EmptyStackException,

Page 11: Data Structures- Part8 stacks and queues

The Stack Abstract Data TypeThe Stack Abstract Data TypeCode Fragment 5.2: Code Fragment 5.2: Interface Stack documented with comments in Javadoc style (Section 1.9.3). Note also the use of the generic Interface Stack documented with comments in Javadoc style (Section 1.9.3). Note also the use of the generic

parameterized type, E, which implies that a stack can contain elements of any specified class.parameterized type, E, which implies that a stack can contain elements of any specified class.

Page 12: Data Structures- Part8 stacks and queues

A Simple Array-Based Stack ImplementationA Simple Array-Based Stack Implementation We can implement a stack by storing its elements in an We can implement a stack by storing its elements in an

array. array. Specifically, the stack in this implementation consists of Specifically, the stack in this implementation consists of

an an NN-element array -element array SS plus an integer variable plus an integer variable tt that gives the index of the top element in array that gives the index of the top element in array SS. .

Figure 5.2: Figure 5.2: Implementing a stack with an array Implementing a stack with an array SS. The top element in the stack is stored in the cell . The top element in the stack is stored in the cell SS[[tt].].

Recalling that arrays start at index 0 in Java, Recalling that arrays start at index 0 in Java, we initialize we initialize tt to −1, and we use this value for to −1, and we use this value for tt to identify an empty to identify an empty

stack. stack. Likewise, we can use Likewise, we can use tt to determine the number of elements to determine the number of elements

((tt + 1). + 1). FullStackException, to signal the error that arises if we try to FullStackException, to signal the error that arises if we try to

insert a new element into a full array.insert a new element into a full array. Exception FullStackException is specific to this Exception FullStackException is specific to this

implementation and is not defined in the stack ADT. implementation and is not defined in the stack ADT.

Page 13: Data Structures- Part8 stacks and queues

Code Fragment 5.3: Code Fragment 5.3: Implementing a Implementing a stack using an array of a given size, stack using an array of a given size, NN..

Page 14: Data Structures- Part8 stacks and queues

Analyzing the Array-Based Stack Analyzing the Array-Based Stack ImplementationImplementation

The correctness of the methods in the array-based implementation follows The correctness of the methods in the array-based implementation follows immediately from the definition of the methods themselves. immediately from the definition of the methods themselves.

Note that we could have avoided resetting the old Note that we could have avoided resetting the old SS[[tt] to ] to nullnull and we would still and we would still have a correct method. have a correct method.

There is a trade-off in being able to avoid this assignment should we be thinking There is a trade-off in being able to avoid this assignment should we be thinking about implementing these algorithms in Java.about implementing these algorithms in Java.

The trade-off involves the Java The trade-off involves the Java garbage collectiongarbage collection mechanism that searches mechanism that searches memory for objects that are no longer referenced by active objects, and reclaims memory for objects that are no longer referenced by active objects, and reclaims their space for future use. Let their space for future use. Let ee = = SS[[tt] be the top element before the pop method ] be the top element before the pop method is called.is called.

By making By making SS[[tt] a null reference, we indicate that the stack no longer needs to hold ] a null reference, we indicate that the stack no longer needs to hold a reference to object a reference to object ee. Indeed, if there are no other active references to . Indeed, if there are no other active references to ee, then , then the memory space taken by the memory space taken by ee will be reclaimed by the garbage collector. will be reclaimed by the garbage collector.

Table 5.1Table 5.1 shows the running times for methods in a realization of a stack by an shows the running times for methods in a realization of a stack by an array. Each of the stack methods in the array realization executes a constant array. Each of the stack methods in the array realization executes a constant number of statements involving arithmetic operations, comparisons, and number of statements involving arithmetic operations, comparisons, and assignments. assignments.

In addition, pop also calls isEmpty, which itself runs in constant time. Thus, in this In addition, pop also calls isEmpty, which itself runs in constant time. Thus, in this implementation of the Stack ADT, each method runs in constant time, that is, they implementation of the Stack ADT, each method runs in constant time, that is, they each run in each run in OO(1) time.(1) time.

Page 15: Data Structures- Part8 stacks and queues

Table 5.1: Table 5.1: Performance of a stack realized by an array. The space Performance of a stack realized by an array. The space usage is usage is OO((NN), where ), where NN is the size of the array, determined at the time the is the size of the array, determined at the time the stack is instantiated. Note that the space usage is independent from the stack is instantiated. Note that the space usage is independent from the number number nn ≤ ≤ NN of elements that are actually in the stack. of elements that are actually in the stack.

MethodMethod TimeTime

sizesize OO(1)(1)

is Emptyis Empty OO(1)(1)

toptop OO(1)(1)

pushpush OO(1)(1)

poppop OO(1)(1)

Page 16: Data Structures- Part8 stacks and queues

A Drawback with the Array-Based A Drawback with the Array-Based Stack ImplementationStack Implementation

The array implementation of a stack is simple and efficient. This implementation has one negative aspect

it must assume a fixed upper bound, CAPACITY, on the ultimate size of the stack. In Code Fragment 5.4, we chose the capacity value 1,000 more or less arbitrarily. An application may actually need much less space than this,

which would waste memory. An application may need more space than this,

which would cause our stack implementation to generate an exception as soon as a client program tries to push its 1,001st object on the stack.

Thus, even with its simplicity and efficiency, the array-based stack implementation is not necessarily ideal.

Fortunately, there is another implementation, which we discuss next, that does not have a size limitation and use space proportional to the actual number of elements stored in the stack.

Still, in cases where we have a good estimate on the number of items needing to go in the stack,

the array-based implementation is hard to beat. Stacks serve a vital role in a number of computing applications, so it is helpful to have a fast

stack ADT implementation such as the simple array-based implementation.

Page 17: Data Structures- Part8 stacks and queues

Implementing a Stack with a Implementing a Stack with a Generic Linked ListGeneric Linked List

Using a singly linked list to implement the stack ADT. Using a singly linked list to implement the stack ADT. In designing such an implementation, we need to decide if In designing such an implementation, we need to decide if

the top of the stack is at the head the top of the stack is at the head or at the tail of the list. or at the tail of the list.

Since we can insert and delete elements in constant time only at the Since we can insert and delete elements in constant time only at the head. head. Thus, it is more efficient to have the top of the stack at the head of our Thus, it is more efficient to have the top of the stack at the head of our

list. list. in order to perform operation size in constant time, in order to perform operation size in constant time,

we keep track of the current number of elements in an instance variable.we keep track of the current number of elements in an instance variable. Rather than use a linked list that can only store objects of a certain Rather than use a linked list that can only store objects of a certain

type, we would like, in this case, to implement a generic stack using type, we would like, in this case, to implement a generic stack using a a genericgeneric linked list. linked list.

Thus, we need to use a generic kind of node to implement this Thus, we need to use a generic kind of node to implement this linked list. We show such a Node class in linked list. We show such a Node class in Code Fragment 5.6Code Fragment 5.6..

Page 18: Data Structures- Part8 stacks and queues

Implementing a Stack with a Implementing a Stack with a Generic Linked ListGeneric Linked List

Code Fragment 5.6: Code Fragment 5.6: Class Node, which implements a generic node Class Node, which implements a generic node for a singly linked list.for a singly linked list.

Page 19: Data Structures- Part8 stacks and queues

A Generic NodeStack ClassA Generic NodeStack Class A Java implementation of a stack, by means of a generic singly linked list, is A Java implementation of a stack, by means of a generic singly linked list, is

given in given in Code Fragment 5.7Code Fragment 5.7. . All the methods of the Stack interface are executed in constant time.All the methods of the Stack interface are executed in constant time. In addition to being time efficient, this linked list implementation has a In addition to being time efficient, this linked list implementation has a

space requirement that is space requirement that is OO((nn), where ), where nn is the current number of elements is the current number of elements in the stack. in the stack.

Thus, this implementation does not require that a new exception be created Thus, this implementation does not require that a new exception be created to handle size overflow problems. We use an instance variable top to refer to handle size overflow problems. We use an instance variable top to refer to the head of the list (which points to the to the head of the list (which points to the nullnull object if the list is empty). object if the list is empty).

When we push a new element When we push a new element ee on the stack, we simply create a new node on the stack, we simply create a new node vv for for ee, reference , reference ee from from vv, and insert , and insert vv at the head of the list. at the head of the list.

Likewise, when we pop an element from the stack, we simply remove the Likewise, when we pop an element from the stack, we simply remove the node at the head of the list and return its element. Thus, we perform all node at the head of the list and return its element. Thus, we perform all insertions and removals of elements at the head of the list. insertions and removals of elements at the head of the list.

Page 20: Data Structures- Part8 stacks and queues

Code Fragment 5.7: Class NodeStack, which implements the Stack interface using a singly linked list, whose nodes are objects of class Node from Code Fragment 5.6.

Page 21: Data Structures- Part8 stacks and queues

Reversing an Array Using a StackReversing an Array Using a Stack

We can use a stack to reverse the elements in an array, We can use a stack to reverse the elements in an array, thereby producing a nonrecursive algorithm for the array-reversal problem thereby producing a nonrecursive algorithm for the array-reversal problem

introduced in introduced in Section 3.5.1.Section 3.5.1. The basic idea is simply to push all the elements of the array in order into a The basic idea is simply to push all the elements of the array in order into a

stack and then fill the array back up again by popping the elements off of stack and then fill the array back up again by popping the elements off of the stack. the stack.

In In Code Fragment 5.8Code Fragment 5.8, we give a Java implementation of this algorithm. , we give a Java implementation of this algorithm. Incidentally, this method also illustrates how we can use generic types in a Incidentally, this method also illustrates how we can use generic types in a simple application that uses a generic stack. simple application that uses a generic stack.

In particular, when the elements are popped off the stack in this example, In particular, when the elements are popped off the stack in this example, they are automatically returned as elements of the E type; hence, they can they are automatically returned as elements of the E type; hence, they can be immediately returned to the input array. We show an example use of this be immediately returned to the input array. We show an example use of this method in method in Code Fragment 5.9Code Fragment 5.9..

Page 22: Data Structures- Part8 stacks and queues

Code Fragment 5.8: A generic method that reverses the elements in an array of type E objects, using a stack declared using the Stack<E> interface.

Page 23: Data Structures- Part8 stacks and queues

Code Fragment 5.9: A test of the reverse method using two arrays.

Page 24: Data Structures- Part8 stacks and queues

Code Fragment 5.8: A generic method that reverses the elements in an array of type E objects, using a stack declared using the Stack<E> interface.

Page 25: Data Structures- Part8 stacks and queues

Code Fragment 5.9: A test of the reverse method using two arrays.

Page 26: Data Structures- Part8 stacks and queues

Matching Parentheses and HTML TagsMatching Parentheses and HTML Tags

Two related applications of stacks, Two related applications of stacks, the first of which is for matching parentheses the first of which is for matching parentheses and grouping symbols in arithmetic expressions.and grouping symbols in arithmetic expressions.

Arithmetic expressions can contain various pairs of Arithmetic expressions can contain various pairs of grouping symbols, such asgrouping symbols, such as•• Parentheses: "(" and ")"Parentheses: "(" and ")"•• Braces: "{" and "}"Braces: "{" and "}"•• Brackets: "[" and "]"Brackets: "[" and "]"•• Floor function symbols: " " and " "⌊ ⌋Floor function symbols: " " and " "⌊ ⌋•• Ceiling function symbols: " " and " ,"⌈ ⌉Ceiling function symbols: " " and " ,"⌈ ⌉

and each opening symbol must match with its and each opening symbol must match with its corresponding closing symbol. :corresponding closing symbol. :

[(5 + [(5 + xx) − () − (yy + + zz)].)].

Page 27: Data Structures- Part8 stacks and queues

Matching Parentheses and HTML TagsMatching Parentheses and HTML Tags

The following examples further illustrate The following examples further illustrate this concept: this concept:

•• Correct: ( )(( )){([( )])}Correct: ( )(( )){([( )])}

•• Correct: ((( )(( )){([( )])}))Correct: ((( )(( )){([( )])}))

•• Incorrect: )(( )){([( )])}Incorrect: )(( )){([( )])}

•• Incorrect: ({[])}Incorrect: ({[])}

•• Incorrect: (.Incorrect: (.

Page 28: Data Structures- Part8 stacks and queues

An Algorithm for Parentheses MatchingAn Algorithm for Parentheses Matching

An important problem in processing arithmetic expressions is to make sure their An important problem in processing arithmetic expressions is to make sure their grouping symbols match up correctly.grouping symbols match up correctly.

We can use a stack We can use a stack SS to perform the matching of grouping symbols in an arithmetic to perform the matching of grouping symbols in an arithmetic expression with a single left-to-right scan. expression with a single left-to-right scan.

The algorithm tests that left and right symbols match up and also that the left and The algorithm tests that left and right symbols match up and also that the left and right symbols are both of the same type.right symbols are both of the same type.

Suppose we are given a sequence Suppose we are given a sequence XX = = xx00xx11xx2…2…xnxn−1, where each −1, where each xixi is a is a tokentoken that that can be a grouping symbol, a variable name, an arithmetic operator, or a number. can be a grouping symbol, a variable name, an arithmetic operator, or a number.

The basic idea behind checking that the grouping symbols in The basic idea behind checking that the grouping symbols in SS match correctly, match correctly, is to process the tokens in is to process the tokens in XX in order. in order. Each time we encounter an opening symbol, we push that symbol onto Each time we encounter an opening symbol, we push that symbol onto SS, , and each time we encounter a closing symbol, we pop the top symbol from the stack and each time we encounter a closing symbol, we pop the top symbol from the stack SS

(assuming (assuming SS is not empty) and we check that these two symbols are of the same type. is not empty) and we check that these two symbols are of the same type. If the stack is empty after we have processed the whole sequence, then the symbols in If the stack is empty after we have processed the whole sequence, then the symbols in XX

match. match. Assuming that the push and pop operations are implemented to run in constant time, this Assuming that the push and pop operations are implemented to run in constant time, this

algorithm runs in algorithm runs in OO((nn), that is linear, time. We give a pseudo-code description of this ), that is linear, time. We give a pseudo-code description of this algorithm in algorithm in Code Fragment 5.10Code Fragment 5.10..

Page 29: Data Structures- Part8 stacks and queues

Code Fragment 5.10: Algorithm for matching grouping symbols in an arithmetic expression.

Page 30: Data Structures- Part8 stacks and queues

Matching Tags in an HTML DocumentMatching Tags in an HTML Document

Another application in which matching is important is in the Another application in which matching is important is in the validation of HTML documents. validation of HTML documents.

HTML is the standard format for hyperlinked documents on the HTML is the standard format for hyperlinked documents on the Internet. IInternet. I

n an HTML document, portions of text are delimited by n an HTML document, portions of text are delimited by HTML tagsHTML tags. . A simple opening HTML tag has the form "<name>" and the A simple opening HTML tag has the form "<name>" and the

corresponding closing tag has the form "</name>." Commonly used corresponding closing tag has the form "</name>." Commonly used HTML tags includeHTML tags include•• body: document bodybody: document body•• h1: section headerh1: section header•• center: center justifycenter: center justify•• p: paragraphp: paragraph•• ol: numbered (ordered) listol: numbered (ordered) list•• li: list item.li: list item.

We show a sample HTML document and a possible rendering in We show a sample HTML document and a possible rendering in Figure 5.3Figure 5.3..

Page 31: Data Structures- Part8 stacks and queues

Figure 5.3: Illustrating HTML tags. (a) An HTML document; (b) its rendering.

Page 32: Data Structures- Part8 stacks and queues

Code Fragment 5.11: A complete Java program for testing if an HTML document has fully matching tags. (Continues in Code Fragment 5.12.)

Page 33: Data Structures- Part8 stacks and queues

What is Queue?What is Queue? Queue is a data structure in which insertion is Queue is a data structure in which insertion is

done at one end (Front), while deletion is done at one end (Front), while deletion is performed at the other end (Rear)performed at the other end (Rear) Contrast with stack, where insertion and deletion Contrast with stack, where insertion and deletion

at one and the same endat one and the same end It is First In, First Out (FIFO) structureIt is First In, First Out (FIFO) structure

For example, customers standing in a check-out For example, customers standing in a check-out line in a store, the first customer in is the first line in a store, the first customer in is the first customer served.customer served.

Page 34: Data Structures- Part8 stacks and queues

Queue operationsQueue operations Enqueue: insert an element at the rear of the Enqueue: insert an element at the rear of the

listlist Dequeue: delete the element at the front of Dequeue: delete the element at the front of

the listthe list

Insert (Enqueue)

Remove(Dequeue) rearfront

Page 35: Data Structures- Part8 stacks and queues

Building a Queue Step-by-StepBuilding a Queue Step-by-Step There are several different algorithms to There are several different algorithms to

implement Enqueue and Dequeueimplement Enqueue and Dequeue EnqueuingEnqueuing

The The front indexfront index is always fixed is always fixed The The rear indexrear index moves forward in the array moves forward in the array

front

rear

Enqueue(3)

3

front

rear

Enqueue(6)

3 6

front

rear

Enqueue(9)

3 6 9

Page 36: Data Structures- Part8 stacks and queues

Dequeuing Dequeuing The element at the front of the queue is removedThe element at the front of the queue is removed Move all the elements after it by one positionMove all the elements after it by one position

Dequeue()

front

rear

6 9

Dequeue() Dequeue()

front

rear

9

rear = -1

front

Building a Queue Step-by-StepBuilding a Queue Step-by-Step

Page 37: Data Structures- Part8 stacks and queues

The Queue Abstract Data TypeThe Queue Abstract Data Type Formally, the queue abstract data type defines a collection that keeps Formally, the queue abstract data type defines a collection that keeps

objects in a sequence, where objects in a sequence, where element access and deletion are restricted to the first element in the sequence, element access and deletion are restricted to the first element in the sequence,

the the frontfront of the queue, of the queue, and element insertion is restricted to the end of the sequence, the and element insertion is restricted to the end of the sequence, the rearrear of the of the

queue. queue. This restriction enforces the rule that items are inserted and deleted in a queue This restriction enforces the rule that items are inserted and deleted in a queue

according to the first-in first-out (FIFO) principle.according to the first-in first-out (FIFO) principle. The The queuequeue abstract data type (ADT) supports the following two fundamental abstract data type (ADT) supports the following two fundamental

methods:methods: enqueue(enqueue(ee): Insert element ): Insert element ee at the rear of the queue. at the rear of the queue. dequeue(): Remove and return from the queue the object at the front; dequeue(): Remove and return from the queue the object at the front;

an error occurs if the queue is empty.an error occurs if the queue is empty. Additionally, similar to the case with the Stack ADT, the queue ADT includes Additionally, similar to the case with the Stack ADT, the queue ADT includes

the following supporting methods:the following supporting methods: size(): Return the number of objects in the queue.size(): Return the number of objects in the queue. isEmpty(): Return a Boolean value that indicates whether the queue is empty.isEmpty(): Return a Boolean value that indicates whether the queue is empty.

front(): Return, but do not remove, the front object in the queue; front(): Return, but do not remove, the front object in the queue; an error occurs if the queue is empty.an error occurs if the queue is empty.

Page 38: Data Structures- Part8 stacks and queues

The Queue Abstract Data TypeThe Queue Abstract Data Type

Example 5.4: The Example 5.4: The following table shows a following table shows a series of queue series of queue operations and their operations and their effects on an initially effects on an initially empty queue Q of integer empty queue Q of integer objects. For simplicity, we objects. For simplicity, we use integers instead of use integers instead of integer objects as integer objects as arguments of the arguments of the operations.operations.

OperationOperation OutputOutput front ← Q ← rearfront ← Q ← rear

enqueue(5)enqueue(5)

enqueue(3)enqueue(3)

dequeue( )dequeue( )

enqueue(7)enqueue(7)

dequeue( )dequeue( )

front( )front( )

dequeue( )dequeue( )

dequeue( )dequeue( )

isEmpty( )isEmpty( )

enqueue(9)enqueue(9)

enqueue(7)enqueue(7)

size()size()

enqueue(3)enqueue(3)

enqueue(5)enqueue(5)

dequeue( )dequeue( )

--

--

55

--

33

77

77

"error""error"

truetrue

--

--

22

--

--

99

(5)(5)

(5, 3) (5, 3)

(3) (3)

(3, 7)(3, 7)

(7) (7)

(7)(7)

( ) ( )

( ) ( )

( ) ( )

(9)(9)

(9, 7) (9, 7)

(9, 7)(9, 7)

(9, 7, 3)(9, 7, 3)

(9, 7, 3, 5)(9, 7, 3, 5)

(7, 3, 5)(7, 3, 5)

Page 39: Data Structures- Part8 stacks and queues

The Queue Abstract Data TypeThe Queue Abstract Data Type Example ApplicationsExample Applications There are several possible applications for queues. There are several possible applications for queues.

Stores, Stores, theaters, theaters, reservation centers, reservation centers, and other similar services typically process customer requests and other similar services typically process customer requests

according to the FIFO principle. according to the FIFO principle. A queue would therefore be a logical choice for a data A queue would therefore be a logical choice for a data

structure to handle transaction processing for such structure to handle transaction processing for such applications. applications.

For example, it would be a natural choice for handling For example, it would be a natural choice for handling calls to the reservation center of an airline or to the box calls to the reservation center of an airline or to the box office of a theater.office of a theater.

Page 40: Data Structures- Part8 stacks and queues

A Queue Interface in JavaA Queue Interface in Java A Java interface for the queue ADT is given in A Java interface for the queue ADT is given in Code Code

Fragment 5.13Fragment 5.13. . This generic interface specifies that objects of arbitrary This generic interface specifies that objects of arbitrary

object types can be inserted into the queue. object types can be inserted into the queue. Thus, we don't have to use explicit casting when Thus, we don't have to use explicit casting when

removing elements.removing elements. Note that the size and isEmpty methods have the same Note that the size and isEmpty methods have the same

meaning as their counterparts in the Stack ADT. meaning as their counterparts in the Stack ADT. These two methods, as well as the front method, are These two methods, as well as the front method, are

known as known as accessoraccessor methods, for they return a value and methods, for they return a value and do not change the contents of the data structure.do not change the contents of the data structure.

Page 41: Data Structures- Part8 stacks and queues

Code Fragment 5.13: Interface Queue documented with comments in Javadoc style.

Page 42: Data Structures- Part8 stacks and queues

A Simple Array-Based Queue ImplementationA Simple Array-Based Queue Implementation

We present a simple realization of a queue by means of We present a simple realization of a queue by means of an array, an array, QQ, of fixed capacity, storing its elements. , of fixed capacity, storing its elements.

Since the main rule with the queue ADT is that we insert Since the main rule with the queue ADT is that we insert and delete objects according to the FIFO principle, we and delete objects according to the FIFO principle, we must decide how we are going to keep track of the front must decide how we are going to keep track of the front and rear of the queue.and rear of the queue.

One possibility is to adapt the approach we used for the One possibility is to adapt the approach we used for the stack implementation, stack implementation, letting letting QQ[0] be the front of the queue and then letting the queue [0] be the front of the queue and then letting the queue

grow from there. grow from there. This is not an efficient solution, however, for it requires that we move This is not an efficient solution, however, for it requires that we move

all the elements forward one array cell each time we perform a all the elements forward one array cell each time we perform a dequeue operation. dequeue operation.

Such an implementation would therefore take Such an implementation would therefore take OO((nn) time to perform the ) time to perform the dequeue method, where dequeue method, where nn is the current number of objects in the is the current number of objects in the queue. queue.

If we want to achieve constant time for each queue method, we need a If we want to achieve constant time for each queue method, we need a different approach, but that approach may have its own demerits.different approach, but that approach may have its own demerits.

Page 43: Data Structures- Part8 stacks and queues

Using an Array in a Circular WayUsing an Array in a Circular Way To avoid moving objects once they are placed in To avoid moving objects once they are placed in QQ, ,

we define two variables we define two variables ff and and rr, which have the following meanings: , which have the following meanings: ff is an index to the cell of is an index to the cell of QQ storing the first element of the queue (next candidate to be removed by a dequeue storing the first element of the queue (next candidate to be removed by a dequeue

operation), operation), ff = = rr queue is empty. queue is empty. rr is an index to the next available array cell in is an index to the next available array cell in QQ..

Initially, we assign Initially, we assign ff = = rr = 0, which indicates that the queue is empty. = 0, which indicates that the queue is empty. When we remove an element from the front of the queue, increment When we remove an element from the front of the queue, increment ff to index to index

the next cell. the next cell. Adding an element, we store it in cell Adding an element, we store it in cell QQ[[rr] and increment ] and increment rr to index the next to index the next

available cell in available cell in QQ. . This scheme allows us to implement methods front, enqueue, and dequeue in This scheme allows us to implement methods front, enqueue, and dequeue in

constant time, that is, constant time, that is, OO(1) time. (1) time. However, there is still a problem with this approach.However, there is still a problem with this approach. what happens if we repeatedly enqueue and dequeue a single element N

different times. We would have We would have ff = = rr = = NN. If we were then to try to insert the element just one more . If we were then to try to insert the element just one more

time, we would get an array-out-of-bounds error (since the time, we would get an array-out-of-bounds error (since the NN valid locations in valid locations in QQ are are from from QQ[0] to [0] to QQ[[NN − 1]), even though there is plenty of room in the queue in this case. − 1]), even though there is plenty of room in the queue in this case.

To avoid this problem and be able to utilize all of the array To avoid this problem and be able to utilize all of the array QQ, we let the , we let the ff and and rr indices "wrap around" the end of indices "wrap around" the end of QQ. .

That is, we now view That is, we now view QQ as a "circular array" that goes from as a "circular array" that goes from QQ[0] to [0] to QQ[[NN − 1] and then − 1] and then immediately back to immediately back to QQ[0] again. (See [0] again. (See Figure 5.4Figure 5.4.).)

Page 44: Data Structures- Part8 stacks and queues

Figure 5.4: Using array Q in a circular fashion:

(a) the "normal" configuration with f ≤ r; (b) the "wrapped around" configuration (b) with r < f. The cells storing queue elements are highlighted.

Page 45: Data Structures- Part8 stacks and queues

Using the Modulo Operator to Using the Modulo Operator to Implement a Circular ArrayImplement a Circular Array

Implementing this circular view of Implementing this circular view of QQ is actually pretty easy. is actually pretty easy. Each time we increment Each time we increment ff or or rr, we compute this increment as , we compute this increment as

"("(ff + 1) mod + 1) mod NN" or "(" or "(rr + 1) mod + 1) mod NN," respectively.," respectively. "mod" is the "mod" is the modulomodulo operator, which is computed by taking operator, which is computed by taking

the remainder after an integral division. the remainder after an integral division. For example, 14 divided by 4 is 3 with remainder 2, so 14 For example, 14 divided by 4 is 3 with remainder 2, so 14

mod 4 = 2. mod 4 = 2. Java uses "%" to denote the modulo operator. By using the Java uses "%" to denote the modulo operator. By using the

modulo operator, we can view modulo operator, we can view QQ as a circular array and as a circular array and implement each queue method in a constant amount of time implement each queue method in a constant amount of time (that is, (that is, OO(1) time). We describe how to use this approach to (1) time). We describe how to use this approach to implement a queue in implement a queue in Code Fragment 5.14Code Fragment 5.14..

Page 46: Data Structures- Part8 stacks and queues

Code Fragment 5.14: Implementation of a queue using a circular array. The implementation uses the modulo operator to "wrap" indices around the end of the array and it also includes two instance variables, f and r, which index the front of the queue and first empty cell after the rear of the queue respectively.

Page 47: Data Structures- Part8 stacks and queues

Using the Modulo Operator to Using the Modulo Operator to Implement a Circular ArrayImplement a Circular Array

The implementation above contains an important detail, which might be missed at first. The implementation above contains an important detail, which might be missed at first. Consider the situation that occurs if we enqueue Consider the situation that occurs if we enqueue NN objects into objects into QQ without dequeuing any without dequeuing any

of them. We would have of them. We would have ff = = rr, which is the same condition that occurs when the queue is , which is the same condition that occurs when the queue is empty. empty.

Hence, we would not be able to tell the difference between a full queue and an empty one Hence, we would not be able to tell the difference between a full queue and an empty one in this case. in this case.

Fortunately, this is not a big problem, and a number of ways for dealing with it exist.Fortunately, this is not a big problem, and a number of ways for dealing with it exist. The solution we describe here is to insist that The solution we describe here is to insist that QQ can never hold more than can never hold more than NN − 1 objects. − 1 objects. This simple rule for handling a full queue takes care of the final problem with our This simple rule for handling a full queue takes care of the final problem with our

implementation, and leads to the pseudo-coded descriptions of the queue methods given implementation, and leads to the pseudo-coded descriptions of the queue methods given in in Code Fragment 5.14Code Fragment 5.14. .

Note our introduction of an implementation-specific exception, called FullQueueException, Note our introduction of an implementation-specific exception, called FullQueueException, to signal that no more elements can be inserted in the queue. to signal that no more elements can be inserted in the queue.

Also note the way we compute the size of the queue by means of the expression (Also note the way we compute the size of the queue by means of the expression (NN − − ff + + rr) mod ) mod NN, which gives the correct result both in the "normal" configuration (when , which gives the correct result both in the "normal" configuration (when ff ≤ ≤ rr) and ) and in the "wrapped around" configuration (when in the "wrapped around" configuration (when rr < < ff). ).

The Java implementation of a queue by means of an array is similar to that of a stack, and The Java implementation of a queue by means of an array is similar to that of a stack, and is left as an is left as an exercise (P-5.4)exercise (P-5.4)..

Table 5.2Table 5.2 shows the running times of methods in a realization of a queue by an array. As shows the running times of methods in a realization of a queue by an array. As with our array-based stack implementation, each of the queue methods in the array with our array-based stack implementation, each of the queue methods in the array realization executes a constant number of statements involving arithmetic operations, realization executes a constant number of statements involving arithmetic operations, comparisons, and assignments. Thus, each method in this implementation runs in comparisons, and assignments. Thus, each method in this implementation runs in OO(1) (1) time.time.

Page 48: Data Structures- Part8 stacks and queues

Table 5.2:Table 5.2: Performance of a queue realized by an arrayPerformance of a queue realized by an array

The space usage is The space usage is OO((NN), ), where where NN is the size of the is the size of the array, determined at the array, determined at the time the queue is created. time the queue is created. Note that the space Note that the space usage is independent usage is independent from the number from the number nn < < NN of of elements that are actually elements that are actually in the queue.in the queue.

MethodMethod TimeTime

SizeSize

isEmptyisEmpty

frontfront

enqueueenqueue

dequeuedequeue

OO(1)(1)

OO(1) (1)

OO(1) (1)

OO(1)(1)

OO(1)(1)

Problem is the capacity of the queue to be some fixed value. In a real application, we may actually need more or less queue capacity than this, but if we have a good capacity estimate, then the array-based implementation is quite efficient.

Page 49: Data Structures- Part8 stacks and queues

Implementing a Queue with a Implementing a Queue with a Generic Linked ListGeneric Linked List

We can efficiently implement the queue ADT We can efficiently implement the queue ADT using a generic singly linked list. using a generic singly linked list.

For efficiency reasons, For efficiency reasons, the front of the queue to be at the head of the list, the front of the queue to be at the head of the list, and the rear of the queue to be at the tail of the list. and the rear of the queue to be at the tail of the list. In this way, we remove from the head and insert at In this way, we remove from the head and insert at

the tail. (Why would it be bad to insert at the head and the tail. (Why would it be bad to insert at the head and remove at the tail?) Note that we need to maintain remove at the tail?) Note that we need to maintain references to both the head and tail nodes of the list. references to both the head and tail nodes of the list. Rather than go into every detail of this Rather than go into every detail of this implementation, we simply give a Java implementation, we simply give a Java implementation for the fundamental queue methods in implementation for the fundamental queue methods in Code Fragment 5.15Code Fragment 5.15..

Page 50: Data Structures- Part8 stacks and queues

Code Fragment 5.15: Code Fragment 5.15: Methods enqueue and dequeue in the Methods enqueue and dequeue in the implementation of the queue ADT by means of a singly linked list, using implementation of the queue ADT by means of a singly linked list, using nodes from class Node of nodes from class Node of Code Fragment 5.6Code Fragment 5.6..

Each of the methods of the singly linked list implementation of the queue ADT runs in O(1) time. We also avoid the need to specify a maximum size for the queue, but this benefit comes at the expense of increasing the amount of space used per element. Still, the methods in the singly linked list queue implementation are more complicated than we might like, for we must take extra care in how we deal with special cases where the queue is empty before an enqueue or where the queue becomes empty after a dequeue.

Page 51: Data Structures- Part8 stacks and queues

Round Robin SchedulersRound Robin Schedulers A popular use of the queue data structure is to implement A popular use of the queue data structure is to implement

a a round robinround robin scheduler, where scheduler, where we iterate through a collection of elements in a circular fashion we iterate through a collection of elements in a circular fashion

and "service" each element by performing a given action on it. and "service" each element by performing a given action on it. Such a schedule is used, Such a schedule is used,

for example, to fairly allocate a resource that must be shared by a for example, to fairly allocate a resource that must be shared by a collection of clients. collection of clients.

For instance, we can use a round robin scheduler to allocate a slice For instance, we can use a round robin scheduler to allocate a slice of CPU time to various applications running concurrently on a of CPU time to various applications running concurrently on a computer.computer.

We can implement a round robin scheduler using a We can implement a round robin scheduler using a queue, queue, QQ, by repeatedly performing the following steps , by repeatedly performing the following steps (see (see Figure 5.5Figure 5.5): ): 1.1. ee ← ← QQ.dequeue().dequeue()2.2. Service element Service element ee3.3. QQ.enqueue(.enqueue(ee))

Page 52: Data Structures- Part8 stacks and queues

Figure 5.5: The three iterative steps for using a queue to implement a round robin scheduler.

Page 53: Data Structures- Part8 stacks and queues

The Josephus ProblemThe Josephus Problem In the children's game "hot potato," a group of In the children's game "hot potato," a group of nn children children

sit in a circle passing an object, called the "potato," sit in a circle passing an object, called the "potato," around the circle. The potato begins with a starting child around the circle. The potato begins with a starting child in the circle, and the children continue passing the potato in the circle, and the children continue passing the potato until a leader rings a bell, at which point the child holding until a leader rings a bell, at which point the child holding the potato must leave the game after handing the potato the potato must leave the game after handing the potato to the next child in the circle. After the selected child to the next child in the circle. After the selected child leaves, the other children close up the circle. This leaves, the other children close up the circle. This process is then continued until there is only one child process is then continued until there is only one child remaining, who is declared the winner. If the leader remaining, who is declared the winner. If the leader always uses the strategy of ringing the bell after the always uses the strategy of ringing the bell after the potato has been passed potato has been passed kk times, for some fixed value times, for some fixed value kk, , then determining the winner for a given list of children is then determining the winner for a given list of children is known as the known as the Josephus problemJosephus problem..

Page 54: Data Structures- Part8 stacks and queues

Solving the Josephus Problem Solving the Josephus Problem Using a QueueUsing a Queue

We can solve the Josephus problem for a collection of We can solve the Josephus problem for a collection of nn elements using a queue, by associating the potato with elements using a queue, by associating the potato with the element at the front of the queue and storing the element at the front of the queue and storing elements in the queue according to their order around elements in the queue according to their order around the circle. Thus, passing the potato is equivalent to the circle. Thus, passing the potato is equivalent to dequeuing an element and immediately enqueuing it dequeuing an element and immediately enqueuing it again. After this process has been performed again. After this process has been performed kk times, we times, we remove the front element by dequeuing it from the queue remove the front element by dequeuing it from the queue and discarding it. We show a complete Java program for and discarding it. We show a complete Java program for solving the Josephus problem using this approach in solving the Josephus problem using this approach in Code Fragment 5.16Code Fragment 5.16, which describes a solution that , which describes a solution that runs in runs in OO((nknk) time. (We can solve this problem faster ) time. (We can solve this problem faster using techniques beyond the scope of this book.)using techniques beyond the scope of this book.)

Page 55: Data Structures- Part8 stacks and queues

Code Fragment 5.16: A complete Java program for solving the Josephus problem using a queue. Class NodeQueue is shown in Code Fragment 5.15.