Professional Documents
Culture Documents
Intro Stacks-Queues
Intro Stacks-Queues
LU-FSEG-BIS – 2021-2022
What is a Stack?
• A stack is a collection of data items where all data is inserted and removed from the same end - the top.
• A stack is considered a last-in, first-out (LIFO) data structure because the newest data is removed first and the
oldest data is removed last.
• Stacks are the simplest of all data structures, yet they are also among the most important, as they are used in a
host of different applications, and as a tool for many more sophisticated data structures and algorithms.
Formally, a stack is an abstract data type (ADT) that supports the following two update methods:
Stack Operations
• push(e): Adds element e to the top of the stack.
• pop(): Removes and returns the top element from the stack
(or null if the stack is empty).
By convention, we assume that elements added to the stack can have arbitrary type and that a newly created
stack is empty.
Stack Operations
• Stack
Stack In Action
The java.util.Stack Class
Because of the importance of the stack ADT, Java has included, since its original version, a concrete class
named java.util.Stack that implements the LIFO semantics of a stack.
Stack Class in Java
public class Node {
private int data;
private Node next;
Recalling that arrays start at index 0 in Java, when the stack holds elements from data[0] to data[t] inclusive, it
has size t + 1. By convention, when the stack is empty it will have t equal to 1 (and thus has size t + 1, which is
0).
A Simple Array-Based Stack Implementation
A Stack Interface in Java
• In order to formalize our abstraction of a stack, we define what is known as its application programming
interface (API) in the form of a Java interface, which describes the names of the methods that the ADT
supports and how they are to be declared and used.
A Simple Generic Array-Based Stack
Implementation
A Simple Generic Array-Based Stack
Implementation
main
A Simple ArrayList Based Generic Stack
Implementation
Analyzing the Array-Based Stack
Implementation
The correctness of the methods in the array-based implementation follows from our definition of index t. Note
well that when pushing an element, t is incremented before placing the new element, so that it uses the first
available cell.
A Drawback of This Array-Based Stack
Implementation
The array implementation of a stack is simple and efficient. Nevertheless, this implementation has one negative
aspect—it relies on a fixed-capacity array, which limits the ultimate size of the stack.
For convenience, we allow the user of a stack to specify the capacity as a parameter to the constructor (and offer a
default constructor that uses capacity of 1,000).
In cases where a user has a good estimate on the number of items needing to go in the stack, the array-based
implementation is hard to beat. However, if the estimate is wrong, there can be grave consequences. If the application
needs much less space than the reserved capacity, memory is wasted. Worse yet, if an attempt is made to push an item
onto a stack that has already reached its maximum capacity, the implementation throws an IllegalStateException,
refusing to store the new element.
Thus, even with its simplicity and efficiency, the array-based stack implementation is not necessarily ideal.
Fortunately, we will later demonstrate two approaches for implementing a stack without such a size limitation and with
space always proportional to the actual number of elements stored in the stack (a singly linked list for storage; a more
advanced array-based approach that overcomes the limit of a fixed capacity).
Implementing a Stack with a Singly
Linked List
Unlike our array-based implementation, the linked-list approach has memory usage that is always proportional
to the number of actual elements currently in the stack, and without an arbitrary capacity limit.
In designing such an implementation, we need to decide if the top of the stack is at the front or back of the list.
There is clearly a best choice here, however, since we can insert and delete elements in constant time only at the
front. With the top of the stack stored at the front of the list, all methods execute in constant time.
Reversing an Array Using a Stack
Implementing a Stack with a Singly
Linked List
Generic
Abstract Data Types
• abstract data type (ADT): A specification of a collection of data
and the operations that can be performed on it.
• Describes what a collection does, not how it does it
• Described in Java with interfaces (e.g., List, Map, Set)
• Separate from implementation
20
List ADT
• An ordered collection the form A0, A1, ..., AN-1, where N is the
size of the list
• Operations described in Java's List interface (subset):
add(elt, index) inserts the element at the specified position
in the list
remove(index) removes the element at the specified position
get(index) returns the element at the specified position
set(index, elt) replaces the element at the specified position
with the specified element
contains(elt) returns true if the list contains the element
size() returns the number of elements in the list
21
Stack ADT
• stack: a list with the restriction that insertions/deletions can
only be performed at the top/end of the list
• Last-In, First-Out ("LIFO")
• The elements are stored in order of insertion,
but we do not think of them as having indexes.
• The client can only add/remove/examine
the last element added (the "top").
22
Applications of Stacks
• Programming languages:
• method calls are placed onto a stack (call=push, return=pop)
return var
method3 local vars
parameters
return var
method2 local vars
parameters
• Sophisticated algorithms:
• searching through a maze with "backtracking"
• many programs use an "undo stack" of previous operations
23
Class Stack
Stack<E>() constructs a new stack with elements of type E
push(value) places given value on top of stack
pop() removes top value from stack and returns it;
throws EmptyStackException if stack is empty
peek() returns top value from stack without removing it;
throws EmptyStackException if stack is empty
size() returns number of elements in stack
isEmpty() returns true if stack has no elements
Stack<Integer> s = new Stack<Integer>();
s.push(42);
s.push(-3);
s.push(17); // bottom [42, -3, 17] top
System.out.println(s.pop()); // 17
24
Stack limitations/idioms
• Remember: You can’t loop over a stack like you do a list.
Stack<Integer> s = new Stack<Integer>();
...
for (int i = 0; i < s.size(); i++) {
do something with s.get(i);
}
while (!s.isEmpty()) {
do something with s.pop();
}
25
Exercise
• Write a method symbolsBalanced that accepts a
String as a parameter and returns whether or not
the parentheses and the curly brackets in that
String are balanced as they would have to be in a
valid Java program.
• Use a Stack to solve this problem.
26
Eclipse concepts
• workspace: a collection of projects
• stored as a directory
• project: a Java program
• must have your files in a project in order to be able to
compile, debug and run them
• by default stored in a directory in your workspace
• perspective: a view of your current project using a set
of pre-laid-out windows and menus
• Java perspective
• debugging perspective
27
Expressions
• What is an Expression?
In any programming language, if we want to perform any
calculation or to frame a condition etc., we use a set of
symbols to perform the task. These set of symbols makes an
expression.
1. Infix Notation
Infix notations are normal notations, that are used by us while write different mathematical
expressions. The Prefix and Postfix notations are quite different.
1. Read all the symbols one by one from left to right in the given Infix Expression.
2. If the reading symbol is operand, then directly print it to the result (Output).
3. If the reading symbol is left parenthesis '(', then Push it on to the Stack.
4. If the reading symbol is right parenthesis ')', then Pop all the contents of stack until
respective left parenthesis is popped and print each popped symbol to the result.
5. If the reading symbol is operator (+ , - , * , / etc.,), then Push it on to the Stack. However,
first pop the operators which are already on the stack that have higher or equal
precedence than current operator and print them to the result.
Infix to Postfix Conversion using Stack
Data Structure
Example
In this table, the stack grows toward the left. Thus, the top of the stack is
the leftmost symbol.
In move 3, the left parent was pushed without popping the * because *
had a lower priority then "(".
In move 5, "+" was pushed without popping "(" because you never pop
a left parenthesis until you get an incoming right parenthesis.
In move 7, the incoming right parenthesis caused the "+" and "(" to be
popped but only the "+" as written out.
In move 8, the current "*" had equal priority to the "*" on the stack.
So the "*" on the stack was popped, and the incoming "*" was pushed
ABC+*D* onto the stack.
Postfix Expression Evaluation
Postfix Expression Evaluation using Data
Structure
• A postfix expression can be evaluated using the Stack data structure. To evaluate a postfix
expression using Stack data structure we can use the following steps...
1. Read all the symbols one by one from left to right in the given Postfix Expression
3. If the reading symbol is operator (+ , - , * , / etc.,), then perform TWO pop operations and
store the two popped operands in two different variables (operand1 and operand2).
Then perform reading symbol operation using operand1 and operand2 and push result
back on to the Stack.
4. Finally! perform a pop operation and display the popped value as final result.
• Example
• Consider the following Expression...
Postfix Evaluation using Stack Data Structure
Notes
Move 5: operator * is current token, so 7 and 2 are popped, multiplied, pushed back
onto stack.
Notice that the postfix notation has been created to properly reflect operator
precedence. Thus, postfix expressions never need parentheses.
Postfix Expression Evaluation using Data
Structure
Postfix Expression Evaluation using Data
Structure
8*6
Infix to Prefix Conversion using Data
Structure
• Step 1: Reverse the infix expression
i.e. A+B*C will become C*B+A.
Note while reversing each ‘(‘ will become ‘)’ and each ‘)’ becomes ‘(‘.
• Postfix: An expression is called the postfix expression if the operator appears in the expression
after the operands. Simply of the form (operand1 operand2 operator).
• Conversion of Prefix expression directly to Postfix without going through the process of
converting them first to Infix and then to Postfix is much better in terms of computation and
better understanding the expression (Computers evaluate using Postfix expression).
Prefix to Postfix Conversion
Prefix to Postfix Conversion
Algorithm for Prefix to Postfix:
• Read the Prefix expression in reverse order (from right to left)
// Driver Code
public static void main(String args[])
{
String pre_exp = "*-A/BC-/AKL";
System.out.println("Postfix : "
+ preToPost(pre_exp));
}
}
Converting between these notations
The most straightforward method is to start by inserting all the implicit
brackets that show the order of evaluation e.g.:
Converting between these notations
Repeat this for all the operators in an expression, and finally remove any superfluous brackets.
You can use a similar trick to convert to and from parse trees - each bracketed triplet of an operator and
its two operands (or sub-expressions) corresponds to a node of the tree. The corresponding parse trees
are:
Prefix and Postfix Expressions in Data
Structure
We usually say that elements enter a queue at the back and are removed from the front. A metaphor for this
terminology is a line of people waiting to get on an amusement park ride.
There are many other applications of queues. Stores, theaters, reservation centers, and other similar services
typically process customer requests according to the FIFO principle. A queue would therefore be a logical
choice for a data structure to handle calls to a customer service center, or a wait-list at a restaurant. FIFO queues
are also used by many computing devices, such as a networked printer, or a Web server responding to requests.
The queue abstract data type (ADT) supports the following two update methods:
• The queue ADT also includes the following accessor methods (with first being analogous to the stack’s top
method):
The Queue Abstract Data Type
Generic Array-Based Queue Implementation
In this section, we will consider how to use an array to efficiently support the FIFO semantics of the Queue
ADT. Let’s assume that as elements are inserted into a queue, we store them in an array such that the first
element is at index 0, the second element at index 1, and so on.
Generic Array-Based Queue Implementation
Analyzing the Efficiency of an Array-
Based Queue
Implementing a Queue with a Generic Singly
Linked List
Generic Circular Queue
Double-Ended Queue
We consider a queue-like data structure that supports insertion and deletion at both the front and the back of the
queue. Such a structure is called a double-ended queue, or deque, which is usually pronounced “deck” to
avoid confusion with the dequeue method of the regular queue ADT, which is pronounced like the
abbreviation “D.Q.”
The deque abstract data type is more general than both the stack and the queue ADTs. The extra generality can
be useful in some applications. For example, we described a restaurant using a queue to maintain a waitlist.
Occasionally, the first person might be removed from the queue only to find that a table was not available;
typically, the restaurant will reinsert the person at the first position in the queue. It may also be that a customer at
the end of the queue may grow impatient and leave the restaurant. (We will need an even more general data
structure if we want to model customers leaving the queue from other positions.)
The Deque Abstract Data Type
The deque abstract data type is richer than both the stack and the queue ADTs. To provide a symmetrical
abstraction, the deque ADT is defined to support the following update methods: