Data Structures Unit 1
Data Structures Unit 1
Data Structures
R18 CSE/IT II year
By
D. Subhashini
Associate Professor
CSE dept
Aurora’s Technological and Research Institute Department of
Computer Science and Engineering
UNIT I
Introduction to Data Structures, abstract data
types, Linear list – singly linked list implementation,
insertion, deletion and searching operations on linear
list, Stacks-Operations, array and linked
representations of stacks, stack applications,
Queues-operations, array and linked representations.
Data Structures
Data Structure is a way to store and organize data so that it can be used efficiently.
The data structure is not any programming language like C, C++, java, etc. It is a set of
algorithms that we can use in any programming language to structure the data in the
memory.
To structure the data in memory, 'n' number of algorithms were proposed, and all these
algorithms are known as Abstract data types. These abstract data types are the set of rules.
The primitive data structures are primitive data types. The int, char, float, double, and
pointer are the primitive data structures that can hold a single value.
The arrangement of data in a sequential manner is known as a linear data structure. The
data structures used for this purpose are Arrays, Linked list, Stacks, and Queues. In
these data structures, one element is connected to only one another element in a linear
form.
When one element is connected to the 'n' number of elements known as a non-linear
data structure. The best example is trees and graphs. In this case, the elements are
arranged in a random manner.
o Static data structure: It is a type of data structure where the size is allocated at
the compile time. Therefore, the maximum size is fixed.
o Dynamic data structure: It is a type of data structure where the size is allocated at
the run time. Therefore, the maximum size is flexible.
Major Operations
The major or the common operations that can be performed on the data structures are:
An ADT tells what is to be done and data structure tells how it is to be done. In other
words, we can say that ADT gives us the blueprint while data structure provides the
implementation part.
As the different data structures can be implemented in a particular ADT, but the different
implementations are compared for time and space. For example, the Stack ADT can be
implemented by both Arrays and linked list. Suppose the array is providing time efficiency
while the linked list is providing space efficiency, so the one which is the best suited for the
current user's requirements will be selected.
Linear Data Structures: A data structure is called linear if all of its elements are arranged
in the linear order. In linear data structures, each element has the successors and
predecessors except the first and last element.
Arrays: An array is a collection of similar type of data items and each data item is called an
element of the array. The data type of the element may be any valid data type like char,
int, float or double.
The elements of array share the same variable name but each one carries a different index
number known as subscript. The array can be one dimensional, two dimensional or
multidimensional.
Linked List: Linked list is a linear data structure which is used to maintain a list in the
memory. It can be seen as the collection of nodes stored at non-contiguous memory
locations. Each node of the list contains a pointer to its adjacent node.
Stack: Stack is a linear list in which insertion and deletions are allowed only at one end,
called top.
A stack is an abstract data type (ADT), can be implemented in most of the programming
languages. It is named as stack because it behaves like a real-world stack, for example: -
piles of plates or deck of cards etc.
Queue: Queue is a linear list in which elements can be inserted only at one end
called rear and deleted only at the other end called front.
It is an abstract data structure, similar to stack. Queue is opened at both end therefore it
follows First-In-First-Out (FIFO) methodology for storing the data items.
Non Linear Data Structures: This data structure does not form a sequence i.e. each item
or element is connected with two or more other items in a non-linear arrangement. Types of
Non Linear Data Structures are given below:
Trees: Trees are multilevel data structures with a hierarchical relationship among its
elements known as nodes. The bottommost nodes in the herierchy are called leaf
node while the topmost node is called root node. Each node contains pointers to point
adjacent nodes.
Tree data structure is based on the parent-child relationship among the nodes. Each node in
the tree can have more than one children except the leaf nodes whereas each node can
have at most one parent except the root node.
Graphs: Graphs can be defined as the pictorial representation of the set of elements
(represented by vertices) connected by the links known as edges. A graph is different from
tree in the sense that a graph can have cycle while the tree cannot have the one.
2) Insertion: Insertion can be defined as the process of adding the elements to the data
structure at any location.
3) Deletion: The process of removing an element from the data structure is called Deletion.
We can delete an element from the data structure at any random location.
If we try to delete an element from an empty data structure then underflow occurs.
4) Searching: The process of finding the location of an element within the data structure is
called Searching. There are two algorithms to perform searching, Linear Search and Binary
Search.
5) Sorting: The process of arranging the data structure in a specific order is known as
Sorting. There are many algorithms that can be used to perform sorting, for example,
insertion sort, selection sort, bubble sort, etc.
6) Merging: When two lists List A and List B of size M and N respectively, of similar type of
elements, clubbed or joined to produce the third list, List C of size (M+N), then this process
is called merging
Pointer
Pointer is used to points the address of the value stored anywhere in the computer memory.
To obtain the value stored at the location is known as dereferencing the pointer. Pointer
improves the performance for repetitive process such as:
o Traversing String
o Lookup Tables
o Control Tables
o Tree Structures
Pointer Details
o Pointer arithmetic: There are four arithmetic operators that can be used in
pointers: ++, --, +, -
o Array of pointers: You can define arrays to hold a number of pointers.
o Pointer to pointer: C allows you to have pointer on a pointer and so on.
o Passing pointers to functions in C: Passing an argument by reference or by
address enable the passed argument to be changed in the calling function by the
called function.
o Return pointer from functions in C: C allows a function to return a pointer to the
local variable, static variable and dynamically allocated memory as well.
Program
Pointer
#include <stdio.h>
int main( )
{
int a = 5;
int *b;
b = &a;
address of a = -
1284473004
address of b = 3010494296
Structure
A structure is a composite data type that defines a grouped list of variables that are to be
placed under one name in a block of memory. It allows different variables to be accessed by
using a single pointer to the structure.
Syntax
struct structure_name
{
data_type member1;
data_type member2;
.
.
data_type memeber;
};
Advantages
o It can hold variables of different data types.
o We can create objects containing different types of attributes.
o It allows us to re-use the data layout across programs.
o It is used to implement other data structures like linked lists, stacks, queues, trees,
graphs etc.
Program
#include<stdio.h>
#include<conio.h>
void main( )
{
struct employee
{
int id ;
float salary ;
int mobile ;
};
struct employee e1,e2,e3 ;
clrscr();
printf ("\nEnter ids, salary & mobile no. of 3 employee\n"
scanf ("%d %f %d", &e1.id, &e1.salary, &e1.mobile);
Linked List
If we want to store the value in a memory, we need a memory manager that manages the
memory for every variable. For example, if we want to create a variable of integer type like:
int x;
In the above example, we have created a variable 'x' of type integer. As we know that
integer variable occupies 4 bytes, so 'x' variable will occupy 4 bytes to store the value.
int x[3];
In the above example, we have declared an array of size 3. As we know, that all the values
of an array are stored in a continuous manner, so all the three values of an array are stored
in a sequential fashion. The total memory space occupied by the array would be 3*4 = 12
bytes.
o We cannot insert more than 3 elements in the above example because only 3 spaces
are allocated for 3 elements.
o In the case of an array, lots of wastage of memory can occur. For example, if we
declare an array of 50 size but we insert only 10 elements in an array. So, in this
case, the memory space for other 40 elements will get wasted and cannot be used
by another variable as this whole space is occupied by an array.
In array, we are providing the fixed-size at the compile-time, due to which wastage of
memory occurs. The solution to this problem is to use the linked list.
Suppose a programmer made a request for storing the integer value then size of 4-byte
memory block is assigned to the integer value. The programmer made another request for
storing 3 more integer elements; then, three different memory blocks are assigned to these
three elements but the memory blocks are available in a random location. So, how are the
elements connected?.
These elements are linked to each other by providing one additional information along with
an element, i.e., the address of the next element. The variable that stores the address of
the next element is known as a pointer. Therefore, we can say that the linked list contains
two parts, i.e., the first one is the data element, and the other is the pointer. The pointer
variable will occupy 4 bytes which is pointing to the next element.
A linked list can also be defined as the collection of the nodes in which one node is
connected to another node, and node consists of two parts, i.e., one is the data
part and the second one is the address part, as shown in the below figure:
In the above figure, we can observe that each node contains the data and the address of
the next node. The last node of the linked list contains the NULL value in the address part.
o Memory usage
The node in a linked list occupies more memory than array as each node occupies
two types of variables, i.e., one is a simple variable, and another is a pointer variable
that occupies 4 bytes in the memory.
o Traversal
In a linked list, the traversal is not easy. If we want to access the element in a linked
list, we cannot access the element randomly, but in the case of an array, we can
randomly access the element by index. For example, if we want to access the
3rd node, then we need to traverse all the nodes before it. So, the time required to
access a particular node is large.
o Reverse traversing
In a linked list, backtracking or reverse traversing is difficult. In a doubly linked list,
it is easier but requires more memory to store the back pointer.
o With the help of a linked list, the polynomials can be represented as well as we can
perform the operations on the polynomial. We know that polynomial is a collection of
terms in which each term contains coefficient and power. The coefficients and power
of each term are stored as node and link pointer points to the next element in a
linked list, so linked list can be used to create, delete and display the polynomial.
o A sparse matrix is used in scientific computation and numerical analysis. So, a linked
list is used to represent the sparse matrix.
o The various operations like student's details, employee's details or product details
can be implemented using the linked list as the linked list uses the structure data
type that can hold different data types.
o Stack, Queue, tree and various other data structures can be implemented using a
linked list.
o The graph is a collection of edges and vertices, and the graph can be represented as
an adjacency matrix and adjacency list. If we want to represent the graph as an
adjacency matrix, then it can be implemented as an array. If we want to represent
the graph as an adjacency list, then it can be implemented as a linked list.
o To implement hashing, we require hash tables. The hash table contains entries that
are implemented using linked list.
o A linked list can be used to implement dynamic memory allocation. The dynamic
memory allocation is the memory allocation done at the run-time.
Suppose we have three nodes, and the addresses of these three nodes are 100, 200 and
300 respectively. The representation of three nodes as a linked list is shown in the below
figure:
We can observe in the above figure that there are three different nodes having address 100,
200 and 300 respectively. The first node contains the address of the next node, i.e., 200,
the second node contains the address of the last node, i.e., 300, and the third node
contains the NULL value in its address part as it does not point to any node. The pointer
that holds the address of the initial node is known as a head pointer.
The linked list, which is shown in the above diagram, is known as a singly linked list as it
contains only a single link. In this list, only forward traversal is possible; we cannot traverse
in the backward direction as it has only one link in the list.
struct node
{
int data;
struct node *next;
}
Suppose we have three nodes, and the address of these nodes are 100, 200 and 300,
respectively. The representation of these nodes in a doubly-linked list is shown below:
As we can observe in the above figure, the node in a doubly-linked list has two address
parts; one part stores the address of the next while the other part of the node stores
the previous node's address. The initial node in the doubly linked list has the NULL value
in the address part, which provides the address of the previous node.
struct node
{
int data;
struct node *next;
struct node *prev;
}
In the above representation, we have defined a user-defined structure named a node with
three members, one is data of integer type, and the other two are the pointers, i.e., next
and prev of the node type. The next pointer variable holds the address of the next node,
and the prev pointer holds the address of the previous node. The type of both the
pointers, i.e., next and prev is struct node as both the pointers are storing the address of
the node of the struct node type.
struct node
{
int data;
struct node *next;
}
A circular linked list is a sequence of elements in which each node has a link to the next
node, and the last node is having a link to the first node. The representation of the circular
linked list will be similar to the singly linked list, as shown below:
The above figure shows the representation of the doubly circular linked list in which the last
node is attached to the first node and thus creates a circle. It is a doubly linked list also
because each node holds the address of the previous node also. The main difference
between the doubly linked list and doubly circular linked list is that the doubly circular linked
list does not contain the NULL value in the previous field of the node. As the doubly circular
linked contains three parts, i.e., two address parts and one data part so its representation is
similar to the doubly linked list.
struct node
{
int data;
struct node *next;
struct node *prev;
}
One way chain or singly linked list can be traversed only in one direction. In other words,
we can say that each node contains only next pointer, therefore we cannot traverse the list
in the reverse direction.
Consider an example where the marks obtained by the student in three subjects are stored
in a linked list as shown in the figure.
In the above figure, the arrow represents the links. The data part of every node contains
the marks obtained by the student in the different subject. The last node in the list is
identified by the null pointer which is present in the address part of the last node. We can
have as many elements we require, in the data part of the list.
Node Creation
struct node
{
int data;
struct node *next;
};
Insertion
The insertion into a singly linked list can be performed at different positions. Based on the
position of the new node being inserted, the insertion is categorized into the following
categories.
SN Operation Description
1 Insertion It involves inserting any element at the front of the list. We just
at need to a few link adjustments to make the new node as the
beginning head of the list.
2 Insertion It involves insertion at the last of the linked list. The new node
at end of can be inserted as the only node in the list or it can be inserted
the list as the last one. Different logics are implemented in each
scenario.
3 Insertion It involves insertion after the specified node of the linked list.
after We need to skip the desired number of nodes in order to reach
specified the node after which the new node will be inserted. .
node
o Allocate the space for the new node and store data into the data part of the node.
This will be done by the following statements.
o Make the link part of the new node pointing to the existing first node of the list. This
will be done by using the following statement.
ptr->next = head;
o At the last, we need to make the new node as the first node of the list this will be
done by using the following statement.
head = ptr;
C Function
#include<stdio.h>
#include<stdlib.h>
void insert_first(int);
struct node
{
int data;
struct node *next;
};
struct node *head;
void main ()
{
int choice,item;
do
{
ptr->data = item;
ptr -> next = NULL;
o Since, ptr is the only node that will be inserted in the list hence, we need to make
this node pointed by the head pointer of the list. This will be done by using the
following Statements.
head = ptr
temp = head
o Then, traverse through the entire linked list using the statements:
o At the end of the loop, the temp will be pointing to the last node of the list. Now,
allocate the space for the new node, and assign the item to its data part. Since, the
new node is going to be the last node of the list hence, the next part of this node
needs to be pointing to the null. We need to make the next part of the temp node
(which is currently the last node of the list) point to the new node (ptr) .
temp = head;
while (temp -> next != NULL)
{
temp = temp -> next;
}
temp->next = ptr;
ptr->next = NULL;
C Function
#include<stdio.h>
#include<stdlib.h>
void insert_last(int);
struct node
{
int data;
struct node *next;
};
struct node *head;
void main ()
{
int choice,item;
do
{
printf("\nEnter the item which you want to insert?\n");
scanf("%d",&item);
insert_last(item);
printf("\nPress 0 to insert more ?\n");
scanf("%d",&choice);
}while(choice == 0);
}
Void insert_last(int item)
{
struct node *ptr = (struct node*)malloc(sizeof(struct node));
struct node *temp;
if(ptr == NULL)
{
printf("\nOVERFLOW");
}
else
{
ptr->data = item;
if(head == NULL)
{
ptr -> next = NULL;
head = ptr;
printf("\nNode inserted");
}
else
{
temp = head;
while (temp -> next != NULL)
{
temp = temp -> next;
}
temp->next = ptr;
ptr->next = NULL;
printf("\nNode inserted");
}
}
}
temp=head;
for(i=0;i<loc;i++)
{
temp = temp->next;
if(temp == NULL)
{
return;
}
}
o Allocate the space for the new node and add the item to the data part of it. This will
be done by using the following statements.
o now, we just need to make the next part of the temp, point to the new node ptr. This
will insert the new node ptr, at the specified position.
C Function
#include<stdio.h>
#include<stdlib.h>
void insertatpos(int);
void create(int);
struct node
{
int data;
struct node *next;
};
struct node *head;
void main ()
{
int choice,item,loc;
do
{
printf("\nEnter the item which you want to insert?\n");
scanf("%d",&item);
if(head == NULL)
{
create(item);
}
else
{
insertatpos(item);
}
printf("\nPress 0 to insert more ?\n");
scanf("%d",&choice);
}while(choice == 0);
}
void create(int item)
{
{
ptr->data = item;
ptr->next = head;
head = ptr;
printf("\nNode inserted\n");
}
}
void insertatpos(int item)
{
struct node *ptr = (struct node *) malloc (sizeof(struct node));
struct node *temp;
int i,loc;
if(ptr == NULL)
{
printf("\nOVERFLOW");
}
else
{
}
ptr ->next = temp ->next;
temp ->next = ptr;
printf("\nNode inserted");
}
printf("\ncan't insert\n");
return;
}
}
ptr ->next = temp ->next;
temp ->next = ptr;
printf("\nNode inserted");
}
}
}
Deletion
The Deletion of a node from a singly linked list can be performed at different positions.
Based on the position of the node being deleted, the operation is categorized into the
following categories.
SN Operation Description
1 Deletion It involves deletion of a node from the beginning of the list. This
at is the simplest operation among all. It just need a few
beginning adjustments in the node pointers.
2 Deletion It involves deleting the last node of the list. The list can either
at the end be empty or full. Different logic is implemented for the different
of the list scenarios.
3 Deletion It involves deleting the node after the specified node in the list.
after we need to skip the desired number of nodes to reach the node
specified after which the node will be deleted. This requires traversing
node through the list.
therefore, we just need to make the head, point to the next of the head. This will be done
by using the following statements.
ptr = head;
head = ptr->next;
Now, free the pointer ptr which was pointing to the head node of the list. This will be done
by using the following statement.
free(ptr)
1. There is only one node in the list and that needs to be deleted.
2. There are more than one node in the list and the last node of the list will be deleted.
ptr = head
head = NULL
free(ptr)
For this purpose, just declare a temporary pointer temp and assign it to head of the list. We
also need to keep track of the second last node of the list. For this purpose, two pointers ptr
and ptr1 will be used where ptr will point to the last node and ptr1 will point to the second
last node of the list.
ptr = head;
while(ptr->next != NULL)
{
ptr1 = ptr;
ptr = ptr ->next;
}
Now, we just need to make the pointer ptr1 point to the NULL and the last node of the list
that is pointed by ptr will become free. It will be done by using the following statements.
ptr1->next = NULL;
free(ptr);
ptr=head;
for(i=0;i<loc;i++)
{
ptr1 = ptr;
ptr = ptr->next;
if(ptr == NULL)
{
printf("\nThere are less than %d elements in the list..",loc);
return;
}
}
Now, our task is almost done, we just need to make a few pointer adjustments. Make the
next of ptr1 (points to the specified node) point to the next of ptr (the node which is to be
deleted).This will be done by using the following statements.
C function
void search()
{
int pos=0,element;
printf("\nEnter Elent to be searched:");
scanf("%d",&element);
struct node *temp;
temp = head;
while ((temp != NULL) && (temp->data != element))
{
pos++;
temp = temp->next;
}
if (temp == NULL)
printf("\nUnsuccessful Search");
else
printf ("\nElement found at %d node",pos+1);
}
#include<stdio.h>
#include<stdlib.h>
void insertatpos();
void create();
void display();
void deletenode();
void search();
struct node
{
int data;
struct node *next;
};
struct node *head;
void main ()
{
int choice;
head = NULL;
do
{
printf("\nMENU\n");
printf("1. Create\n2. Insert\n3. Delete\n4.Display\n5. Search\n6. Exit");
printf("\nenter your choice");
scanf("%d",&choice);
switch (choice)
{
case 1:
if(head == NULL)
{
create();
}
else
printf ("List already created");
break;
case 2:
insertatpos();
break;
case 3: deletenode();
break;
case 4: printf("\nContents of List are \n");
display();
break;
case 5: search();
break;
case 6: exit(0);
}
}while(1);
}
void create()
{
int item;
struct node *ptr = (struct node *)malloc(sizeof(struct node *));
if(ptr == NULL)
{
printf("\nOVERFLOW\n");
}
else
{
printf("\nEnter the element\n");
scanf("%d",&item);
ptr->data = item;
ptr->next = head;
head = ptr;
printf("\nList Created\n");
}
}
void insertatpos()
{
int item;
struct node *ptr = (struct node *) malloc (sizeof(struct node));
struct node *temp;
int i,loc;
if(ptr == NULL)
{
printf("\nOVERFLOW");
}
else
{
printf("\nEnter the element to insert\n");
scanf("%d",&item);
printf("Enter the location after which you want to insert");
scanf("%d",&loc);
ptr->data = item;
if (loc == 0)
{
ptr->next = head;
head = ptr;
printf("\n Inserted");
}
else
{
temp=head;
for(i=1;i<loc;i++)
{
temp = temp->next;
if(temp == NULL)
{
printf("\ncan't insert\n");
return;
}
}
}
void deletenode()
{
int loc,i;
struct node *ptr,*ptr1;
printf("Enter the location of the node you want to delete");
scanf("%d",&loc);
ptr = head;
if (loc == 1)
{
head = head->next;
free(ptr);
}
else
{
for(i=1;i<loc;i++)
{
ptr1 = ptr;
ptr = ptr->next;
if(ptr == NULL)
{
printf("\nthe node to be deleted is not existing");
return;
}
}
ptr1->next = ptr->next;
free(ptr);
}
printf("\n Deleted");
}
void display()
{
struct node *temp;
temp = head;
while (temp != NULL)
{
printf (" -> %d",temp->data);
temp = temp->next;
}
}
void search()
{
int pos=0,element;
printf("\nEnter Elent to be searched:");
scanf("%d",&element);
struct node *temp;
temp = head;
while ((temp != NULL) && (temp->data != element))
{
pos++;
temp = temp->next;
}
if (temp == NULL)
printf("\nUnsuccessful Search");
else
printf ("\nElement found at %d node",pos+1);
}
Stacks
A Stack is a linear data structure that follows the LIFO (Last-In-First-Out) principle.
Stack has one end, whereas the Queue has two ends (front and rear). Stack contains only
one pointer top pointer pointing to the topmost element of the stack. Whenever an
element is added in the stack, it is added on the top of the stack, and the element can be
deleted only from the top of the stack. In other words, a stack can be defined as a
container in which insertion and deletion can be done from the one end known as
the top of the stack.
Working of Stack
Stack works on the LIFO pattern. As we can observe in the below figure there are five
memory blocks in the stack; therefore, the size of the stack is 5.
Suppose we want to store the elements in a stack and let's assume that stack is empty. We
have taken the stack of size 5 as shown below in which we are pushing the elements one by
one until the stack becomes full.
Since our stack is full as the size of the stack is 5. In the above cases, we can observe that
it grows from the top when we were entering the new element in the stack.
When we perform the delete operation on the stack, it follows the LIFO pattern, which
means that the value entered last will be removed first. In the above case, the value 5 is
entered last, so it will be removed first.
PUSH operation
The steps involved in the PUSH operation is given below:
POP operation
The steps involved in the POP operation is given below:
o Before deleting the element from the stack, we check whether the stack is empty.
o If we try to delete the element from the empty stack, then the underflow condition
occurs.
o If the stack is not empty, we first access the element which is pointed by the top
o Once the pop operation is performed, the top is decremented by 1, i.e., top=top-1.
Applications of Stack
The following are the applications of the stack:
o Balancing of block symbols: Stack is used for balancing a symbol. For example,
we have the following program:
int main()
{
cout<<"Hello";
cout<<"Students";
}
As we know, each program has an opening and closing braces; when the opening braces
come, we push the braces in a stack, and when the closing braces appear, we pop the
opening braces from the stack. Therefore, the net value comes out to be zero. If any
symbol is left in the stack, it means that some syntax problem occurs in a program.
o String reversal: Stack is also used for reversing a string. We can achieve this with
the help of a stack.
First, we push all the characters of the string in a stack until we reach the null
character.
After pushing all the characters, we start taking out the character one by one until
we reach the bottom of the stack.
o Recursion: The recursion means that the function is calling itself again. To maintain
the previous states, the compiler creates a system stack in which all the previous
records of the function are maintained.
o DFS(Depth First Search): This search is implemented on a Graph, and Graph uses
the stack data structure.
o Backtracking: Suppose we have to create a path to solve a maze problem. If we
are moving in a particular path, and we realize that we come on the wrong way. In
order to come at the beginning of the path to create a new path, we have to use the
stack data structure.
o Expression conversion: Stack can also be used for expression conversion. This is
one of the most important applications of stack. The list of the expression conversion
is given below:
o Infix to prefix
o Infix to postfix
o Prefix to infix
o Prefix to postfix etc
o Memory management: The stack manages the memory. The memory is assigned
in the contiguous memory blocks. The memory is known as stack memory as all the
variables are assigned in a function call stack memory. The memory size assigned to
the program is known to the compiler. When the function is created, all its variables
are assigned in the stack memory. When the function completed its execution, all the
variables assigned in the stack are released.
1. Increment the variable Top so that it can now refere to the next memory location.
2. Add element at the position of incremented top. This is referred to as adding new
element at the top of the stack.
Stack is overflown when we try to insert an element into a completely filled stack therefore,
our main function must always avoid stack overflow condition.
Algorithm:
begin
if top = n then stack full
top = top + 1
stack (top) : = item;
end
The underflow condition occurs when we try to delete an element from an already empty
stack.
Algorithm :
begin
if top = 0 then stack empty;
item := stack(top);
top = top - 1;
end;
#include <stdio.h>
int stack[100],n, top=-1;
void push();
void pop();
void display();
void main ()
{
int choice;
printf("Enter the number of elements in the stack ");
scanf("%d",&n);
printf("*********Stack operations using array*********");
printf("\n----------------------------------------------\n");
do
{
printf("Chose one from the below options...\n");
printf("\n1.Push\n2.Pop\n3.Show\n4.Exit");
printf("\n Enter your choice \n");
scanf("%d",&choice);
switch(choice)
{
case 1: push();
break;
case 2: pop();
break;
case 3:display();
break;
case 4:printf("Exiting....");
exit(0);
break;
default: printf("Please Enter valid choice ");
}
} while(1);
}
void push ()
{
int val;
if (top == n )
printf("\n Overflow");
else
{
printf("Enter the value?");
scanf("%d",&val);
top = top +1;
stack[top] = val;
}
}
void pop ()
{
if(top == -1)
printf("Underflow");
else
top = top -1;
}
void show()
{
for (i=top;i>=0;i--)
{
printf("%d\n",stack[i]);
}
if(top == -1)
{
printf("Stack is empty");
}
}
In linked list implementation of stack, the nodes are maintained non-contiguously in the
memory. Each node contains a pointer to its immediate successor node in the stack.
Stack is said to be overflown if the space left in the memory heap is not enough to create
a node.
C implementation :
void push ()
{
int val;
struct node *ptr =(struct node*)malloc(sizeof(struct node));
if(ptr == NULL)
{
printf("not able to push the element");
}
else
{
printf("Enter the value");
scanf("%d",&val);
if(head==NULL)
{
ptr->val = val;
ptr -> next = NULL;
head=ptr;
}
else
{
ptr->val = val;
ptr->next = head;
head=ptr;
}
printf("Item pushed");
}
}
C implementation
void pop()
{
int item;
struct node *ptr;
if (head == NULL)
printf("Underflow");
else
{
item = head->val;
ptr = head;
head = head->next;
free(ptr);
printf("Item popped");
}
}
C Implementation
void display()
{
int i;
struct node *ptr;
ptr=head;
if(ptr == NULL)
printf("Stack is empty\n");
else
{
printf("Printing Stack elements \n");
while(ptr!=NULL)
{
printf("%d\n",ptr->val);
ptr = ptr->next;
}
}
}
#include <stdio.h>
#include <stdlib.h>
void push();
void pop();
void display();
struct node
{
int val;
struct node *next;
};
struct node *head;
void main ()
{
int choice=0;
printf("\n*********Stack operations using linked list*********\n");
printf("\n----------------------------------------------\n");
do
{
printf("\n\nChose one from the below options...\n");
printf("\n1.Push\n2.Pop\n3.Show\n4.Exit");
printf("\n Enter your choice \n");
scanf("%d",&choice);
switch(choice)
{
case 1: push();
break;
case 2: pop();
break;
case 3: display();
break;
case 4: printf("Exiting....");
break;
default: printf("Please Enter valid choice ");
exit(0);
}
}while(1);
}
void push ()
{
int val;
struct node *ptr = (struct node*)malloc(sizeof(struct node));
if(ptr == NULL)
printf("not able to push the element");
else
{
printf("Enter the value");
scanf("%d",&val);
if(head==NULL)
{
ptr->val = val;
ptr -> next = NULL;
head=ptr;
}
else
{
ptr->val = val;
ptr->next = head;
head=ptr;
}
printf("Item pushed");
}
}
void pop()
{
int item;
struct node *ptr;
if (head == NULL)
printf("Underflow");
else
{
item = head->val;
ptr = head;
head = head->next;
free(ptr);
printf("Item popped");
}
}
void display()
{
int i;
struct node *ptr;
ptr=head;
if(ptr == NULL)
printf("Stack is empty\n");
else
{
printf("Printing Stack elements \n");
while(ptr!=NULL)
{
printf("%d\n",ptr->val);
ptr = ptr->next;
}
}
}
Queue
A queue can be defined as an ordered list which enables insert operations to
be performed at one end called REAR and delete operations to be performed
at another end called FRONT.
Queue is referred to be as First In First Out list.
For example, people waiting in line for a rail ticket form a queue.
Applications of Queue
Due to the fact that queue performs actions on first in first out basis which is quite fair for
the ordering of actions. There are various applications of queues discussed as below.
1. Queues are widely used as waiting lists for a single shared resource like printer, disk,
CPU.
2. Queues are used in asynchronous transfer of data (where data is not being
transferred at the same rate between two processes) for eg. pipes, file IO, sockets.
3. Queues are used as buffers in most of the applications like MP3 media player, CD
player, etc.
4. Queue are used to maintain the play list in media players in order to add and remove
the songs from the play-list.
5. Queues are used in operating systems for handling interrupts.
Complexity
Average Worst
Implementations of Queue
There are two ways of implementing the Queue:
Types of Queue
There are four types of Queues:
Linear Queue
In Linear Queue, an insertion takes place from one end while the deletion occurs from
another end. The end at which the insertion takes place is known as the rear end, and the
end at which the deletion takes place is known as front end. It strictly follows the FIFO rule.
The linear Queue can be represented, as shown in the below figure:
The above figure shows that the elements are inserted from the rear end, and if we insert
more elements in a Queue, then the rear value gets incremented on every insertion. If we
want to show the deletion, then it can be represented as:
In the above figure, we can observe that the front pointer points to the next element, and
the element which was previously pointed by the front pointer was deleted.
The major drawback of using a linear Queue is that insertion is done only from the rear
end. If the first three elements are deleted from the Queue, we cannot insert more
elements even though the space is available in a Linear Queue. In this case, the linear
Queue shows the overflow condition as the rear is pointing to the last element of the
Queue.
Circular Queue
In Circular Queue, all the nodes are represented as circular. It is similar to the linear Queue
except that the last element of the queue is connected to the first element. It is also known
as Ring Buffer as the ends are connected to another end. The circular queue can be
represented as:
The drawback that occurs in a linear queue is overcome by using the circular queue. If the
empty space is available in a circular queue, the new element can be added in an empty
space by simply incrementing the value of rear.
Priority Queue
A priority queue is another special type of Queue data structure in which each element has
some priority associated with it. Based on the priority of the element, the elements are
arranged in a priority queue. If the elements occur with the same priority, then they are
served according to the FIFO principle.
In priority Queue, the insertion takes place based on the arrival while the deletion occurs
based on the priority.
Deque
In Deque, the insertion and deletion can occur from both ends.
The above figure shows the queue of characters forming the English word "HELLO". Since,
No deletion is performed in the queue till now, therefore the value of front remains -1 .
However, the value of rear increases by one every time an insertion is performed in the
queue. After inserting an element into the queue shown in the above figure, the queue will
look something like following. The value of rear will become 5 while the value of front
remains same.
After deleting an element, the value of front will increase from -1 to 0. however, the queue
will look something like following.
If the item is to be inserted as the first element in the list, in that case set the value of front
and rear to 0 and insert the element at the rear end.
Otherwise keep increasing the value of rear and insert each element one by one having rear
as the index.
C Function
void insert (int queue[], int max, int front, int rear, int item)
{
if (rear + 1 == max)
{
printf("overflow");
}
else
{
if(front == -1 && rear == -1)
{
front = 0;
rear = 0;
}
else
{
rear = rear + 1;
}
queue[rear]=item;
}
}
Otherwise, keep increasing the value of front and return the item stored at the front end of
the queue at each time.
C Function
int delete (int queue[], int max, int front, int rear)
{
int y;
if (front == -1 || front > rear)
printf("underflow");
else
{
y = queue[front];
if(front == rear)
front = rear = -1;
else
front = front + 1;
return y;
}
}
void insert();
void delete();
void display();
int front = -1, rear = -1;
int queue[maxsize];
void main ()
{
int choice;
while(choice != 4)
{
printf("\n ********Main Menu******\n");
printf("\n===================\n");
printf("\n1.insert\n2.Delete\n3.Display\n4.Exit\n");
printf("\nEnter your choice ?");
scanf("%d",&choice);
switch(choice)
{
case 1: insert();
break;
case 2: delete();
break;
case 3: display();
break;
case 4: exit(0);
Default: printf("\nEnter valid choice??\n");
}
}
}
void insert()
{
int item;
printf("\nEnter the element\n");
scanf("\n%d",&item);
if(rear == maxsize-1)
{
printf("\nOVERFLOW\n");
return;
}
if(front == -1 && rear == -1)
{
front = 0;
rear = 0;
}
else
{
rear = rear+1;
}
queue[rear] = item;
printf("\nValue inserted ");
}
void delete()
{
int item;
if (front == -1 || front > rear)
{
printf("\nUNDERFLOW\n");
return;
}
else
{
item = queue[front];
if(front == rear)
{
front = -1;
rear = -1 ;
}
else
{
front = front + 1;
}
printf("\nvalue deleted ");
}
}
void display()
{
int i;
if(rear == -1)
{
printf("\nEmpty queue\n");
}
else
{ printf("\nprinting values .....\n");
for(i=front;i<=rear;i++)
{
printf("\n%d\n",queue[i]);
}
}
o Memory wastage : The space of the array, which is used to store queue elements,
can never be reused to store the elements of that queue because the elements can
only be inserted at front end and the value of front might be so high so that, all the
space before that, can never be filled.
The above figure shows how the memory space is wasted in the array representation of
queue. In the above figure, a queue of size 10 having 3 elements, is shown. The value of
the front variable is 5, therefore, we can not reinsert the values in the place of already
deleted element before the position of front. That much space of the array is wasted and
can not be used in the future (for this queue).
In a linked queue, each node of the queue consists of two parts i.e. data part and the link
part. Each element of the queue points to its immediate next element in the memory.
In the linked queue, there are two pointers maintained in the memory i.e. front pointer and
rear pointer. The front pointer contains the address of the starting element of the queue
while the rear pointer contains the address of the last element of the queue.
Insertion and deletions are performed at rear and front end respectively. If front and rear
both are NULL, it indicates that the queue is empty.
Insert operation
The insert operation append the queue by adding an element to the end of the queue. The
new element will be the last element of the queue.
Firstly, allocate the memory for the new node ptr by using the following statement.
There can be the two scenario of inserting this new node ptr into the linked queue.
In the first scenario, we insert element into an empty queue. In this case, the
condition front = NULL becomes true. Now, the new element will be added as the only
element of the queue and the next pointer of front and rear pointer both, will point to NULL.
In the second case, the queue contains more than one element. The condition front = NULL
becomes false. In this scenario, we need to update the end pointer rear so that the next
pointer of rear will point to the new node ptr. Since, this is a linked queue, hence we also
need to make the rear pointer point to the newly added node ptr. We also need to make the
next pointer of rear point to NULL.
Deletion
Deletion operation removes the element that is first inserted among all the queue elements.
Firstly, we need to check either the list is empty or not. The condition front == NULL
becomes true if the list is empty, in this case , we simply write underflow on the console
and make exit.
Otherwise, we will delete the element that is pointed by the pointer front. For this purpose,
copy the node pointed by the front pointer into the pointer ptr. Now, shift the front pointer,
point to its next node and free the node pointed by the node ptr. This is done by using the
following statements.
ptr = front;
front = front -> next;
free(ptr);
struct node
{
int data;
struct node *next;
};
struct node *front;
struct node *rear;
void insert();
void delete();
void display();
void main ()
{
int choice;
while(choice != 4)
{
printf("\n********Main Menu********\n");
printf("\n===================\n");
printf("\n1.insert\n2.Delete\n3.Display\n4.Exit\n");
printf("\nEnter your choice ?");
scanf("%d",& choice);
switch(choice)
{
case 1: insert();
break;
case 2: delete();
break;
case 3: display();
break;
case 4: exit(0);
break;
default: printf("\nEnter valid choice??\n");
}
}
}
void insert()
{
struct node *ptr;
int item;
ptr = (struct node *) malloc (sizeof(struct node));
if(ptr == NULL)
{
printf("\nOVERFLOW\n");
return;
}
else
{
printf("\nEnter value?\n");
scanf("%d",&item);
ptr -> data = item;
if(front == NULL)
{
front = ptr;
rear = ptr;
front -> next = NULL;
rear -> next = NULL;
}
else
{
rear -> next = ptr;
rear = ptr;
rear->next = NULL;
}
}
}
void delete ()
{
struct node *ptr;
if(front == NULL)
{
printf("\nUNDERFLOW\n");
return;
}
else
{
ptr = front;
front = front -> next;
free(ptr);
}
}
void display()
{
struct node *ptr;
ptr = front;
if(front == NULL)
printf("\nEmpty queue\n");
else
{
printf("\nprinting values .....\n");
while(ptr != NULL)
{
printf("\n%d\n",ptr -> data);
ptr = ptr -> next;
}
}
}