Data-Structures Unit-1 2
Data-Structures Unit-1 2
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 Structure
Introduction
Data Structure can be defined as the group of data elements which provides an efficient way of
storing and organizing data in the computer so that it can be used efficiently. Some examples of
Data Structures are arrays, Linked List, Stack, Queue, etc. Data Structures are widely used in
almost every aspect of Computer Science i.e. Operating System, Compiler Design, Artificial
intelligence, Graphics and many more.
Data Structures are the main part of many computer science algorithms as they enable the
programmers to handle the data in an efficient way. It plays a vital role in enhancing the
performance of a software or a program as the main function of the software is to store and
retrieve the user's data as fast as possible.
Basic Terminology
Data structures are the building blocks of any program or the software. Choosing the appropriate
data structure for a program is the most difficult task for a programmer. Following terminology
is used as far as data structures are concerned.
Data: Data can be defined as an elementary value or the collection of values, for example,
student's name and its id are the data about the student.
Group Items: Data items which have subordinate data items are called Group item, for example,
name of a student can have first name and the last name.
Record: Record can be defined as the collection of various data items, for example, if we talk
about the student entity, then its name, address, course and marks can be grouped together to
form the record for the student.
File: A File is a collection of various records of one type of entity, for example, if there are 60
employees in the class, then there will be 20 records in the related file where each record
contains the data about each employee.
Field: Field is a single elementary unit of information representing the attribute of an entity.
As applications are getting complex and amount of data is increasing day by day, there may arise
the following problems:
Processor speed: To handle very large amount of data, high speed processing is required, but as
the data is growing day by day to the billions of files per entity, processor may fail to deal with
that much amount of data.
Data Search: Consider an inventory size of 106 items in a store, If our application needs to
search for a particular item, it needs to traverse 106 items every time, results in slowing down the
search process.
Multiple requests: If thousands of users are searching the data simultaneously on a web server,
then there are the chances that a very large server can be failed during that process
in order to solve the above problems, data structures are used. Data is organized to form a data
structure in such a way that all items are not required to be searched and required data can be
searched instantly.
Efficiency: Efficiency of a program depends upon the choice of data structures. For example:
suppose, we have some data and we need to perform the search for a particular record. In that
case, if we organize our data in an array, we will have to search sequentially element by element.
hence, using array may not be very efficient here. There are better data structures which can
make the search process efficient like ordered array, binary search tree or hash tables.
Reusability: Data structures are reusable, i.e. once we have implemented a particular data
structure, we can use it at any other place. Implementation of data structures can be compiled
into libraries which can be used by different clients.
Abstraction: Data structure is specified by the ADT which provides a level of abstraction. The
client program uses the data structure through interface only, without getting into the
implementation details.
Linear Data Structures: A data structure is called linear if all of its elements are arranged in the
linear order. In linear data structures, the elements are stored in non-hierarchical way where 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.
The individual elements of the array age are:
age[0], age[1], age[2], age[3],. age[98], age[99].
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.
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.
If a data structure organizes the data in random order, then that data structure is called as
Non-Linear Data Structure.
Example
1. Tree
2. Graph
3. Dictionaries
4. Heaps
5. Tries, Etc.,
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 atmost
one parent except the root node. Trees can be classfied into many categories which will be
discussed later in this tutorial.
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 can not have the one.
1) Traversing: Every data structure contains the set of data elements. Traversing the data
structure means visiting each element of the data structure in order to perform some specific
operation like searching or sorting.
Example: If we need to calculate the average of the marks obtained by a student in 6 different
subject, we need to traverse the complete array of marks and calculate the total sum, then we will
devide that sum by the number of subjects i.e. 6, in order to find the average.
2) Insertion: Insertion can be defined as the process of adding the elements to the data structure
at any location.
If the size of data structure is n then we can only insert n-1 data elements into it.
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. We will discuss each one of them later in this tutorial.
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
An abstract data type, sometimes abbreviated ADT, is a logical description of how we view the
data and the operations that are allowed without regard to how they will be implemented. This
means that we are concerned only with what data is representing and not with how it will
eventually be constructed. By providing this level of abstraction, we are creating an
encapsulation around the data. The idea is that by encapsulating the details of the
implementation, we are hiding them from the user’s view. This is called information hiding. The
implementation of an abstract data type, often referred to as a data structure, will require that we
provide a physical view of the data using some collection of programming constructs and
primitive data types.
As the items can be added or removed only from the top i.e. the last item to be added to a stack is the first
item to be removed.
Operations on stack:
While performing push and pop operations the following test must be conducted on the stack.
a) Stack is empty or not b) stack is full or not
1. Push: Push operation is used to add new elements in to the stack. At the time of addition first check
the stack is full or not. If the stack is full it generates an error message "stack overflow".
2. Pop: Pop operation is used to delete elements from the stack. At the time of deletion first check the
stack is empty or not. If the stack is empty it generates an error message "stack underflow".
All insertions and deletions take place at the same end, so the last element added to the stack will be the
first element removed from the stack. When a stack is created, the stack base remains fixed while the stack
top changes as elements are added and removed. The most accessible element is the top and the least
accessible element is the bottom of the stack.
1. push():When an element is added to a stack, the operation is performed by push(). Below Figure
shows the creation of a stack and addition of elements using push().
Initially top=-1, we can insert an element in to the stack, increment the top value i.e top=top+1. We can
insert an element in to the stack first check the condition is stack is full or not. i.e top>=size-1. Otherwise
add the element in to the stack.
Step 1: START
Step 2: if top>=size-1 then
Write “ Stack is Overflow”
Step 3: Otherwise
3.1 : read data value ‘x’
3.2 : top=top+1;
3.3 : stack[top]=x;
Step 4: END
figure shows a stack initially with three elements and shows the deletion of elements using pop().
We can insert an element from the stack, decrement the top value i.e top=top-1.
We can delete an element from the stack first check the condition is stack is empty or not.
i.e top==-1. Otherwise remove the element from the stack.
Algorithm: procedure pop():
Step 1: START
Step 2: if top==-1 then
Write “Stack is Underflow”
Step 3: otherwise
3.1 : print “deleted element”
3.2 : top=top-1;
Step 4: END
3. display():This operation performed display the elements in the stack. We display the element in the
stack check the condition is stack is empty or not i.e top==-1.Otherwise display the list of elements in
the stack.
OUTPUT:
Enter the number of elements in the stack 5
*********Stack operations using array*********
Applications of STACK:
Application of Stack :
Recursive Function.
Expression Evaluation.
Expression Conversion.
Infix to postfix
Infix to prefix
Postfix to infix
Postfix to prefix
Prefix to infix
Prefix to postfix
Reverse a Data
Processing Function Calls
Expressions:
• Operator is a symbol which performs a particular task like arithmetic operation or logical
operation or conditional operation etc.,
• Operands are the values on which the operators can perform the task. Here operand can
be a direct value or variable or address of memory location
Expression types:
Based on the operator position, expressions are divided into THREE types. They are as follows.
• Infix Expression
• Example
• Postfix Expression
• In postfix expression, operator is used after operands. We can say that "Operator
follows the Operands".
• Example:
• Prefix Expression
• In prefix expression, operator is used before operands. We can say that "Operands
follows the Operator".
• Example:
• If the scanned symbol is an operand, then place directly in the postfix expression
(output).
• If the symbol scanned is a right parenthesis, then go on popping all the items from the
stack and place them in the postfix expression till we get the matching left parenthesis.
• If the scanned symbol is an operator, then go on removing all the operators from the stack
and place them in the postfix expression, if and only if the precedence of the operator
which is on the top of the stack is greater than (or greater than or equal) to the precedence
of the scanned operator and push the scanned operator onto the stack otherwise, push the
scanned operator onto the stack.
Example-1
Example2:
Convert ((A – (B + C)) * D) ↑ (E + F) infix expression to postfix form:
Example3
Convert a + b * c + (d * e + f) * g the infix expression into postfix form.
SYMBOL POSTFIX STRING STACK REMARKS
a a
+ a +
b ab +
* ab +*
c abc +*
+ abc*+ +
( abc*+ +(
d abc*+d +(
* abc*+d +(*
e abc*+de +(*
+ abc*+de* +(+
f abc*+de*f +(+
) abc*+de*f+ +
* abc*+de*f+ +*
g abc*+de*f+g +*
End of The input is now empty. Pop the output symbols
abc*+de*f+g*+
string from the stack until it is empty.
Example 3:
Example 4:
Convert the following infix expression A+(B *C–(D/E↑F)*G)*H into its equivalent postfix expression.
A A
+ A +
( A +(
B AB +(
* AB +(*
C ABC +(*
- ABC* +(-
( ABC* +(-(
D ABC*D +(-(
/ ABC*D +(-(/
E ABC*DE +(-(/
↑ ABC*DE +(-(/↑
F ABC*DEF +(-(/↑
) ABC*DEF↑/ +(-
* ABC*DEF↑/ +(-*
G ABC*DEF↑/G +(-*
) ABC*DEF↑/G*- +
* ABC*DEF↑/G*- +*
H ABC*DEF↑/G*-H +*
6 6
5 6, 5
2 6, 5, 2
8 2 3 5 6, 5, 5, 8 Next 8 is pushed
Example2
Example 3:
6 6
2 6, 2
3 6, 2, 3
+ 2 3 5 6, 5
- 6 5 1 1
3 6 5 1 1, 3
8 6 5 1 1, 3, 8
2 6 5 1 1, 3, 8, 2
/ 8 2 4 1, 3, 4
+ 3 4 7 1, 7
* 1 7 7 7
2 1 7 7 7, 2
↑ 7 2 49 49
3 7 2 49 49, 3
+ 49 3 52 52
Reverse a Data:
To reverse a given set of data, we need to reorder the data so that the first and last elements are
exchanged, the second and second last element are exchanged, and so on for all other elements.
o Reversing a string
Reverse a String
A Stack can be used to reverse the characters of a string. This can be achieved by simply pushing
one by one each character onto the Stack, which later can be popped from the Stack one by one.
Because of the last in first out property of the Stack, the first character of the Stack is on the
bottom of the Stack and the last character of the String is on the Top of the Stack and after
performing the pop operation in the Stack, the Stack returns the String in Reverse order.
Stack plays an important role in programs that call several functions in succession. Suppose we
have a program containing three functions: A, B, and C. function A invokes function B, which
invokes the function C.
When we invoke function A, which contains a call to function B, then its processing will not be
completed until function B has completed its execution and returned. Similarly for function B
and C. So we observe that function A will only be completed after function B is completed and
function B will only be completed after function C is completed. Therefore, function A is first to
be started and last to be completed. To conclude, the above function activity matches the last in
first out behavior and can easily be handled using Stack.
Consider addrA, addrB, addrC be the addresses of the statements to which control is returned
after completing the function A, B, and C, respectively.
The above figure shows that return addresses appear in the Stack in the reverse order in which
the functions were called. After each function is completed, the pop operation is performed, and
execution continues at the address removed from the Stack. Thus the program that calls several
functions in succession can be handled optimally by the stack data structure. Control returns to
each function at a correct place, which is the reverse order of the calling sequence.
A queue is linear data structure and collection of elements. A queue is another special kind of list, where
items are inserted at one end called the rear and deleted at the other end called the front. The principle of
queue is a “FIFO” or “First-in-first-out”.
Queue is an abstract data structure. A queue is a useful data structure in programming. It is similar to the
ticket queue outside a cinema hall, where the first person entering the queue is the first person who gets the
ticket.
A real-world example of queue can be a single-lane one-way road, where the vehicle enters first, exits first.
More real-world examples can be seen as queues at the ticket windows and bus-stops and our college
library.
The operations for a queue are analogues to those for a stack; the difference is that the insertions go at the
end of the list, rather than the beginning.
Operations on QUEUE:
A queue is an object or more specifically an abstract data structure (ADT) that allows the following
operations:
Enqueue or insertion: which inserts an element at the end of the queue.
Dequeue or deletion: which deletes an element at the start of the queue.
Again insert another element 33 to the queue. The status of the queue is:
Now, delete an element. The element deleted is the element at the front of the queue.So the status of
the queue is:
Again, delete an element. The element to be deleted is always pointed to by the FRONT pointer. So,
22 is deleted. The queue status is as follows:
Now, insert new elements 44 and 55 into the queue. The queue status is:
Now it is not possible to insert an element 66 even though there are two vacant positions in
the linear queue. To overcome this problem the elements of the queue are to be shifted
towards the beginning of the queue so that it creates vacant position at the rear end. Then
the FRONT and REAR are to be adjusted properly. The element 66 can be inserted at the
rear end. After this operation, the queue status is as follows:
This difficulty can overcome if we treat queue position with index 0 as a position that comes
after position with index 4 i.e., we treat the queue as a circular queue.
Check if the queue is already full by comparing rear to max - 1. if so, then return an overflow
error
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.
Otherwise, keep increasing the value of front and return the item stored at the front end of the
queue at each time.
printf("\n==============================================================
===\n");
printf("\n1.insert an element\n2.Delete an element\n3.Display the queue\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()
{
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;
}
}
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]);
}
}
}
Although, the technique of creating a queue is easy, but there are some drawbacks of using this
technique to implement a queue.
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).
o Deciding the array size
One of the most common problem with array implementation is the size of the array which
requires to be declared in advance. Due to the fact that, the queue can be extended at runtime
depending upon the problem, the extension in the array size is a time taking process and almost
impossible to be performed at runtime since a lot of reallocations take place. Due to this reason,
we can declare the array large enough so that we can store queue elements as enough as possible
but the main problem with this declaration is that, most of the array slots (nearly half) can never
be reused. It will again lead to memory wastage.
Types of Queues
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.
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 all the ends are connected to another end. The circular queue can be
represented as:
he 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.
3. 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. The priority Queue can be shown as:
The above figure shows that the highest priority element comes first and the elements of the
same priority are arranged based on FIFO structure.
4. Deque
Both the Linear Queue and Deque are different as the linear queue follows the FIFO principle
whereas, deque does not follow the FIFO principle. In Deque, the insertion and deletion can occur
from both ends.
1. It allocates the memory dynamically. All the nodes of linked list are non-contiguously
stored in the memory and linked together with the help of pointers.
2. Sizing is no longer a problem since we do not need to define its size at the time of
declaration. List grows as per the program's demand and limited to the available memory
space.
It is the commonly used linked list in programs. If we are talking about the linked list, it means it
is a singly linked list. The singly linked list is a data structure that contains two parts, i.e., one is
the data part, and the other one is the address part, which contains the address of the next or the
successor node. The address part in a node is also known as a pointer.
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.
Representation of the node in a singly linked list
struct node
{
int data;
struct node *next;
}
As the name suggests, the doubly linked list contains two pointers. We can define the doubly
linked list as a linear data structure with three parts: the data part and the other two address part.
In other words, a doubly linked list is a list that has three parts in a single node, includes one data
part, a pointer to its previous node, and a pointer to the next node.
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.
A circular linked list is a variation of a singly linked list. The only difference between the singly
linked list and a circular linked list is that the last node does not point to any node in a singly
linked list, so its link part contains a NULL value. On the other hand, the circular linked list is a
list in which the last node connects to the first node, so the link part of the last node holds the
first node's address. The circular linked list has no starting and ending node. We can traverse in
any direction, i.e., either backward or forward. The diagrammatic representation of the circular
linked list is shown below:
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:
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 can not 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.
There are various operations which can be performed on singly linked list. A list of all such
operations is given below.
Node Creation
struct node
{
int data;
struct node *next;
};
struct node *head, *ptr;
ptr = (struct node *)malloc(sizeof(struct node *));
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.
1. Inserting at Beginning
2. Inserting at the End of the LIst
3. Inserting after specified node
Inserting a new element into a singly linked list at beginning is quite simple. We just need to
make a few adjustments in the node links. There are the following steps which need to be
followed in order to inser a new node in the list at beginning.
1. 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.
ptr = (struct node *) malloc(sizeof(struct node *));
ptr → data = item
2. 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
head = ptr;
Algorithm
o Step 1: IF PTR = NULL
Write OVERFLOW
Go to Step 7
[END OF IF]
o Step 2: SET NEW_NODE = PTR
o Step 3: SET PTR = PTR → NEXT
o Step 4: SET NEW_NODE → DATA = VAL
o Step 5: SET NEW_NODE → NEXT = HEAD
o Step 6: SET HEAD = NEW_NODE
o Step 7: EXIT
Function for inserting element at beginning of the list
void beginsert()
{
struct node *ptr;
int item;
ptr = (struct node *) malloc(sizeof(struct node *));
if(ptr == NULL)
{
printf("\n memory insufficient to allocate");
}
else
{
printf("\nEnter value\n");
scanf("%d",&item);
ptr->data = item;
ptr->next = head; head = ptr;
In order to insert a node at the last, there are two following scenarios which need to be
mentioned.
1. The node is being added to an empty list(CASE 1)
2. The node is being added to the end of the linked list(CASE2)
in the first case,(CASE1)
o The condition (head == NULL) gets satisfied. Hence, we just need to allocate the space
for the node by using malloc statement in C. Data and the link part of the node are set
up by using the following statements.
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
In the second case: CASE(2):
o The condition Head = NULL would fail, since Head is not null. Now, we need to declare
a temporary pointer temp in order to traverse through the list. temp is made to point the
first node of the list.
Temp = head
o Then, traverse through the entire linked list using the statements:
while (temp→ next != NULL)
temp = temp → next;
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 o
o If 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;
void lastinsert()
{
struct node *ptr,*temp;
int item;
ptr = (struct node*)malloc(sizeof(struct node));
if(ptr == NULL)
{
printf("\nOVERFLOW");
}
else
{
printf("\nEnter value?\n"); scanf("%d",&item);
ptr->data = item;
if(head == NULL)
}
}
}
10
Algorithm
o STEP 1: IF PTR = NULL
WRITE OVERFLOW
GOTO STEP 12
END OF IF
o STEP 2: SET NEW_NODE = PTR
o STEP 3: NEW_NODE → DATA = VAL
o STEP 4: SET TEMP = HEAD
o STEP 5: SET I = 0
o STEP 6: REPEAT STEP 5 AND 6 UNTIL I<loc< li=""></loc<>
o STEP 7: TEMP = TEMP → NEXT
o STEP 8: IF TEMP = NULL
WRITE "DESIRED NODE NOT PRESENT"
GOTO STEP 12
END OF IF
END OF LOOP
o STEP 9: PTR → NEXT = TEMP → NEXT
o STEP 10: TEMP → NEXT = PTR
o STEP 11: SET PTR = NEW_NODE
o STEP 12: EXIT
C Function
void randominsert()
{
int i,loc,item;
11
Deleting a node from the beginning of the list is the simplest operation of all. It just need a few
adjustments in the node pointers. Since the first node of the list is to be deleted, therefore, we just
need to make the head, point to the next of the head. This will be done by using the following
statements
12
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)
Algorithm
o Step 1: IF HEAD = NULL
Write UNDERFLOW
Go to Step 5
[END OF IF]
o Step 2: SET PTR = HEAD
o Step 3: SET HEAD = HEAD -> NEXT
o Step 4: FREE PTR
o Step 5: EXIT
C function
void begdelete()
{
struct node *ptr;
if(head == NULL)
{
printf("\nList is empty");
}
else
{
ptr = head;
head = ptr->next;
free(ptr);
printf("\n Node deleted from the begining ...");
}
13
14
C Function
void end_delete()
{
struct node *ptr,*ptr1;
if(head == NULL)
{
printf("\nlist is empty");
}
else if(head -> next == NULL)
{
head = NULL;
free(head);
printf("\nOnly node of the list deleted ...");
}
else
{
ptr = head;
while(ptr->next != NULL)
{
ptr1 = ptr;
ptr = ptr ->next;
}
ptr1->next = NULL;
free(ptr);
printf("\n Deleted Node from the last ...");
15
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.
Algorithm
o STEP 1: IF HEAD = NULL
WRITE UNDERFLOW
GOTO STEP 10
END OF IF
16
Searching is performed in order to find the location of a particular element in the list. Searching
any element in the list needs traversing through the list and make the comparison of every
element of the list with the specified element. If the element is matched with any of the list
element then the location of the element is returned from the function.
Algorithm
o Step 1: SET PTR = HEAD
o Step 2: Set I = 0
o STEP 3: IF PTR = NULL
write i+1
End of IF
o STEP 6: I = I + 1
o STEP 7: PTR = PTR → NEXT
[END OF LOOP]
17
C Function
void search()
{
struct node *ptr;
int item,i=0,flag;
ptr = head;
if(ptr == NULL)
{
printf("\nEmpty List\n");
}
else
{
printf("\nEnter item which you want to search?\n");
scanf("%d",&item);
while (ptr!=NULL)
{
if(ptr->data == item)
{
printf("item found at location %d ",i+1);
flag=0;
}
else
{
flag=1;
}
i++;
ptr = ptr -> next;
}
if(flag==1)
{
printf("Item not found\n");
}
}
18
Algorithm
o STEP 1: SET PTR = HEAD
o STEP 2: IF PTR = NULL
WRITE "EMPTY LIST"
GOTO STEP 7
END OF IF
o STEP 4: REPEAT STEP 5 AND 6 UNTIL PTR != NULL
o STEP 5: PRINT PTR→ DATA
o STEP 6: PTR = PTR → NEXT
[END OF LOOP]
o STEP 7: EXIT
DISADVANTAGE
19
Doubly linked list is a complex type of linked list in which a node contains a pointer to the
previous as well as the next node in the sequence. Therefore, in a doubly linked list, a node
consists of three parts: node data, pointer to the next node in sequence (next pointer) , pointer to
the previous node (previous pointer). A sample node in a doubly linked list is shown in the
figure.
A doubly linked list containing three nodes having numbers from 1 to 3 in their data part, is
shown in the following image.
struct node
{
struct node *prev;
int data;
struct node *next;
}
The prev part of the first node and the next part of the last node will always contain null
indicating end in each direction.
In a singly linked list, we could traverse only in one direction, because each node contains
address of the next node and it doesn't have any record of its previous nodes. However, doubly
Memory Representation of a doubly linked list is shown in the following image. Generally,
doubly linked list consumes more space for every node and therefore, causes more expansive
basic operations such as insertion and deletion. However, we can easily manipulate the elements
of the list since the list maintains pointers in both the directions (forward and backward).
In the following image, the first element of the list that is i.e. 13 stored at address 1. The head
pointer points to the starting address 1. Since this is the first element being added to the list
therefore the prev of the list contains null. The next node of the list resides at address 4
therefore the first node contains 4 in its next pointer.
We can traverse the list in this way until we find any node containing null or -1 in its next part.
Node Creation
struct node
{
struct node *prev;
int data;
struct node *next;
};
struct node *head;
INSERTION
Insertion in doubly linked list at beginning
As in doubly linked list, each node of the list contain double pointers therefore we have to
maintain more number of pointers in doubly linked list as compare to singly linked list.
There are two scenarios of inserting any element into doubly linked list. Either the list is empty
or it contains at least one element. Perform the following steps to insert a node in doubly linked
list at beginning.
o Allocate the space for the new node in the memory. This will be done by using the
following statement.
ptr = (struct node *)malloc(sizeof(struct node));
o Check whether the list is empty or not. The list is empty if the condition head == NULL
holds. In that case, the node will be inserted as the only node of the list and therefore the
prev and the next pointer of the node will point to NULL and the head pointer will point
to this node.
ptr->next = NULL;
ptr->prev=NULL;
ptr->data=item;
head=ptr;
o In the second scenario, the condition head == NULL become false and the node will be
inserted in beginning. The next pointer of the node will point to the existing head pointer
C Function
void insertbeginning( )
{
struct node *ptr = (struct node *)malloc(sizeof(struct node));
int item;
printf(“enter the value”);
Algorithm
o Step 1: IF PTR = NULL
Write
OVERFLOW Go
to Step 11
[END OF IF]
o Step 2: SET NEW_NODE = PTR
o Step 3: SET PTR = PTR -> NEXT
o Step 4: SET NEW_NODE -> DATA = VAL
o Step 5: SET NEW_NODE -> NEXT = NULL
o Step 6: SET TEMP = START
o Step 7: Repeat Step 8 while TEMP -> NEXT != NULL
o Step 8: SET TEMP = TEMP ->
NEXT [END OF LOOP]
o Step 9: SET TEMP -> NEXT = NEW_NODE
o Step 10C: SET NEW_NODE -> PREV = TEMP
o Step 11: EXIT
In order to insert a node after the specified node in the list, we need to skip the required number
of nodes in order to reach the mentioned node and then make the pointer adjustments as required.
Use the following steps for this purpose.
o Allocate the memory for the new node. Use the following statements for this.
ptr = (struct node *)malloc(sizeof(struct node));
o Traverse the list by using the pointer temp to skip the required number of nodes in order
to reach the specified node.
temp=head;
for(i=0;i<loc;i++)
{
temp = temp->next;
if(temp == NULL) // the temp will be //null if the list doesn't last long //up to mentio
ned location
{
return;
}
}
o The temp would point to the specified node at the end of the for loop. The new node
needs to be inserted after this node therefore we need to make a fer pointer adjustments
here. Make the next pointer of ptr point to the next node of temp.
ptr → next = temp → next;
make the prev of the new node ptr point to temp.
ptr → prev = temp;
make the next pointer of temp point to the new node ptr.
temp → next = ptr;
make the previous pointer of the next node of temp point to the new node.
temp → next → prev = ptr;
Algorithm
o Step 1: IF PTR = NULL
Write OVERFLOW
Go to Step 15
[END OF IF]
C Function
void insert_specified(int item)
{
struct node *ptr = (struct node *)malloc(sizeof(struct node));
struct node *temp;
int i, loc;
if(ptr == NULL)
{
printf("\n OVERFLOW");
DELETION OPERATION
Deletion at beginning
Deletion in doubly linked list at the beginning is the simplest operation. We just need to copy the
head pointer to pointer ptr and shift the head pointer to its next.
Ptr = head;
head = head → next;
now make the prev of this new head node point to NULL. This will be done by using the
following statements.
head → prev = NULL
Now free the pointer ptr by using the free function.
free(ptr)
Algorithm
o STEP 1: IF HEAD = NULL
WRITE
UNDERFLOW
GOTO STEP 6
C FUNCTION
void beginning_delete()
{
struct node *ptr;
if(head == NULL)
{
printf("\n UNDERFLOW\n");
}
else if(head->next == NULL)
{
head = NULL;
free(head);
printf("\nNode Deleted\n");
}
else
{
ptr = head;
head = head -> next;
head -> prev = NULL;
free(ptr);
printf("\nNode Deleted\n");
}
}
In order to delete the last node of the list, we need to follow the following steps.
o If the list is already empty then the condition head == NULL will become true and
therefore the operation can not be carried on.
o If there is only one node in the list then the condition head → next == NULL become
true. In this case, we just need to assign the head of the list to NULL and free head in
order to completely delete the list.
o Otherwise, just traverse the list to reach the last node of the list. This will be done by
using the following statements.
ptr = head;
if(ptr->next != NULL)
{
ptr = ptr -> next;
}
o The ptr would point to the last node of the ist at the end of the for loop. Just make the
next pointer of the previous node of ptr to NULL.
ptr → prev → next = NULL
free(ptr)
ALGORITHM
o Step 1: IF HEAD = NULL
Write UNDERFLOW
Go to Step 7
[END OF IF]
[END OF LOOP]
C PROGRAM
void last_delete()
{
struct node *ptr;
if(head == NULL)
{
printf("\n UNDERFLOW\n");
}
else if(head->next == NULL)
{
head = NULL;
free(head);
printf("\nNode Deleted\n");
}
else
{
ptr = head;
if(ptr->next != NULL)
{
ptr = ptr -> next;
}
ptr -> prev -> next = NULL;
free(ptr);
printf("\nNode Deleted\n");
}
}
C FUNCTION
void delete_specified( )
{
struct node *ptr, *temp;
int val;
printf("Enter the value");
scanf("%d",&val);
temp = head;
while(temp -> data != val)
temp = temp -> next;
if(temp -> next == NULL)
{
printf("\nCan't delete\n");
}
else if(temp -> next -> next == NULL)
{
temp ->next = NULL;
printf("\nNode Deleted\n");
}
else
{
ptr = temp -> next;
temp -> next = ptr -> next;
ptr -> next -> prev = temp;
free(ptr);
printf("\nNode Deleted\n");
Algorithm
o Step 1: IF HEAD == NULL
WRITE "UNDERFLOW"
GOTO STEP 8
[END OF IF]
o Step 2: Set PTR = HEAD
o Step 3: Set i = 0
o Step 4: Repeat step 5 to 7 while PTR != NULL
o Step 5: IF PTR → data = item
return i
[END OF
IF]
o Step 6: i = i + 1
o Step 7: PTR = PTR → next
o Step 8: Exit
C FUNCTION
void search()
{
struct node *ptr;
int item,i=0,flag;
ptr = head;
if(ptr == NULL)
{
printf("\nEmpty List\n");
}
else
{
printf("\nEnter item which you want to search?\n");
scanf("%d",&item);
while (ptr!=NULL)
{
if(ptr->data == item)
{
printf("\nitem found at location %d ",i+1);
flag=0;
break;
}
else
{
flag=1;
}
i++;
ptr = ptr -> next;
}
if(flag==1)
{
printf("\nItem not found\n");
}
}
Traversing is the most common operation in case of each data structure. For this purpose, copy
the head pointer in any of the temporary pointer ptr.
Ptr = head
then, traverse through the list by using while loop. Keep shifting value of pointer
variable ptr until we find the last node. The last node contains null in its next part.
while(ptr != NULL)
{
printf("%d\n",ptr->data);
ptr=ptr->next;
}
Although, traversing means visiting each node of the list once to perform some specific
operation. Here, we are printing the data associated with each node of the list.
Algorithm
WRITE "UNDERFLOW"
GOTO STEP 6
[END OF IF]
C Function
int traverse()
{
struct node *ptr;
if(head == NULL)
{
printf("\nEmpty List\n");
}
else
{
ptr = head;
while(ptr != NULL)
{
printf("%d\n",ptr->data);
ptr=ptr->next;
}
}
}
SLL nodes contains 2 field -data field DLL nodes contains 3 fields -data field, a previous
and next link field. link field and a next link field.
In SLL, the traversal can be done In DLL, the traversal can be done using the
using the next node link only. Thus previous node link or the next node link. Thus
traversal is possible in one direction traversal is possible in both directions (forward and
only. backward).
The SLL occupies less memory than The DLL occupies more memory than SLL as it
DLL as it has only 2 fields. has 3 fields.
In a circular Singly linked list, the last node of the list contains a pointer to the first node of the list.
We traverse a circular singly linked list until we reach the same node where we started. The circular
singly liked list has no beginning and no ending. There is no null value present in the next part of any of
the nodes.
Circular linked list are mostly used in task maintenance in operating systems. There are many examples
where circular linked list are being used in computer science including browser surfing where a record of
pages visited in the past by the user, is maintained in the form of circular linked lists and can be accessed
again on clicking the previous button.
i)Creation
#include<stdio.h>
#include<stdlib.h>
void create(int);
struct node
{
int data;
struct node *next;
};
struct node *head;
void main ()
{
int choice,item;
do
{
printf("1.Append List\n2.Exit\n3.Enter your choice?");
scanf("%d",&choice);
switch(choice)
{
case 1:
printf("\nEnter the item\n");
scanf("%d",&item);
create(item);
break;
case 2:
exit(0);
break;
default:
printf("\nPlease enter valid choice\n");
}
}while(choice != 3);
}
void create(int item)
{
ii) Insertion
Insertion into circular singly linked list at beginning
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
};
struct node *head;
void beginsert ();
void lastinsert ();
void display();
void main ()
{
int choice =0;
while(choice != 4)
{
printf("\n*********Main Menu*********\n"); printf("\
nChoose one option from the following list ...\n");
printf("\n===============================================\n");
printf("\n1.Insert in begining\n2.Insert at last\n3.display\n4.Exit\n");
printf("\nEnter your choice?\n");
scanf("\n%d",&choice);
switch(choice)
{
case 1:
beginsert();
break;
case 2:
lastinsert();
break;
case 3:
display();
case 4:
}
void lastinsert()
{
struct node *ptr,*temp;
int item;
ptr = (struct node *)malloc(sizeof(struct node));
if(ptr == NULL)
{
printf("\nOVERFLOW\n");
}
else
{
printf("\nEnter Data?");
printf("\nnode inserted\n");
}
void display()
{
struct node *ptr;
ptr=head;
if(head == NULL)
{
printf("\nnothing to print");
}
else
{
printf("\n printing values ... \n");
iii) Deletion
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
};
struct node *head;
void create();
void begin_delete();
void last_delete();
void display();
void main ()
{
int choice =0;
while(choice != 5)
case 1:
create();
break;
case 2:
begin_delete();
break;
case 3:
last_delete();
break;
case 4:
display();
break;
case 5:
exit(0);
break;
default:
printf("Please enter valid choice..");
}
}
}
void create()
{
struct node *ptr,*temp;
int item;
ptr = (struct node *)malloc(sizeof(struct node));
if(ptr == NULL)
{
printf("\nOVERFLOW\n");
}
else
{
printf("\nEnter Data?");
scanf("%d",&item);
ptr->data = item;
if(head == NULL)
{
head = ptr;
ptr -> next = head;
}
else
printf("\nnode inserted\n");
}
void begin_delete()
{
struct node *ptr;
if(head == NULL)
{
printf("\nUNDERFLOW");
}
else if(head->next == head)
{
head = NULL;
free(head);
printf("\nnode deleted\n");
}
else
{ ptr = head;
while(ptr -> next != head)
ptr = ptr -> next;
ptr->next = head->next;
free(head);
head = ptr->next; printf("\
nnode deleted\n");
}
}
void last_delete()
{
struct node *ptr, *preptr;
if(head==NULL)
{
printf("\nUNDERFLOW");
}
else if (head ->next == head)
{
head = NULL;
free(head);
}
else
{
ptr = head;
while(ptr ->next != head)
{
preptr=ptr;
ptr = ptr->next;
}
preptr->next = ptr -> next;
free(ptr);
printf("\nnode deleted\n");
}
}
void display()
{
struct node *ptr;
ptr=head;
if(head == NULL)
{
printf("\nnothing to print");
}
else
{
printf("\n printing values ... \n");
#include<stdio.h>
#include<stdlib.h>
void create(int);
void traverse();
struct node
{
int data;
struct node *next;
};
struct node *head;
void main ()
{
int choice,item;
do
{
printf("1.Append List\n2.Traverse\n3.Exit\n4.Enter your choice?");
scanf("%d",&choice);
switch(choice)
{
case 1:
printf("\nEnter the item\n");
scanf("%d",&item);
create(item);
break;
case 2:
traverse();
break;
case 3:
exit(0);
break;
default:
printf("\nPlease enter valid choice\n");
}
}while(choice != 3);
}
void create(int item)
{
}
void traverse()
{
struct node *ptr;
ptr=head;
if(head == NULL)
{
printf("\nnothing to print");
}
else
{
printf("\n printing values ... \n");