0% found this document useful (0 votes)
4 views

DS unit-3

A stack is a linear data structure that follows the Last In First Out (LIFO) principle, allowing operations only at one end. It can be implemented using arrays or linked lists, with specific operations such as push, pop, and peek defined for data manipulation. Stacks are widely used in applications like expression evaluation, memory management, and string reversal.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

DS unit-3

A stack is a linear data structure that follows the Last In First Out (LIFO) principle, allowing operations only at one end. It can be implemented using arrays or linked lists, with specific operations such as push, pop, and peek defined for data manipulation. Stacks are widely used in applications like expression evaluation, memory management, and string reversal.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 17

UNIT-3:STACKS

STACKS
 A stack is a linear data structure where elements are stored in the LIFO (Last In First
Out) principle where the last element inserted would be the first element to be deleted.
 A stack is an Abstract Data Type (ADT), that is popularly used in most programming
languages.
 It is named stack because it has the similar operations as the real-world stacks,

for example − a pack of cards or a pile of plates, etc.


 A stack allows all data operations at one end only. At any given time, we can only access
the top element of a stack.
 The following diagram depicts a stack and its operations −

 A stack can be implemented by means of Array, Structure, Pointer, and Linked List.
 Stack can either be a fixed size one or it may have a sense of dynamic resizing.
 Here, we are going to implement stack using arrays, which makes it a fixed size stack
implementation.

1
UNIT-3:STACKS

1. When the stack is empty, trying to perform pop() operation causes an exception
“StackUnderFlow”
2. When the stack is full, trying to perform push() operation causes an exception
“StackUnderFlow”
3. When push() operation is performed ‘top’ will be incremented by one; similarly when
pop() is performed ‘top’ is decremented by one.
4. ‘top’ is a pointer which indicates the status of stack and topmost element on stack.

Implementation of Stack ADT


1.using arrays the implementation is static and the size of stack is to be fixed and given
initially.
2.Implementation of stacks is linked lists where the stack is dynamic and in general can
never become full.
We will then explore the use of ADT list to implement stacks.

Array Implementation of Stack ADT

First let us see how to use an array to implement a stack Stack. First of all since the
implementation is static we need to declare the array size ahead of time. The following are
two of the attributes associated with the Stack:
 MaxSize: This is the maximum size of the stack and is the size of the array.

2
UNIT-3:STACKS

 Top: This is the index of the top element of stack Stack which is the array which
stores elements of stack
The following are the operations associated with the stack:
 IsEmpty: This returns true if stack is empty, else false
 IsFull: This returns true if stack is full, else false.This operation is only defined for
Stack ADT when the ADT is represented using an array and hence is
implementation dependent.
 Top: This returns the element at the top of stack without removing it from the stack
 Push: This adds an element to the top of stack
 Pop: This deletes the element at the top of stack
 DisplayStack: This prints all the data in the stack
Now let us see in detail how the above operations are carried out when the stack is
represented using an array Stack.
Figure 8.2 shows an array Stack [0,1,…..MaxStack-1]
where MaxStack is the maximum size of the stack. The array Stack contains k+1 items
of type StackItem Type. The value of Top or index to access the stack in this case is k.

Basic Operations on Stacks


Stack operations are usually performed for initialization, usage and, de-initialization of the
stack ADT.
 push(): It inserts an element to the top of the stack. It takes O(1) time, as each node is
inserted at the head/top of the linked list.

3
UNIT-3:STACKS

 pop(): It removes an element from the top of the stack. It takes O(1) time, as the top
always points to the newly inserted node.
 peek(): It returns the top element of the stack.
 size(): It returns the size of the stack, i.e., the total number of items in a stack.
 isEmpty(): Returns a boolean value. It returns true if the stack is empty. Else, it
returns false.
 These are all built-in operations to carry out data manipulation and to check the status of
the stack.
 Stack uses pointers that always point to the topmost element within the stack, hence
called as the top pointer.
Algorithem for PUSH():
1.Checks if the stack is full.
2. If the stack is full, produces an error and exit.
3. If the stack is not full, increments top to point next
empty space.
4. Adds data element to the stack location, where top
is pointing.
5. Returns success.

Stack Deletion: pop()


The pop() is a data manipulation operation which removes elements from the stack. The
following pseudo code describes the pop() operation in a simpler way.
Algorithm
1. Checks if the stack is empty.
2. If the stack is empty, produces an error and exit.
3. If the stack is not empty, accesses the data element at
which top is pointing.
4. Decreases the value of top by 1.
5. Returns success.

4
UNIT-3:STACKS

Retrieving topmost Element from Stack: peek()


The peek() is an operation retrieves the topmost element within the stack, without deleting
it. This operation is used to check the status of the stack with the help of the top pointer.
1. START
2. return the element at the top of the stack
3. END
isFull():
The isFull() operation checks whether the stack is full. This operation is used to check the
status of the stack with the help of top pointer.
Algorithm
1. START
2. If the size of the stack is equal to the top position of the stack,
the stack is full. Return 1.
3. Otherwise, return 0.
4. END
verifying whether the Stack is empty: isEmpty()
The isEmpty() operation verifies whether the stack is empty. This operation is used to check
the status of the stack with the help of top pointer.
Algorithm
1. START
2. If the top value is -1, the stack is empty. Return 1.
3. Otherwise, return 0.
4. END

5
UNIT-3:STACKS

IMPLEMENTATION OF STACK USING LINKED LIST

 Linked lists offer a dynamic memory allocation alternative to arrays.


 Despite different data structures, time complexities for stack operations remain
consistent.
 Nodes are non-contiguously maintained in memory, each with a pointer to its
successor node.
 Stack overflow occurs when insufficient memory heap space is available for node
creation.
 Singly linked lists align standard linked list operations with stack operations,
adhering to the Last In, First Out (LIFO) principle.
 Top variable guides operations like Pop, Push, Peek, and Display.
 Unlike arrays, linked lists offer flexibility, eliminating risk of overflow.

6
UNIT-3:STACKS

Stack Implementation vs. Array vs. Linked List


 Array-based stacks work for a fixed number of data values, making them unsuitable
for unknown data sizes.
 Linked list stacks can work for unlimited data values, eliminating the need to fix the
initial size.
 Linked list stacks insert new elements as 'top' elements, pointing to the 'top' node.
 To remove an element, move 'top' to its previous node in the list.
 The first element's next field must always be NULL.
Before performing all the above operations, lets first define node structure
 Step 1 - Include all the header files which are used in the program. And declare all
the user defined functions.
 Step 2 - Define a 'Node' structure with two members data and next.
 Step 3 - Define a Node pointer 'top' and set it to NULL.
 Step 4 - Implement the main method by displaying Menu with list of operations and
make suitable function calls in the main method.
push(value) - Inserting an element into the Stack
We can use the following steps to insert a new node into the stack...
 Step 1 - Create a newNode with given value.
 Step 2 - Check whether stack is Empty (top == NULL)
 Step 3 - If it is Empty, then set newNode → next = NULL.
 Step 4 - If it is Not Empty, then set newNode → next = top.
 Step 5 - Finally, set top = newNode.
pop() - Deleting an Element from a Stack
We can use the following steps to delete a node from the stack...
 Step 1 - Check whether stack is Empty (top == NULL).
 Step 2 - If it is Empty, then display "Stack is Empty!!! Deletion is not
possible!!!" and terminate the function
 Step 3 - If it is Not Empty, then define a Node pointer 'temp' and set it to 'top'.
 Step 4 - Then set 'top = top → next'.

7
UNIT-3:STACKS

 Step 5 - Finally, delete 'temp'. (free(temp)).


display() - Displaying stack of elements
We can use the following steps to display the elements (nodes) of a stack...
 Step 1 - Check whether stack is Empty (top == NULL).
 Step 2 - If it is Empty, then display 'Stack is Empty!!!' and terminate the function.
 Step 3 - If it is Not Empty, then define a Node pointer 'temp' and initialize with top.
 Step 4 - Display 'temp → data --->' and move it to the next node. Repeat the same
until temp reaches to the first node in the stack. (temp → next != NULL).
 Step 5 - Finally! Display 'temp → data ---> NULL'.
Application of Stack in Data Structure are as following:
1. Expression Evaluation
2. Expression Conversion
3. Paranthesis Matching (or) Delimiter Checking
4. String reversial
5. Memory Management
6. Backtracking
7. Processing Function Calls
1. Expression Evaluation:
In computer languages, a stack is an extremely efficient data structure for evaluating
arithmetic statements. Operands and operators are the components of an arithmetic
expression.
The arithmetic expression may additionally contain parenthesis such as "left parenthesis"
and "right parenthesis," in addition to operands and operators.
Example: A + (B – C)
Notations for Arithmetic Expression
There are three notations to represent an arithmetic expression:
 Infix Notation
 Prefix Notation
 Postfix Notation
 Operator Precedence

8
UNIT-3:STACKS

Infix Notation:
Each operator is positioned between the operands in an expression written using the infix
notation. Depending on the requirements of the task, infix expressions may be
parenthesized or not.
Example: A + B, (C – D) etc.
Because the operator appears between the operands, all of these expressions are written in
infix notation.
Prefix Notation:
The operator is listed before the operands in the prefix notation. Since the Polish
mathematician invented this system, it is frequently referred to as polish notation.
Example: + A B, -CD etc.
Because the operator occurs before the operands in all of these expressions, prefix notation
is used.
Postfix Notation:
The operator is listed after the operands in postfix notation. Polish notation is simply
reversed in this notation, which is also referred to as Reverse Polish notation.
Example: AB +, CD+, etc.
All these expressions are in postfix notation because the operator comes after the operands.
Evaluation of Arithmetic Expressions:
Evaluation of Arithmetic Expression requires two steps:
1. Put the provided expression first in special notation.
2. In this new notation, evaluate the expression.
The normal precedence rules for arithmetic expressions must be understood in order to
evaluate the expressions. The following are the five fundamental arithmetic operators’
precedence rules:

Operators Associativity Precedence

Highest followed by
^ exponentiation Right to left *Multiplication and
/division

9
UNIT-3:STACKS

Operators Associativity Precedence

Highest followed by +
*Multiplication,
Left to right addition and –
/division
subtraction

+ addition, –
Left to right lowest
subtraction

2. Backtracking
Another use for Stack is backtracking. The optimization problem is solved using a recursive
technique.
3. Delimiter Checking
 Delimiter checking, or parsing, which entails analysing a source program
syntactically, is the most prevalent application of Stack in data structures.
 Additionally known as parenthesis checking. When a source program written in a
programming language, such as C or C++, is translated into machine language, the
compiler separates the program into several components, such as variable names,
keywords, etc.
 by moving left to right while scanning The mismatched delimiters are the main issue
when translating.
 We employ a variety of delimiters, such as the parenthesis checks (,), curly braces (,),
square brackets (,), and the widely used / and / delimiters.
 Each opening delimiter must be followed by a corresponding closing delimiter, i.e.,
each opening parenthesis must be followed by a corresponding closing parenthesis.
Also, the delimiter can be nested.
 The opening delimiter that occurs later in the source program should be closed before
those occurring earlier.
4. Reverse a Data(or)String reversal:
We must reorder the data in such a way that the first and final elements are switched, the
second and second-last elements are switched, and so on for all subsequent elements if we
want to reverse a given collection of data.
Example: If we were to reverse the string Welcome, we would get Emoclew.
There are different reversing applications:
 Reversing a string

10
UNIT-3:STACKS

 Converting Decimal to Binary


Reverse a String
A Stack can be used to reverse a string’s characters. This can be done by simply popping
each character off of the stack one at a time after pushing them onto the stack one at a time.
The initial character of the Stack is at the bottom of the Stack, the last character of the
String is at the top, and due to the Stack’s last in first out property, after performing the pop
operation in the Stack, the Stack returns the String in reverse order.

Converting Decimal to Binary


Although most business programs employ decimal numbers, some scientific and technical
applications need either binary, octal, or hexadecimal numbers. A number can be
transformed from decimal to binary, octal, or hexadecimal using a stack. Any decimal
number can be converted to a binary number by continually dividing it by two and pushing
the residue of each division onto the stack until the result is 0. The binary counterpart of the
provided decimal number is then obtained by popping the entire stack.
Example: Converting 14 number Decimal to Binary:

In the example above, we get seven as a quotient and one as the reminder when we divide
14 by 2, and these two values are pushed onto the stack. When we divide seven by two once
more, we get three as the quotient and one as the reminder, which is once more added to the
11
UNIT-3:STACKS

Stack. The supplied number is lowered in this manner until it does not reach zero. The
comparable binary number 1110 is what we receive after we totally pop off the stack.
5.Memory management:
Memory management in data structures is crucial for efficient use of resources and optimal
performance. Here are some key concepts:
1. Dynamic Memory Allocation: This involves allocating memory during runtime
using functions like malloc, calloc, realloc, and free in languages like C and C++. It
allows for flexible memory usage, enabling the creation of data structures of varying
sizes1.
2. Static memory management refers to the allocation of memory at compile-time,
meaning the size and location of memory are determined before the program runs.
Here are some key points about static memory management:
 Fixed Size: The memory allocated is fixed and cannot be changed during
runtime. This means you need to know the exact memory requirements
beforehand1.
 Compiler-Controlled: Allocation and deallocation of memory are handled by
the compiler, not the programmer1.
 Stack Usage: Static memory allocation typically uses the stack data
structure. Variables are stored in the stack, and their memory is automatically
managed when functions are called and return1.
 Efficiency: Execution is generally faster compared to dynamic memory
allocation because memory is allocated at compile-time.
 Predictability: Since the memory allocation is fixed, it is easier to predict and
manage memory usage2.

6. Processing Function Calls:


In programs that call multiple functions in quick succession, the stack is crucial. Assume we
have a program with three A, B, and C functions. Function A calls Function B, and Function
B calls Function C.

12
UNIT-3:STACKS

Function A’s processing won’t be finished until function B’s execution and return have
been completed. This is because function A contains a call to function B. The same is true
of functions B and C. As a result, we see that function A can only be finished after function
B, and function B can only be finished after function C. As a result, function A should be
begun first and finished last. In conclusion, utilising Stack makes it simple to handle the
function activity described above, which follows the last in first out behaviour.
Consider the addresses of the statements to which control is transferred following the
completion of functions A, B, and C as addrA, addrB, and addrC, respectively.

In the Stack, return addresses are displayed in the order that the functions were called, as
can be seen in the image above. Each function is finished, followed by the pop operation,
which starts execution at the position where the Stack was removed. The stack data
structure may thus handle the program that calls multiple functions one after another in the
best possible way. Each function receives control in the proper location, which is the calling
sequence’s reversal.

TOWERS OF HANNOI:

A skeletal recursive procedure (Outline) for the solution of the problem for N number of
disks is as follows:
1. Move the top N-1 disks from peg A to peg B (using C as an auxiliarypeg)
2. Move the bottom disk from peg A to peg C

13
UNIT-3:STACKS

3. Move N-1 disks from Peg B to Peg C (using Peg A as an auxiliary peg)
The pictorial representation of the skeletal recursive procedure for N=4 disks is shown
in Figure 2.

Algorithm
TOH( n, Sour, Aux , Des)
If(n=1)
Write ("Move Disk “, n ," from ", Sour ," to ",Des)
Else
TOH(n-1,Sour,Des,Aux);
Write ("Move Disk “, n ," from ", Sour ," to ",Des)
TOH(n-1,Aux,Sour,Des);
END
14
UNIT-3:STACKS

Let's take an example to better understand the algorithm (For n=3).

RECURSION:
Recursion, in the context of algorithms, is a powerful programming concept where a
function calls itself during its execution. It involves breaking down a complex problem into
simpler, more manageable sub-problems and solving each of them. This repetitive self-
calling characteristic distinguishes recursive approaches from traditional iterative methods.
Significance of Recursion in Algorithms
Recursion plays a fundamental and pivotal role in the landscape of algorithms, offering a
unique perspective on problem-solving. Its significance lies in the following aspects:
1. Fundamental Role in Problem-Solving
Recursion provides a natural and elegant way to tackle complex problems by expressing
them in terms of simpler sub-problems. This decomposition allows programmers to build
solutions incrementally, starting from the simplest cases and gradually addressing more
intricate scenarios.
2. Versatility in Handling Complex Tasks
Recursion enables algorithms to embrace a "divide-and-conquer" strategy. By breaking
down a problem into smaller components and solving them independently, recursive
solutions often lead to clearer and more maintainable code. This versatility is particularly
advantageous in scenarios where a problem can be naturally divided into smaller, solvable
sub-problems.

15
UNIT-3:STACKS

Types of Recursion
A. Linear Recursion
Linear recursion represents the basic form of recursion, where a function calls itself exactly
once during its execution. This straightforward approach forms the foundation for more
complex recursive strategies.
B. Tail Recursion
Tail recursion is a specific form of recursion where the recursive call is the last operation
performed in the function. This unique characteristic allows for optimization in certain
programming environments.
Recursion vs. Iteration
A. Contrasting Recursion with Iterative Approaches
Understanding recursion necessitates a comparison with its iterative counterpart. Both
recursion and iteration are mechanisms for solving problems, but they have distinct
characteristics. Let's delve into the differences between these two approaches:
1. Examining the Differences Between Recursive and Iterative Solutions
Recursion:
 Function Calls Itself: Recursive solutions involve a function calling itself to solve
smaller instances of a problem.
 Elegant, Concise Code: Recursive code can be more expressive and concise,
particularly for problems naturally suited to divide-and-conquer strategies.
 Stack Usage: Each recursive call adds a frame to the call stack, which can lead to
stack overflow for deeply nested calls.
Iteration:
 Looping Constructs: Iterative solutions use looping constructs
(e.g., for or while loops) to repeatedly execute a set of statements.
 Explicit Control: The flow of control is explicit and follows a linear sequence,
making it easier to trace and understand.
 Memory Usage: Iterative solutions typically use less memory as they don't rely on
the call stack.
2. When to Choose Recursion over Iteration and Vice Versa

16
UNIT-3:STACKS

Choose Recursion When:


 Divide-and-Conquer Structure: The problem naturally lends itself to a divide-and-
conquer approach, where breaking it down into smaller sub-problems simplifies the
solution.
 Readability: Recursive solutions can lead to more readable and intuitive code for
certain problems.
 Tree-Like Structures: Problems involving tree-like structures, such as traversing a
tree or calculating Fibonacci numbers, often have elegant recursive solutions.
Choose Iteration When:
 Sequential Execution: The problem can be solved sequentially without the need for
breaking it down into smaller sub-problems.
 Efficiency: Iterative solutions might be more efficient in terms of memory usage and
execution speed for certain types of problems.
 Tail Recursion Optimization: Some languages and compilers optimize tail
recursion, making certain recursive solutions as efficient as iterative ones.

***********************

17

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy