You are on page 1of 80

Data Structure

An Introduction to Stacks and 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).

Additionally, a stack supports the following accessor methods for convenience:


• top(): Returns the top element of the stack, without removing it (or null if the stack is empty).
• size(): Returns the number of elements in the stack.
• isEmpty(): Returns a boolean indicating whether 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;

public int getData() {


return data;
}
public Node getNext() {
return next;
}
public void setData(int x) {
System.out.println("inside setData");
data = x;
}

public void setNext(Node p) {


next = p;
}
}
Stack Class in Java Tracing push operation
Tracing pop operation
A Simple Array-Based Stack
Implementation
As our first implementation of the stack ADT, we store elements in an array, named data, with capacity N for
some fixed N. We oriented the stack so that the bottom element of the stack is always stored in cell data[0], and
the top element of the stack in cell data[t] for index t that is equal to one less than the current size of the stack.

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

• ADTs can be implemented in multiple ways by classes:


• ArrayList and LinkedList implement List
• HashSet and TreeSet implement Set
• LinkedList , ArrayDeque, etc. implement
Queue
• Java messed up on Stack—there's no Stack interface, just a class.

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

• ArrayList and LinkedList are implementations

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").

• basic stack operations:


• push: Add an element to the top.
• pop: Remove the top element.
• peek: Examine the top element.

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

• Matching up related pairs of things: method1


return var
local vars
parameters
• find out whether a string is a palindrome
• examine a file to see if its braces { } and other operators match

• 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);
}

• Instead, you pull contents out of the stack to view them.


• Idiom: Remove each element until the stack is empty.

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.

An expression can be defined as follows...

An expression is a collection of operators and operands


that represents a specific value.
Expressions
In above definition, operator is a symbol which performs a
particular task like arithmetic operation or logical operation or
conditional operation etc.,

Operands are the values on which the operators can perform


the task. Here operand can be a direct value or variable or
address of memory location.
Expressions Types in Data Structure
The way to write arithmetic expression is known as a notation. An arithmetic expression can be
written in three different but equivalent notations. These notations are:

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.

2. Prefix Notation (Polish Notation)


In this notation, operator is prefixed to operands, i.e. operator is written ahead of operands.
For example, +ab. This is equivalent to its infix notation a + b.

3. Postfix Notation (Reversed Polish Notation)


In this notation style, the operator is postfixed to the operands i.e., the operator is written
after the operands. For example, ab+. This is equivalent to its infix notation a + b.
Infix Expression
In infix expression, operator is used in between the operands.

The general structure of an Infix expression is as follows...

Operand1 Operator Operand2


Postfix Expression
• In postfix expression, operator is used after operands. We
can say that "Operator follows the Operands".

The general structure of Postfix expression is as follows...

Operand1 Operand2 Operator


Prefix Expression
• In prefix expression, operator is used before operands. We
can say that "Operands follows the Operator".

The general structure of Prefix expression is as follows...

Operator Operand1 Operand2


Expressions Types in Data Structure
• Every expression can be represented using all the three
different types of expressions.

• And we can convert an expression from one form to another


form like Infix to Postfix, Infix to Prefix, Prefix to Postfix and
vice versa.
Infix to Postfix Conversion
To convert any Infix expression into Postfix or Prefix
expression we can use the following procedure...

1. Find all the operators in the given Infix expression.

2. Find the order of operators evaluated according to their


Operator precedence.

3. Convert each operator into required type of expression


(Postfix or Prefix) in the same order.
Infix to Postfix Conversion
• Example
Consider the following Infix Expression to be converted into Postfix
Expression...
D=A+B*C

• Step 1 - The Operators in the given Infix Expression : = , + , *


• Step 2 - The Order of Operators according to their preference : * , + , =
• Step 3 - Now, convert the first operator * ----- D = A + B C *
• Step 4 - Convert the next operator + ----- D = A BC* +
• Step 5 - Convert the next operator = ----- D ABC*+ =

• Finally, given Infix Expression is converted into Postfix Expression as follows...


DABC*+=
Infix to Postfix Conversion using Stack
Data Structure
• To convert Infix Expression into Postfix Expression using a stack data structure, We can use the
following steps...

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

Consider the following Infix Expression...


(A+B)*(C-D)

The given infix expression can be converted into postfix


expression using Stack data Structure as follows...
Infix to Postfix Conversion using Stack
Data Structure
Infix to Postfix Conversion using Stack
Data Structure

The final Postfix Expression is as


follows...
AB+CD-*
Infix to Postfix Conversion using Stack Data
Structure
Notes:

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 other words, there is a distinction between incoming priority, which


is very high for a "(", and instack priority, which is lower than any operator.

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

2. If the reading symbol is operand, then push it on to the Stack.

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 4: an operator is encountered, so 4 and 3 are popped, summed, then pushed


back onto stack.

Move 5: operator * is current token, so 7 and 2 are popped, multiplied, pushed back
onto stack.

Move 7: stack top holds correct value.

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 ‘(‘.

• Step 2: Obtain the “nearly” postfix expression of the modified


expression i.e. CB*A+.

• Step 3: Reverse the postfix expression. Hence in our example prefix is


+A*BC.
Prefix to Postfix Conversion
• Prefix: An expression is called the prefix expression if the operator appears in the expression
before the operands. Simply of the form (operator operand1 operand2).

Example : *+AB-CD (Infix : (A+B) * (C-D) )

• 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).

Example : AB+CD-* (Infix : (A+B * (C-D) )

• Given a Prefix expression, convert it into a Postfix expression.

• 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)

• If the symbol is an operand, then push it onto the Stack


• If the symbol is an operator, then pop two operands
from the Stack

Create a string by concatenating the two operands and the


operator after them.

string = operand1 + operand2 + operator

And push the resultant string back to Stack

•Repeat the above steps until end of Prefix expression.


Prefix to Postfix Conversion // reading from right to left
for (int i = length - 1; i >= 0; i--)
{
// check if symbol is operator
// JavaProgram to convert prefix to postfix if (isOperator(pre_exp.charAt(i)))
import java.util.*; {
// pop two operands from stack
class GFG { String op1 = s.peek();
s.pop();
// funtion to check if character String op2 = s.peek();
// is operator or not s.pop();
static boolean isOperator(char x)
{ // concat the operands and operator
switch (x) { String temp = op1 + op2 + pre_exp.charAt(i);
case '+':
case '-': // Push String temp back to stack
case '/': s.push(temp);
case '*': }
return true;
} // if symbol is an operand
return false; else {
} // push the operand to the stack
s.push(pre_exp.charAt(i) + "");
// Convert prefix to Postfix expression }
}
static String preToPost(String pre_exp)
{
// stack contains only the Postfix expression
Stack<String> s = new Stack<String>(); return s.peek();
}
// length of expression
int length = pre_exp.length();
Prefix to Postfix Conversion

// 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

Here is a more complex expression: (A + B) * C - (D - E) * (F + G).


Converting between these notations
You can convert directly between these bracketed forms simply by moving the operator within the
brackets
e.g. (X + Y) or (X Y +) or (+ X Y).

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

Expression No Infix Notation Prefix Notation Postfix Notation

1 a+b +ab ab+


2 (a + b) * c *+abc ab+c*
3 a * (b + c) *a+bc abc+*
4 a/b+c/d +/ab/cd ab/cd/+
5 (a + b) * (c + d) *+ab+cd ab+cd+*
6 ((a + b) * c) - d -*+abcd ab+c*d-
Queues
Another fundamental data structure is the queue. It is a close “cousin” of the stack, but a queue is a collection
of objects that are inserted and removed according to the first-in, first-out (FIFO) principle. That is, elements
can be inserted at any time, but only the element that has been in the queue the longest can be next removed.

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.

People waiting in line to purchase tickets


Phone calls being routed to a customer service center
A Queue In Action
A Queue Structure
A Queue using Array
A Queue using Array
A Queue using Array
A Queue using Array
A Queue using Array
Queue Implementation using Linked-List:
Code Java
Queue Implementation using Linked-List:
Code Java
Tracing insert()
Queue Implementation using Linked-List:
Code Java
Tracing remove()
The Queue Abstract Data Type
Formally, the queue abstract data type defines a collection that keeps objects in a sequence, where element
access and deletion are restricted to the first element in the queue, and element insertion is restricted to the back
of the sequence. This restriction enforces the rule that items are inserted and deleted in a queue according to the
first-in, first-out (FIFO) principle.

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:

Additionally, the deque ADT will include the following accessors:


The Deque Abstract Data Type
The Deque Abstract Data Type

You might also like