0% found this document useful (0 votes)
12 views26 pages

ADT in DS

The document explains data structures and abstract data types (ADTs), emphasizing their importance in organizing data efficiently and providing a clear interface for operations without implementation details. It discusses the advantages and disadvantages of ADTs, such as encapsulation, abstraction, and modularity, while also providing examples of ADTs like stacks and their operations. Additionally, it covers the concept of abstraction and encapsulation, illustrating how these principles apply to real-world examples like smartphones and arrays.

Uploaded by

megha210103
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)
12 views26 pages

ADT in DS

The document explains data structures and abstract data types (ADTs), emphasizing their importance in organizing data efficiently and providing a clear interface for operations without implementation details. It discusses the advantages and disadvantages of ADTs, such as encapsulation, abstraction, and modularity, while also providing examples of ADTs like stacks and their operations. Additionally, it covers the concept of abstraction and encapsulation, illustrating how these principles apply to real-world examples like smartphones and arrays.

Uploaded by

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

Abstract data type in data structure

What is data structure?


A data structure is a technique of organizing the data so that the data can be
utilized efficiently. There are two ways of viewing the data structure:
o Mathematical/ Logical/ Abstract models/ Views: The data structure is
the way of organizing the data that requires some protocols or rules.
These rules need to be modeled that come under the logical/abstract
model.
o Implementation: The second part is the implementation part. The rules
must be implemented using some programming language.

Why data structure?


The following are the advantages of using the data structure:
o These are the essential ingredients used for creating fast and powerful
algorithms.
o They help us to manage and organize the data.
o Data structures make the code cleaner and easier to understand.

What is abstract data type?


An abstract data type is an abstraction of a data structure that provides only
the interface to which the data structure must adhere. The interface does not
give any specific details about something should be implemented or in what
programming language.
In other words, we can say that abstract data types are the entities that are
definitions of data and operations but do not have implementation details. In
this case, we know the data that we are storing and the operations that can be
performed on the data, but we don't know about the implementation details.
The reason for not having implementation details is that every programming
language has a different implementation strategy for example; a C data
structure is implemented using structures while a C++ data structure is
implemented using objects and classes.
For example, a List is an abstract data type that is implemented using a
dynamic array and linked list. A queue is implemented using linked list-based
queue, array-based queue, and stack-based queue. A Map is implemented
using Tree map, hash map, or hash table.
Abstract data type model
Before knowing about the abstract data type model, we should know about
abstraction and encapsulation.
Abstraction: It is a technique of hiding the internal details from the user and
only showing the necessary details to the user.
Encapsulation: It is a technique of combining the data and the member function
in a single unit is known as encapsulation.

The above figure shows the ADT model. There are two types of modules in
the ADT model, i.e., the public function and the private function. The ADT
model also contains the data structures that we are using in a program. In this
model, first encapsulation is performed, i.e., all the data is wrapped in a single
unit, i.e., ADT. Then, the abstraction is performed means showing the
operations that can be performed on the data structure and what are the data
structures that we are using in a program.
Let's understand the abstract data type with a real-world example.
If we consider the smartphone. We look at the high specifications of the
smartphone, such as:
o 4 GB RAM
o Snapdragon 2.2ghz processor
o 5 inch LCD screen
o Dual camera
o Android 8.0
The above specifications of the smartphone are the data, and we can also
perform the following operations on the smartphone:
o call(): We can call through the smartphone.
o text(): We can text a message.
o photo(): We can click a photo.
o video(): We can also make a video.
The smartphone is an entity whose data or specifications and operations are
given above. The abstract/logical view and operations are the abstract or
logical views of a smartphone.
The implementation view of the above abstract/logical view is given below:
class Smartphone
{
private:
int ramSize;
string processorName;
float screenSize;
int cameraCount;
string androidVersion;
public:
void call();
void text();
void photo();
void video();
}
The above code is the implementation of the specifications and operations that
can be performed on the smartphone. The implementation view can differ
because the syntax of programming languages is different, but the
abstract/logical view of the data structure would remain the same. Therefore,
we can say that the abstract/logical view is independent of the
implementation view.
Note: We know the operations that can be performed on the predefined data
types such as int, float, char, etc., but we don't know the implementation
details of the data types. Therefore, we can say that the abstract data type is
considered as the hidden box that hides all the internal details of the data type.
Data structure example
Suppose we have an index array of size 4. We have an index location starting
from 0, 1, 2, 3. Array is a data structure where the elements are stored in a
contiguous location. The memory address of the first element is 1000, second
element is 1004, third element is 1008, and the fourth element is 1012. Since it
is of integer type so it will occupy 4 bytes and the difference between the
addresses of each element is 4 bytes. The values stored in an array are 10, 20,
30 and 40. These values, index positions and the memory addresses are the
implementations.
The abstract or logical view of the integer array can be stated as:
o It stores a set of elements of integer type.
o It reads the elements by position, i.e., index.
o It modifies the elements by index
o It performs sorting
The implementation view of the integer array:
1. a[4] = {10, 20, 30, 40}
2. cout<< a[2]
3. a[3] = 50
Features of ADT:
Abstract data types (ADTs) are a way of encapsulating data and operations on
that data into a single unit. Some of the key features of ADTs include:
• Abstraction: The user does not need to know the implementation of the
data structure only essentials are provided.
• Better Conceptualization: ADT gives us a better conceptualization of the
real world.
• Robust: The program is robust and has the ability to catch errors.
• Encapsulation: ADTs hide the internal details of the data and provide a
public interface for users to interact with the data. This allows for easier
maintenance and modification of the data structure.
• Data Abstraction: ADTs provide a level of abstraction from the
implementation details of the data. Users only need to know the
operations that can be performed on the data, not how those operations
are implemented.
• Data Structure Independence: ADTs can be implemented using different
data structures, such as arrays or linked lists, without affecting the
functionality of the ADT.
• Information Hiding: ADTs can protect the integrity of the data by
allowing access only to authorized users and operations. This helps
prevent errors and misuse of the data.
• Modularity: ADTs can be combined with other ADTs to form larger,
more complex data structures. This allows for greater flexibility and
modularity in programming.
Overall, ADTs provide a powerful tool for organizing and manipulating data in
a structured and efficient manner.
Abstract data types (ADTs) have several advantages and disadvantages that
should be considered when deciding to use them in software development.
Here are some of the main advantages and disadvantages of using ADTs:
Advantages:
• Encapsulation: ADTs provide a way to encapsulate data and operations
into a single unit, making it easier to manage and modify the data
structure.
• Abstraction: ADTs allow users to work with data structures without
having to know the implementation details, which can simplify
programming and reduce errors.
• Data Structure Independence: ADTs can be implemented using different
data structures, which can make it easier to adapt to changing needs and
requirements.
• Information Hiding: ADTs can protect the integrity of data by controlling
access and preventing unauthorized modifications.
• Modularity: ADTs can be combined with other ADTs to form more
complex data structures, which can increase flexibility and modularity in
programming.
Disadvantages:
• Overhead: Implementing ADTs can add overhead in terms of memory
and processing, which can affect performance.
• Complexity: ADTs can be complex to implement, especially for large and
complex data structures.
• Learning Curve: Using ADTs requires knowledge of their
implementation and usage, which can take time and effort to learn.
• Limited Flexibility: Some ADTs may be limited in their functionality or
may not be suitable for all types of data structures.
• Cost: Implementing ADTs may require additional resources and
investment, which can increase the cost of development.
Conclusion
Understanding Abstract Data Types (ADTs) and User-Defined Data Types
(UDTs) is crucial for effective data modeling and programming. ADTs provide a
high-level view of data operations and behaviors, promoting abstraction and
encapsulation. UDTs, on the other hand, allow programmers to create custom
data types to implement these abstract concepts. By leveraging both ADTs and
UDTs, developers can design robust and flexible data structures, leading to
more organized and maintainable code. For those interested in deepening their
knowledge, exploring further resources on data structures and programming
paradigms is highly recommended.

STACK as an ADT
The abstract datatype is special kind of datatype, whose behavior is defined by
a set of values and set of operations. The keyword “Abstract” is used as we can
use these datatypes, we can perform different operations. But how those
operations are working that is totally hidden from the user. The ADT is made of
with primitive datatypes, but operation logics are hidden.
Here we will see the stack ADT. These are few operations or functions of the
Stack ADT.
• isFull(), This is used to check whether stack is full or not
• isEmpty(), This is used to check whether stack is empty or not
• push(x), This is used to push x into the stack
• pop(), This is used to delete one element from top of the stack
• peek(), This is used to get the top most element of the stack
• size(), this function is used to get number of elements present into the
stack

Example
#include<iostream>
#include<stack>
using namespace std;
main(){
stack<int> stk;
if(stk.empty()){
cout << "Stack is empty" << endl;
} else {
cout << "Stack is not empty" << endl;
}
//insert elements into stack
stk.push(10);
stk.push(20);
stk.push(30);
stk.push(40);
stk.push(50);
cout << "Size of the stack: " << stk.size() << endl;
//pop and dispay elements
while(!stk.empty()){
int item = stk.top(); // same as peek operation
stk.pop();
cout << item << " ";
}
}
Output
Stack is empty
Size of the stack: 5
50 40 30 20 10

A stack is an abstract data structure that contains a collection of elements. Stack


implements the LIFO mechanism i.e. the element that is pushed at the end is
popped out first. Some of the principal operations in the stack are −
• Push - This adds a data value to the top of the stack.
• Pop - This removes the data value on top of the stack
• Peek - This returns the top data value of the stack
A program that implements a stack using array is given as follows.
Example
#include <iostream>
using namespace std;
int stack[100], n=100, top=-1;
void push(int val) {
if(top>=n-1)
cout<<"Stack Overflow"<<endl;
else {
top++;
stack[top]=val;
}
}
void pop() {
if(top<=-1)
cout<<"Stack Underflow"<<endl;
else {
cout<<"The popped element is "<< stack[top] <<endl;
top--;
}
}
void display() {
if(top>=0) {
cout<<"Stack elements are:";
for(int i=top; i>=0; i--)
cout<<stack[i]<<" ";
cout<<endl;
} else
cout<<"Stack is empty";
}
int main() {
int ch, val;
cout<<"1) Push in stack"<<endl;
cout<<"2) Pop from stack"<<endl;
cout<<"3) Display stack"<<endl;
cout<<"4) Exit"<<endl;
do {
cout<<"Enter choice: "<<endl;
cin>>ch;
switch(ch) {
case 1: {
cout<<"Enter value to be pushed:"<<endl;
cin>>val;
push(val);
break;
}
case 2: {
pop();
break;
}
case 3: {
display();
break;
}
case 4: {
cout<<"Exit"<<endl;
break;
}
default: {
cout<<"Invalid Choice"<<endl;
}
}
}while(ch!=4);
return 0;
}
Output
1) Push in stack
2) Pop from stack
3) Display stack
4) Exit

Enter choice: 1
Enter value to be pushed: 2
Enter choice: 1
Enter value to be pushed: 6
Enter choice: 1
Enter value to be pushed: 8
Enter choice: 1
Enter value to be pushed: 7
Enter choice: 2
The popped element is 7
Enter choice: 3
Stack elements are:8 6 2
Enter choice: 5
Invalid Choice
Enter choice: 4
Exit
In the above program, the push() function takes argument val i.e., value to be
pushed into the stack. If a top is greater than or equal to n, there is no space in a
stack and overflow is printed. Otherwise, val is pushed into the stack. The code
snippet for this is as follows.
void push(int val) {
if(top>=n-1)
cout<<"Stack Overflow"<<endl;
else {
top++;
stack[top]=val;
}
}
The pop() function pops the topmost value of the stack, if there is any value. If
the stack is empty then underflow is printed. This is given as follows.
void pop() {
if(top<=-1)
cout<<"Stack Underflow"<<endl;
else {
cout<<"The popped element is "<< stack[top] <<endl;
top--;
}
}
The display() function displays all the elements in the stack. It uses a for loop to
do so. If there are no elements in the stack, then Stack is empty is printed. This is
given below.
void display() {
if(top>=0) {
cout<<"Stack elements are:";
for(int i=top; i>=0; i--)
cout<<stack[i]<<" ";
cout<<endl;
}else
cout<<"Stack is empty";
}
The function main() provides a choice to the user if they want to push, pop or
display the stack. According to the user response, the appropriate function is
called using switch. If the user enters an invalid response, then that is printed.
The code snippet for this is given below.
int main() {
int ch, val;
cout<<"1) Push in stack"<<endl;
cout<<"2) Pop from stack"<<endl;
cout<<"3) Display stack"<<endl;
cout<<"4) Exit"<<endl;
do {
cout<<"Enter choice: "<<endl;
cin>>ch;
switch(ch) {
case 1: {
cout<<"Enter value to be pushed:"<<endl;
cin>>val;
push(val);
break;
}
case 2: {
pop();
break;
}
case 3: {
display();
break;
}
case 4: {
cout<<"Exit"<<endl;
break;
}
default: {
cout<<"Invalid Choice"<<endl;
}
}
}while(ch!=4);
return 0;
}

Application of Stack
Expression Conversion:
Infix Expressions
Infix expressions are mathematical expressions where the operator is placed
between its operands. This is the most common mathematical notation used by
humans. For example, the expression "2 + 3" is an infix expression, where the
operator "+" is placed between the operands "2" and "3".
Operator precedence rules:
Infix expressions follow operator precedence rules, which determine the order
in which operators are evaluated. For example, multiplication and division have
higher precedence than addition and subtraction. This means that in the
expression "2 + 3 * 4", the multiplication operation will be performed before the
addition operation.
Here's the table summarizing the operator precedence rules for common
mathematical operators:

Operator Precedence

Parentheses () Highest

Exponents ^ High

Multiplication * Medium
Operator Precedence

Division / Medium

Addition + Low

Subtraction - Low

Evaluating Infix Expressions


Evaluating infix expressions requires additional processing to handle the order
of operations and parentheses. First convert the infix expression to postfix
notation. This can be done using a stack or a recursive algorithm. Then evaluate
the postfix expression.
Advantages of Infix Expressions
• More natural and easier to read and understand for humans.
• Widely used and supported by most programming languages and
calculators.
Disadvantages Infix Expressions
• Requires parentheses to specify the order of operations.
• Can be difficult to parse and evaluate efficiently.
Prefix Expressions (Polish Notation)
Prefix expressions are also known as Polish notation, are a mathematical
notation where the operator precedes its operands. This differs from the more
common infix notation, where the operator is placed between its operands.
In prefix notation, the operator is written first, followed by its operands. For
example, the infix expression "a + b" would be written as "+ a b" in prefix
notation.
Evaluating Prefix Expressions
Evaluating prefix expressions can be useful in certain scenarios, such as when
dealing with expressions that have a large number of nested parentheses or
when using a stack-based programming language.
Advantages of Prefix Expressions
• No need for parentheses, as the operator always precedes its operands.
• Easier to parse and evaluate using a stack-based algorithm.
• Can be more efficient in certain situations, such as when dealing with
expressions that have a large number of nested parentheses.
Disadvantages of Prefix Expressions
• Can be difficult to read and understand for humans.
• Not as commonly used as infix notation.
Postfix Expressions (Reverse Polish Notation)
Postfix expressions are also known as Reverse Polish Notation (RPN), are a
mathematical notation where the operator follows its operands. This differs
from the more common infix notation, where the operator is placed between its
operands.
In postfix notation, operands are written first, followed by the operator. For
example, the infix expression "5 + 2" would be written as "5 2 +" in postfix
notation.
Evaluating Postfix Expressions (Reverse Polish Notation)
Evaluating postfix expressions can be useful in certain scenarios, such as when
dealing with expressions that have a large number of nested parentheses or
when using a stack-based programming language.
Advantages of Postfix Notation
• Also eliminates the need for parentheses.
• Easier to read and understand for humans.
• More commonly used than prefix notation.
Disadvantages of Postfix Expressions
• Requires a stack-based algorithm for evaluation.
• Can be less efficient than prefix notation in certain situations.
Comparison of Infix, Prefix and Postfix Expressions
Let's compare infix, prefix, and postfix notations across various criteria:

Postfix Notation
Prefix Notation (Reverse Polish
Infix Notation (Polish Notation) Notation)

Less human- Less human-


Human-readable readable, requires readable, requires
Readability familiarity familiarity
Postfix Notation
Prefix Notation (Reverse Polish
Infix Notation (Polish Notation) Notation)

Operator Between
Before operands After operands
Placement operands

Parentheses
Often required Not required Not required
Requirement

Required,
Not required, Not required,
Operator parentheses
operators have operators have
Precedence determine
fixed precedence fixed precedence
Tracking precedence

Evaluation
Left-to-right Right-to-left Left-to-right
Method

May require Ambiguity rare, Ambiguity rare,


Ambiguity explicit use of straightforward straightforward
Handling parentheses evaluation evaluation

Unary Simplified handling Simplified handling


Requires careful
Operator due to explicit due to explicit
placement
Handling notation notation

Less efficient due


More efficient due More efficient due
to precedence
to fixed precedence to fixed precedence
tracking and
and absence of and absence of
Computer parentheses
parentheses parentheses
Efficiency handling

Common in
Common in Common in
everyday
computer science computer science
arithmetic and
and programming and programming
mathematical
languages languages
Usage notation
Infix to Prefix, Postfix
Prefix and Postfix to Infix
1.infix to prefix
Steps required for infix to prefix conversion using stack in C++
• Initially reverse the expression given for the infix.
• One by one scan of characters.
• Is if character is an operand, then copy it to the output of the prefix
notation.
• When the character is a parenthesis closing then push it to the stack.
• If the character is an opening parenthesis, pop the elements in the stack
till we find the closing parenthesis that corresponds.
• If a character scanned is operator.
• If the operator has greater or equal precedence than the top of the stack,
push the operator to the stack.
• If the operator has less precedence than the top of the stack, pop the
operator and output it to the output of the prefix notation, then check the
above condition with the new top of the stack again.
• After scanning all the characters, reverse the notation output for the
prefix.
C++ Program for Infix To Prefix Conversion using stack data structures: -
#include <bits/stdc++.h> /*Reduces time wastage, Eliminates the need to
write specific header files, Minimizes include statements, and Increases
readability and understandability*/
using namespace std;
//definition of functions
struct Stack *create (int max);
int stackFull (struct Stack *stack);
int stackEmpty (struct Stack *stack);
void pushElement (struct Stack *stack, int item);
int popElement (struct Stack *stack);
int peekElement (struct Stack *stack);
int checkOperand (char ch);
int precedence (char ch);
int postfix (char *expression);
void reverse (char *exp);
void brackets (char *exp);
void conversionInfixToPrefix (char *exp);
// A structure to represent a stack
struct Stack
{
int top;
int maxSize;
int *array;
};
int main ()
{
int n = 10;
cout << "The infix expression is: \n";
char expression[] = "(P+(Q*R)/(S-T))";
cout << expression << "\n";
conversionInfixToPrefix (expression);
cout << "The prefix expression is: \n";
cout << expression;
return 0;
}
//stack implementation
struct Stack * create (int max)
{
struct Stack *stack = (struct Stack *) malloc (sizeof (struct Stack));
stack->maxSize = max;
stack->top = -1;
stack->array = (int *) malloc (stack->maxSize * sizeof (int));
return stack;
}

// Checking with this function is stack is full or not


int stackFull (struct Stack *stack)
{
if (stack->top == stack->maxSize - 1)
{
cout << "Will not be able to push maxSize reached\n";
}
// We know array index from 0 and maxSize starts from 1
return stack->top == stack->maxSize - 1;
}

// if Stack is empty when top is equal to -1 and return true


int stackEmpty (struct Stack *stack)
{
return stack->top == -1;
}

// Push function it inserts value in stack and increments stack top by 1


void pushElement (struct Stack *stack, int item)
{
if (stackFull (stack))
return;
stack->array[++stack->top] = item;
}

// pop Function it remove an item from stack and decreases top by 1


int popElement (struct Stack *stack)
{
if (stackEmpty (stack))
return INT_MIN;
return stack->array[stack->top--];
}

// Function to return the top from stack without removing it


int peekElement (struct Stack *stack)
{
if (stackEmpty (stack))
return INT_MIN;
return stack->array[stack->top];
}

// A function check the given character is operand


int checkOperand (char ch)
{
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
}

// Fucntion to compare precedence if return larger value means higher


precedence
int precedence (char ch)
{
switch (ch)
{
case '+':
case '-':
return 1;

case '*':
case '/':
return 2;

case '^':
return 3;
}
return -1;
}

// The function for infix to postfix conversion


int postfix (char *expression)
{
int i, j;
struct Stack *stack = create (strlen (expression));
if (!stack)
return -1;

for (i = 0, j = -1; expression[i]; ++i)


{
// checking the character we scanned is operand or not
if (checkOperand (expression[i]))
expression[++j] = expression[i];

// if we scan character push it to the stack


else if (expression[i] == '(')
pushElement (stack, expression[i]);

//if we scan character we need to pop and print from the stack
else if (expression[i] == ')')
{
while (!stackEmpty (stack) && peekElement (stack) != '(')
expression[++j] = popElement (stack);
if (!stackEmpty (stack) && peekElement (stack) != '(')
return -1; // invalid expression
else
popElement (stack);
}
else // if an operator
{
while (!stackEmpty (stack)
&& precedence (expression[i]) <=
precedence (peekElement (stack)))
expression[++j] = popElement (stack);
pushElement (stack, expression[i]);
}

// if all first expression characters are scanned


// adding all left elements from stack to expression
while (!stackEmpty (stack))
expression[++j] = popElement (stack);
expression[++j] = '\0';

return 0;
}

void reverse (char *exp)


{ //reverse function for expression
int size = strlen (exp);
int j = size, i = 0;
char temp[size];

temp[j--] = '\0';
while (exp[i] != '\0')
{
temp[j] = exp[i];
j--;
i++;
}
strcpy (exp, temp);
}

void brackets (char *exp)


{
int i = 0;
while (exp[i] != '\0')
{
if (exp[i] == '(')
exp[i] = ')';
else if (exp[i] == ')')
exp[i] = '(';
i++;
}
}

void conversionInfixToPrefix (char *exp)


{
int size = strlen (exp);
reverse (exp);
brackets (exp);
postfix (exp);
reverse (exp);
}
Output:-
The infix expression is:
(P+(Q*R)/(S-T))
The prefix expression is:
+P/*QR-ST
2. Prefix to infix conversion:
In this problem, we are given a prefix expression. Our task is to print the infix
conversion of the given expression.
Prefix expression is those expressions which have operators before the
operands.
Example: +AB.
Infix expressions are those expressions which have operators between the
operands.
Example: A+B
Infix expression are information for human understanding, but the computer
does computations on prefix or postfix expressions (generally postfix).
Let’s take an example to understand the problem
Input: prefix : /+LM/NX
Output: infix : (L+M) / (N/X)
To solve this problem, we will be using the stack data structure. We will
traverse the prefix expression in reverse order of the expression. And for each
element of the expression check for these cases.
If element is operand -> push(element) in stack.
If element is operator -> 2Xpop(topofstack) and push in order as a string =
operand - operator - operand.
Finally, after traversal, the top of the stack will contain a string which is the infix
conversion, print it.
Program to show the implementation of our solution
Example
Live Demo
#include <iostream>
#include <stack>
using namespace std;
bool isOperator(char element) {
switch (element) {
case '+':
case '-':
case '/':
case '*':
return true;
}
return false;
}
string convertToInfix(string prefix) {
stack<string> expression;
int length = prefix.size();
for (int i = length - 1; i >= 0; i--) {
if (isOperator(prefix[i])) {
string op1 = expression.top();
expression.pop();
string op2 = expression.top();
expression.pop();
string temp = "{"+op1+prefix[i]+op2+"}";
expression.push(temp);
} else {
expression.push(string(1, prefix[i]));
}
}
return expression.top();
}
int main() {
string prefix = "*-AB/+CD*XY";
cout<<"Prefix expression : "<<prefix<<endl;
cout<<"Infix expression : " <<convertToInfix(prefix);
return 0;
}
Output
Prefix expression : *-AB/+CD*XY
Infix expression : {{A-B}*{{C+D}/{X*Y}}}

You might also like