Data Structures Notes-1
Data Structures Notes-1
COURSE OBJECTIVES:
To introduce various searching and sorting techniques
To demonstrate operations of linear and non-linear data structure
To develop an application using suitable data structure.
COURSE OUTCOMES: After completion of the course, the student should be able to
CO-1: Understand basic concepts of data structures and analyse computation complexity.
CO-2: Apply linear data structures to implement various sorting, searching techniques.
CO-3: Solve the given problem using linear data structures.
CO-4: Execute the given problem using non-linear data structures.
CO-5: Analyze appropriate and efficient data structure to implement a given problem.
UNIT-I:
Introduction to Data Structures: Abstract Data Types (ADT), Asymptotic Notations. Time-
Space trade off. Searching: Linear Search and Binary Search Techniques and their time complexities.
Linear Data Structures: Stacks - ADT Stack and its operations: Applications of Stacks:
Recursion, Expression Conversion, and evaluation.
UNIT-II:
Linear Data Structures: Queues - ADT queue, Types of Queue: Linear Queue,
CircularQueue, Double ended queue, operations on each types of Queues
Linked Lists: Singly linked lists: Representation in memory, Operations: Traversing, Searching,
insertion, Deletion from linked list; Linked representation of Stack and Queue. Doubly linked List,
Circular Linked Lists: All operations
UNIT-III:
Trees: Basic Tree Terminologies, Different types of Trees: Binary Tree, Binary Search Tree, AVL Tree;
Tree Operations on each of the trees and their algorithms with time complexities.
B-Trees: Definition, Operations.
UNIT-IV:
Priority Queue: Definition, Operations and their time complexities.
Sorting: Objective and properties of different sorting algorithms: Quick Sort, Heap
Sort, Merge Sort; Radix sort
UNIT-V:
Dictionaries: Definition, ADT, Linear List representation, operations- insertion, deletion and
searching, Hash Table representation, Hash function-Division Method, Collision Resolution
Techniques-Separate Chaining, open addressing-linear probing, quadratic probing, double
hashing, Rehashing.
Graphs: Graph terminology –Representation of graphs –Graph Traversal: BFS (breadth first search)
–DFS (depth first search) –Minimum Spanning Tree.
TEXT BOOKS:
1. Fundamental of Data Structure, Horowitz and Sahani, Galgotia Publication
2. Data Structure, Lipschutz, Schaum Series
REFERENCES:
1. Algorithms, Data Structures, and Problem Solving with C++, Illustrated Edition
byMark Allen Weiss, Addison-Wesley Publishing Company
2. How to Solve it by Computer, 2nd Impression by R.G. Dromey, Pearson Education
UNIT-I
1.1. Abstract data type (ADT):
• Abstract Data type is the way we look at a data structure, focusing on
what it does and ignoring how it does its job.
• Abstract Data type (ADT) is a type (or class) for objects whose behavior
is defined by a set of values and a set of operations.
• The definition of ADT only mentions what operations are to be performed
but not how these operations will be implemented. It does not specify
how data will be organized in memory and what algorithms will be used
for implementing the operations.
• The following figure gives the visual representation of abstract data
type.
Definition: f(n) and g(n) are two functions and f(n) = Ω (g(n)) iff there
exist two positive constants c and n0 such that f(n) ≥ c.g(n) for all n ≥ n0
This notation describes both the upper bound and lower bound of an
algorithm so we can say that it defines exact asymptotic behavior. In the real-
case scenario the algorithm does not always run on best and worst cases, the
average running time lies between best and worst and can be represented by the
theta notation.
Definition: f(n) and g(n) are two functions and f(n) = Ω (g(n)) iff there
exist three positive constants c1, c2 and n0 such that c1.g(n) ≤ f(n) ≤ c2.g(n)
for all n ≥ n0
The best Algorithm is that which helps to solve a problem that requires less
space in memory and also takes less time to generate the output. But in
general, it is not always possible to achieve both of these conditions at the
same time.
Compressed vs Uncompressed:
Storing only the source and rendering it as an image every time the page is
requested would be trading time for space: more time used, but less space.
Storing the image would be trading space for time: more space, but less
time.
Smaller code with loops occupies less space but requires more
computation time (for jumping back to the beginning of the loop after each
iteration).
Larger codes require more space but less computation time.
1.4. Searching
Searching is the processing of finding a particular element is present in the
list or not. There are two types of searching techniques.
1. Linear search
2. Binary search.
1.4.1. Linear Search
Linear search technique is also known as sequential search technique. The
linear search is a method of searching an element in a list in sequence. In this
method, the array is searched for the required element from the beginning of
the list/array or from the last element to first element of array and contin ues
until the item is found or the entire list/array has been searched.
Algorithm
Step 1: set-up a flag to indicate “element not found”.
Step 2: Take the first element in the list.
Step 3: If the element in the list is equal to the desired element.
Set flag to “element found.”
Display the message “element found in the list.”
Go to step 6
Step 4: If it is not the end of list,
Take the next element in the list
Go to step 3
Step 5: If the flag is “element not found”
Display the message “element not found”
Step 6: End of the Algorithm
Program
/*
Linear search
*/
#include<stdio.h>
#include<conio.h>
void main()
{
int a[10],i,key,flag=0,n;
clrscr();
printf("\n Array length(No of elements)\n");
scanf("%d",&n);
printf("\n ENter array elements\n");
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
printf("\n Enter key\n");
scanf("%d",&key);
for(i=0;i<n;i++)
{
if(key == a[i])
{
flag=1;
break;
}
}
if(flag == 1)
{
printf("\n Search is successful\n");
}
else
{
printf("\n Search is unsuccessful\n");
}
getch();
}
Advantages
1. It is a simple and conventional method of searching for data. The linear
or sequential name implies that the items are stored in a systematic
manner.
2. The elements in the list can be in any order. i.e. The linear search can
be applied on sorted or unsorted linear data structure.
Disadvantages
1. This method is insufficient when a large number of elements is
present in list.
2. It consumes more time and reduces the retrieval rate of the system.
Time complexities:
1. Best case – O(1) – if the key element is at the first position
2. Average case – O(n) – if the key element is in the middle of the array
3. Worst case - O(n) – if the key element is the at the end of the array.
Program:
/*
Binary search
*/
#include<stdio.h>
#include<conio.h>
void main()
{
int a[10],low,i,high,key,flag=0,n,mid;
clrscr();
printf("\n Array length(No of elements)\n");
scanf("%d",&n);
printf("\n Enter array elements\n");
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
printf("\n Enter key\n");
scanf("%d",&key);
low=0;
high=n-1;
while(low<=high)
{
mid = (low+high)/2;
if(key == a[mid])
{
flag=1;
break;
}
else if(key < a[mid])
{
high = mid-1;
}
else
{
low = mid+1;
}
}
if(flag==1)
{
printf("\n Search is successful\n");
}
else
{
printf("\n Search is unsuccessful\n");
}
getch();
}
Advantages
1. It takes less time complexity (O(logn))
2. it is efficient when the data set is large.
Disadvantages
1. The elements must be in either ascending or descending order.
Time complexities
1. Best case – O(1) – if the key element is in the middle position of an array.
2. Average case – O(logn)
3. Worst case – O(logn)
• Graphics
Linked List: A linked list is a linear data structure; it consists of nodes where
each node contains a data field and a reference(link) to the next node in the
list.
Note: Non-linear data structures follows hierarchy, i.e. the elements are
arranged in multiple levels.
Tree: A tree can be theoretically defined as a finite set of one or more data
items (or nodes) such that :
1. There is a special node called the root of the tree.
2. Removing nodes (or data item) are partitioned into number of mutually
exclusive (i.e., disjoined) subsets each of which is itself a tree, are called
sub tree.
Graph: A graph is a structure made of two components: a set of vertices V,
and a set of edges E. Therefore, a graph is G = (V, E), where G is a graph.
1.6. Stack:
Definition: A stack is a linear data structure in which insertions and
deletions are performed at only one end called the top of the stack.
The last added element will be first removed from the stack. Hence it is also
called Last-in-First-out (LIFO) data structure.
Note:
1. The most frequently accessible element in the stack is the top most
elements, whereas the least accessible element is the bottom of the stack.
The basic operations on stack are 1. Push 2. Pop
1. Push: Inserting an element into the stack is called push.
2. Pop: Removing an element from the stack is called pop.
PRIMITIVE STACK OPERATIONS: PUSH AND POP
The primitive operations performed on the stack are as follows:
PUSH: The process of adding (or inserting) a new element to the top of the
stack is called PUSH operation. Pushing an element to a stack will add the
new element at the top. After every push operation the top is incremented by
one. If the array is full and no new element can be accommodated, then the
stack overflow condition occurs.
Algorithm:
Step 1: IF TOP = MAX-1 PRINT OVERFLOW [END OF IF]
Step 2: SET TOP = TOP+1
Step 3: SET STACK[TOP] = VALUE
Step 4: END
POP: The process of deleting (or removing) an element from the top of stack
is called POP operation. After every pop operation the stack is decremented
by one. If there is no element in the stack and the pop operation is performed,
then the stack underflow condition occurs.
Algorithm:
Step 1: IF TOP = NULL PRINT UNDERFLOW [END OF IF]
Step 2: SET VAL = STACK[TOP]
Step 3: SET TOP = TOP-1
Step 4: END
1.6.1. Stack ADT
• push() – Insert an element at one end of the stack called top.
• pop() – Remove and return the element at the top of the stack, if it is
not empty.
• peek() – Return the element at the top of the stack without removing
it, if the stack is not empty.
• size() – Return the number of elements in the stack.
• isEmpty() – Return true if the stack is empty, otherwise return false
• isFull() - Return true if the stack is full, otherwise return false.
1.6.2. Implementation of Stack
There are two ways to implement a stack.
1. Array representation
2. Linked list representation. (will be discussed in UNIT-2)
C program to implement stack using arrays
#include<stdio.h>
#include<ctype.h>
#define SIZE 5
int s[100];
int top=-1;
void push(int x)
{
if(top==SIZE-1)
{
printf("\n Overflow");
}
else
{
top++;
s[top]=x;
}
}
void pop()
{
if(top==-1)
{
printf("\n Underflow");
}
else
{
printf("\n Deleted item is %d",s[top]);
top--;
}
}
void peek()
{
if(top==-1)
{
printf("\n Underflow");
}
else
{
printf("\n top of the stack is %d",s[top]);
}
}
void display()
{
int i;
if(top==-1)
{
printf("\n Stack is empty");
}
else
{
for(i=top;i>=0;i--)
{
printf("\n %d",s[i]);
}
}
}
void main()
{
int ch,x;
while(1)
{
printf("\n 1. Push \n 2. Pop \n 3. Peek \n 4. Display \n 5.
Exit\n");
printf("\n Enter your choice:");
scanf("%d",&ch);
switch(ch)
{
case 1: printf("\n Enter the item to be inserted into the
stack:");
scanf("%d",&x);
push(x);
break;
case 2: pop();
break;
case 3: peek();
break;
case 4: display();
break;
case 5: exit(0);
default : printf("\n Enter correct choice");
}
}
}
i++;
}
printf("\n Result is %d",s[top]);
return 0;
}
Recursion
Definition: A recursion is defined as a function that calls itself.
Every recursive solution has two major cases. They are
1. Base case, in which the problem is simple enough to be solved directly
without making any further calls to the same function.
2. Recursive case, in which first the problem at hand is divided into
simpler sub-parts. Second the function calls itself but with sub-parts of
the problem obtained in the first step. Third, the result is obtained by
combining the solutions of simpler sub-parts.
Types of Recursions
1. Direct recursion
2. Indirect recursion
3. Tail recursion
Here we have to transfer all the disks from source peg X to the destination
peg Z by using an intermediate peg Y. Following are the rules to be followed
during transfer:
1. Transferring the disks from the source peg to the destination peg such that
at any point of transformation no large size disk is placed on the smaller
one.
2. Only one disk may be moved at a time.
3. Each disk must be stacked on any one of the pegs.
Algorithm
• Base case: if n=1
• Move the ring from A to C using B as spare
• Recursive case:
• Move n – 1 rings from A to B using C as spare
• Move the one ring left on A to C using B as spare
• Move n – 1 rings from B to C using A as spare
Towers of Hanoi Program
#include <stdio.h>
void towerOfHanoi(int n, char from_rod, char to_rod, char aux_rod)
{
if (n == 1)
{
printf("\n Move disk 1 from rod %c to rod %c", from_rod, to_rod);
}
else
{
towerOfHanoi(n-1, from_rod, aux_rod, to_rod);
printf("\n Move disk %d from rod %c to rod %c", n, from_rod,
to_rod);
towerOfHanoi(n-1, aux_rod, to_rod, from_rod);
}
}
int main()
{
int n = 4; // Number of disks
towerOfHanoi(n, 'A', 'C', 'B'); // A, B and C are names of rods
return 0;
}
Important Question:
Short Answer questions
1. Define time and space complexity.
2. What are the asymptotic notations.
3. Define BigO, Omega and Theta notations.
4. What is time-space trade-off?
5. Explain types of time-space trade-off.
6. What are the advantages and disadvantages of linear and Binary
search.
7. What are the time complexities of linear and binary search.
8. Define stack.
9. Define ADT
10. Give stack ADT.
11. What are the applications of stack.
12. What is recursion? Explain its disadvantages.
13. explain recursion types.
14. What is infix, prefix and postfix expression.
Uses of Queue:
1. Railway reservation
2. Cinema ticket booking
3. Operating systems
QUEUE ADT
• enqueue() – Insert an element at the end of the queue.
• dequeue() – Remove and return the first element of the queue, if the
queue is not empty.
• peek() – Return the element of the queue without removing it, if the
queue is not empty.
• size() – Return the number of elements in the queue.
• isEmpty() – Return true if the queue is empty, otherwise return false.
• isFull() – Return true if the queue is full, otherwise return false.
Types of Queues
1. Simple queue
2. Circular queue
3. Double ended queue
Simple Queue
Basically, queue has two operations. 1. Enqueue 2. Dequeue
1. Enqueue: Inserting an element into the queue is called enqueue.
2. Dequeue: Deleting / removing an element from the queue is called
dequeue.
Enqueue
Step 1 - Check whether queue is FULL. (rear == SIZE-1)
Step 2 - If it is FULL, then display "Queue is FULL!!! Insertion is not
possible!!!" and terminate the function.
Step 3 - If it is NOT FULL, then increment rear value by one (rear++)
and set queue[rear] = value.
Dequeue
Step 1 - Check whether queue is EMPTY. (front = rear=-1)
Step 2 - If it is EMPTY, then display "Queue is EMPTY!!! Deletion is not
possible!!!" and terminate the function.
Step 3 - If it is NOT EMPTY, then increment the front value by one (front
++). Then display queue[front] as deleted element. Then check whether
both front and rear are equal (front == rear), if it TRUE, then set both
front and rear to '-1' (front = rear = -1).
Implementing queue using arrays
#include <stdio.h>
#include <stdlib.h>
#define size 5
int Q[size];
int f=-1,r=-1;
void enqueue(int x)
{
if(r==size-1)
{
printf("\n overflow");
}
else if(r==-1&&f==-1)
{
r = r+1;
f=f+1;
Q[r]=x;
}
else
{
r=r+1;
Q[r]=x;
}
void dequeue()
{
if(f==-1)
{
printf("underflow\n");
}
else if(f==r)
{
printf("\n The deleted element is %d",Q[f]);
r=-1;
f=-1;
}
else
{
printf("\n The deleted element is %d",Q[f]);
f=f+1;
}
void peek()
{
if(f==-1)
{
printf("underflow\n");
return 0;
}
else
{
printf("\n Peek element is %d",Q[f]);
void display()
{
int i;
if(r==-1)
{
printf("\n Queue is empty\n");
}
else
{
for(i=f;i<=r;i++)
{
printf("%d\n",Q[i]);
}
}
}
int ch,x;
while(1)
{
printf("\n 1. Insert");
printf("\n 2. Delete");
printf("\n 3. Display ");
printf("\n 4. Peek ");
printf("\n 5. exit");
printf("\n Enter your choice\n");
scanf("%d",&ch);
switch(ch)
{
case 1: printf("\n insert a value\n");
scanf("%d",&x);
enqueue(x);
break;
case 2: dequeue();
break;
case 3: display();
break;
case 4: peek();
break;
case 5: exit(0);
default: printf("\n Enter correct choice\n");
}
}
return 0;
}
Drawback of Simple queue
The above situation says that queue is full, and we cannot insert an element
into the queue because rear is pointing to last position. In the above situation
even though we have empty position in the queue, but we cannot use of them
to insert the new element.
To overcome the above the problem we use circular queue.
Circular Queue
Circular queue is a linear data structure in which operations are performed based
on FIFO ( FIRST IN FIRST OUT) principle and the last position is connected back
to the first position to make a circle.
void enqueue(int x)
{
if((rear+1)%size== front)
{
printf("\n Overflow");
}
else if(front==-1&& rear==-1)
{
front =front+1;
rear = rear +1;
CQ[rear]= x;
}
else
{
rear = (rear + 1)%size;
CQ[rear] = x;
}
int dequeue( )
{
int x;
if(front==-1 && rear == -1)
{
printf("\n Underflow \n");
return 0;
}
else if(front == rear)
{
x = CQ[front];
front =-1;
rear = -1;
}
else
{
x = CQ[front];
front = (front+1)%size;
}
return x;
}
void display()
{
int i;
for(i=front; i!=rear;i=(i+1)%size)
{
printf("%d\n",CQ[i]);
}
printf("%d\n",CQ[i]);
}
Operations on deque:
#include <stdio.h>
#include <stdlib.h>
#define size 10
int Q[size];
int f=-1,r=-1;
void insert_rear(int x)
{
if((r+1)%size==f)
{
printf("\n Overflow");
}
else if(f==-1)
{
f=0;
r=0;
Q[r]=x;
}
else
{
r=(r+1)%size;
Q[r]=x;
}
}
void insert_front(int x)
{
if((r+1)%size==f)
{
printf("\n Overflow");
}
else if(f==0)
{
f=size-1;
Q[f] = x;
}
else
{
f=f-1;
Q[f]=x;
}
}
int delete_front()
{
int x;
if(f==-1)
{
printf("\n Underflow\n");
return 0;
}
else if(f==r)
{
printf(“\n Deleted element is %d”,Q[f]);
f=-1;
r=-1;
}
else
{
printf(“\n Deleted element is %d”,Q[f]);
f = (f+1)%size;
}
}
int delete_rear()
{
int x;
if(f==-1)
{
printf("\n Underflow\n");
return 0;
}
else if(f==r)
{
Printf(“\n Deleted element is %d”,Q[r]);
f=-1;
r=-1;
}
else
{
Printf(“\n Deleted element is %d”,Q[r]);
if(r==0)
r=size-1;
else
r = r-1;
}
}
void display()
{
int i;
if(f==-1)
{
printf("\n Queue is empty\n");
}
else
{
for(i=f;i!=r;i=(i+1)%size)
{
printf("%d\n",Q[i]);
}
printf("%d\n",Q[i]);
}
}
int main(int argc, char *argv[]) {
int ch,x;
while(1)
{
printf("\n 1. Insert rear\n2. Insert front \n 3. delete front \n4.
delete rear\n5. display\n6. exit\n Enter your choice\n");
scanf("%d",&ch);
switch(ch)
{
case 1: printf("\n Enter your choice\n");
scanf("%d",&x);
insert_rear(x);
break;
case 2: printf("\n Enter your choice\n");
scanf("%d",&x);
insert_front(x);
break;\
case 3: delete_front();
break;
case 4: delete_rear();
break;
case 5: display();
break;
case 6: exit(0);
default: printf("\n Enter correct choice\n");
}
}
return 0;
}
Applications of Queue
1. Draw the queue structure in each case when the following operations are
performed on an empty queue.
2. Consider the queue given below which has FRONT = 1 and REAR = 5.
Linked list is a data structure that is free from the aforementioned restrictions.
A linked list does not store its elements in consecutive memory locations and the
user can add any number of elements to it.
Unlike an array, a linked list does not allow random access of data.
Elements in a linked list can be accessed only in a sequential manner.
In a linked list, every node contains a pointer to another node which is of the
same type, hence it is also called a self-referential data type.
Node structure
struct node
Int data;
}
Visual representation of a Node
Static representation
In static representation of a single linked list, two arrays are maintained: one
array for data and the other for links. The linked list and its static representation
using arrays are shown in figures.
Two parallel arrays of equal size are allocated which should be sufficient to store
the entire linked list. Nevertheless, this contradicts the idea of the linked list
(that is non-contagious location of elements). Hence it is not an efficient method.
Linked list representation using arrays
Dynamic representation
The efficient way of representing a linked list is using the free pool of storage.
In this method, there is a memory bank (which is nothing but a collection of free
memory spaces) and a memory manager (a program, in fact). During the
creation of a linked list, whenever a node is required, the request is placed to the
memory manager; the memory manager will then search the memory bank for
the block requested and, if found, grants the desired block to the caller. Again,
there is also another program called the garbage collector; it plays whenever
a node is no more in use; it returns the unused node to the memory bank.
Operations on a single linked list
1. Inserting a node at the beginning of a single linked list
2. Inserting a node at the end of the single linked list
3. Inserting a node at any position
4. Deleting a node from the beginning of the single linked list
5. Deleting a node from the end of the single linked list
6. Deleting a node from any position in the linked list.
7. Traversing linked list (display linked list elements).
8. Searching a node in the linked list
9. Sorting the list elements
10. Reverse linked list elements.
Global declarations
typedef struct linkedlist
{
int data;
struct linkedlist *next;
}node;
node *head=NULL;
1. Inserting a mode at the beginning of the single linked list
Code:
void insert_begin()
{
int x;
node *temp;
temp=(node*)malloc(sizeof(node));
if(temp==NULL)
{
printf("\n Insufficient Memory\n");
}
else
{
printf("\n Enter a value\n");
scanf("%d",&x);
temp->data = x;
temp->next = NULL;
if(head==NULL)
{
head=temp;
}
else
{
temp->next = head;
head=temp;
}
}
}
2. Inserting a node at the end of the single linked list
void insert_end()
{
int x;
node *temp,*temp1;
temp = (node*)malloc(sizeof(node));
if(temp==NULL)
{
printf("\n Insufficient Memory\n");
}
else
{
printf("\n Enter a value\n");
scanf("%d",&x);
temp->data=x;
temp->next=NULL;
if(head==NULL)
{
head=temp;
}
else
{
for(temp1=head;temp1->next!=NULL;temp1=temp1-
>next);
temp1->next=temp;
}
}
}
3. Inserting a node at any position in the single linked list
void insert_pos()
{
int x,p,n; //p represents the position and n represents no of nodes
in the linked list
node *temp,*temp1;
printf("\n Enter position:");
scanf("%d",&p);
for(temp1=head,n=1;temp1->next!=NULL;temp1=temp1->next,n++);
if(p==1)
{
insert_begin();
}
else if(p==n+1)
{
insert_end();
}
else if(p > n+1||p<1)
{
printf("\n Insertion is not possible\n");
}
else
{
temp=(node*)malloc(sizeof(node));
if(temp==NULL)
{
printf("\n Insufficient Memory\n");
}
else
{
for(temp1=head,n=1;n<p-1;n++,temp1=temp1->next);
printf("\n Enter a value\n");
scanf("%d",&x);
temp->data = x;
//temp->next = NULL;
temp->next = temp1->next;
temp1->next = temp;
}
}
}
if(head==NULL)
{
printf("\n List is empty\n");
}
else
{
if(head->next==NULL)
{
temp=head;
printf("\n Deleted node is %d\n",temp->data);
head=NULL;
free(temp);
}
else
{
for(temp1=head;temp1->next->next!=NULL;temp1=temp1-
>next);
temp=temp1->next;
temp1->next=NULL;
printf("\n Deleted node is %d\n",temp->data);
free(temp);
}
}
}
6. Deleting a node from any position of the linked list
void delete_pos()
{
node *temp,*temp1;
int i, pos, count=0;
printf("\n Enter position:");
scanf("%d",&pos);
for(temp1=head;temp1!=NULL;temp1=temp1->next, count++);
if(pos==1)
{
delete_begin();
}
else if(pos==count)
{
delete_end();
}
else if(pos<1||pos>count)
{
printf("\n Deletion is not possible");
}
else
{
for(i=2,temp1=head; i<pos && i<count;i++,temp1=temp1->next);
temp=temp1->next;
temp1->next = temp->next;
printf("\n Deleted node is %d \n", temp->data);
free(temp);
}
}
7. Traversing linked list (display linked list elements)
void display()
{
node *temp;
if(head==NULL)
{
printf("\n List is empty\n");
}
else
{
temp = head;
for(temp=head;temp!=NULL;temp=temp->next)
{
printf("%d\n",temp->data);
}
}
}
8. Searching a node in the linked list
void search()
{
int key,found=0;
node *temp;
printf("\n Enter the key elements:");
scanf("%d",&key);
temp=head;
if(temp==NULL)
{
printf("\n List is empty");
}
else
{
for(;temp!=NULL; temp=temp->next)
{
if(key==temp->data)
{
found=1;
break;
}
}
if(found==1)
{
printf("\n %d is found in the list",key);
}
else
{
printf("\n %d is not found in the list",key);
}
}
}
In case TOP!=NULL, then we will delete the node pointed by TOP, and make
TOP point to the second element of the linked stack. Thus, the updated
stack becomes as shown in Figure.
Algorithm:
Step 1: IF TOP = NULL PRINT UNDERFLOW [END OF IF]
Step 2: SET PTR = TOP
Step 3: SET TOP = TOP NEXT
Step 4: FREE PTR
Step 5: END
C Code: (which is similar to deleting a node from the beginning of the single linked
list).
void delete_begin()
{
node *temp;
if(top ==NULL)
{
printf("\n Linked List is empty\n");
}
else
{
temp= top;
printf("\n Deleted node is %d \n",temp->data);
top=head->next;
free(temp);
}
}
C Program to implement stack using linked list
#include<stdio.h>
#include<stdlib.h>
typedef struct linkedlist
{
int data;
struct linkedlist *next;
}node;
node *top=NULL;
void push()
{
int x;
node *temp;
temp=(node*)malloc(sizeof(node));
if(temp==NULL)
{
printf("\n Insufficient Memory\n");
}
else
{
printf("\n Enter a value\n");
scanf("%d",&x);
temp->data = x;
temp->next = NULL;
if(top==NULL)
{
top=temp;
}
else
{
temp->next = top;
top=temp;
}
}
}
void pop()
{
node *temp;
if(top ==NULL)
{
printf("\n Linked List is empty\n");
}
else
{
temp= top;
printf("\n Deleted node is %d \n",temp->data);
top=top->next;
free(temp);
}
}
void display()
{
node *temp;
if(top ==NULL)
{
printf("List is empty\n");
}
else
{
for(temp= top;temp!=NULL;temp=temp->next)
{
printf("%d\n",temp->data);
}
}
}
void main()
{
int x;
while(1)
{
printf("[Link]\[Link]\[Link]\[Link]\n");
printf("Enter your choice\n");
scanf("%d",&x);
switch(x)
{
case 1:push();break;
case 2:pop();break;
case 3:display();break;
case 4:exit(0);
default:printf("Enter correct choice\n");
}
}
}
Queue implementation using linked list
A queue has basically two operations 1. Enqueue 2. Dequeue
Enqueue: inserting an element into the queue
Dequeue: Deleting an element from the queue.
Enqueue:
The enqueue operation is used to insert an element into a queue. The new
element is added as the last element of the queue. Consider the linked
queue shown in Figure.
Algorithm
Step 1: Allocate memory for the new node and name it as PTR
Step 2: SET PTR DATA = VAL
Step 3: IF FRONT = NULL
SET FRONT = REAR = PTR
SET FRONT->NEXT = REAR->NEXT = NULL
ELSE
SET REAR->NEXT = PTR
SET REAR = PTR
SET REAR->NEXT = NULL
[END OF IF]
Step 4: END
Dequeue: The delete operation is used to delete the element that is first
inserted in a queue, i.e., the element whose address is stored in FRONT.
However, before deleting the value, we must first check if FRONT=NULL
because if this is the case, then the queue is empty and no more deletions can
be done. If an attempt is made to delete a value from a queue that is already
empty, an underflow message is printed. Consider the queue shown in Figure.
Algorithm
Step 1: IF FRONT = NULL
Write “Underflow”
Go to Step 5
[END OF IF]
Step 2: SET PTR = FRONT
Step 3: SET FRONT = FRONT NEXT
Step 4: FREE PTR
Step 5: END
C Program to implement queue using linked list
//Queue using linked list
#include<stdio.h>
#include<stdlib.h>
void enqueue()
{
node *temp;
int x;
temp = (node *)malloc(sizeof(node));
if(temp==NULL)
{
printf("Insufficient Memory\n");
}
else
{
printf("Enter a number : ");
scanf("%d", &x);
temp->data = x;
temp->next = NULL;
if(rear==NULL)
{
front = temp;
rear = temp;
}
else
{
rear->next = temp;
rear = temp;
}
}
}
void dequeue()
{
node *temp;
if(front==NULL)
{
printf("Queue Underflow\n");
}
else
{
temp = front;
if(front->next==NULL) //Checking for one node
{
rear = NULL;
}
front = temp->next;
printf("Deleted %d\n", temp->data);
free(temp);
}
}
void display()
{
node *temp;
if(rear==NULL)
{
printf("Queue is empty\n");
}
else
{
printf("Queue elements are");
for(temp = front; temp!=rear; temp = temp->next)
{
printf("\n%d", temp->data);
}
printf("\n%d\n", rear->data);
}
}
void main()
{
int ch;
while(1)
{
printf("\nOperations\n1. Enqueue\n2. Dequeue\n3. Display\n4.
Exit\n");
printf("\nEnter your choice : ");
scanf("%d", &ch);
switch(ch)
{
case 1: enqueue();
break;
case 2: dequeue();
break;
case 3: display();
break;
case 4: printf("\n");
exit(0);
default: printf("\nInvalid Choice\n");
}
}
}
Note: This feature makes it circular in nature. It is essential to know that the
circular linked lists have no end, and we need to be careful while traversing
the linked list.
Operations performed on a circular linked list are
1. Inserting a node at the beginning of a circular linked list
2. Inserting a node at the end of the circular linked list
3. Inserting a node at any position in the circular linked list
4. Deleting a node from the beginning of the circular linked list
5. Deleting a node from the end of the circular linked list
6. Deleting a node from any position in the circular linked list.
7. Traversing linked list (display linked list elements).
void insert_begin()
{
node *temp, *temp1;
int x;
temp = (node *)malloc(sizeof(node));
if(temp==NULL)
{
printf("Insufficient Memory\n");
}
else
{
printf("Enter a number : ");
scanf("%d", &x);
temp->data = x;
temp->next = NULL;
if(head==NULL)
{
head = temp;
head->next = head;
}
else
{
temp->next = head;
for(temp1=head;temp1->next!=head;temp1=temp1->next);
temp1->next = temp;
head = temp;
}
}
}
void insert_end()
{
node *temp, *temp1;
int x;
temp = (node *)malloc(sizeof(node));
if(temp==NULL)
{
printf("Insufficient Memory\n");
}
else
{
printf("Enter a number : ");
scanf("%d", &x);
temp->data = x;
temp->next = NULL;
if(head==NULL)
{
head = temp;
head->next = head;
}
else
{
temp->next = head;
for(temp1=head;temp1->next!=head;temp1=temp1->next);
temp1->next = temp;
}
}
}
void insert_pos()
{
node *temp, *temp1;
int x, pos, count, i;
printf("Enter a position : ");
scanf("%d", &pos);
for(count=1, temp1 = head; temp1->next!=head; temp1=temp1->next,count++);
if(head==NULL)
{
count--;
}
if(pos<1||pos>count+1)
{
printf("Insertion not possible\n");
}
else if(pos==1)
{
insert_begin();
}
else if(pos==count+1)
{
insert_end();
}
else
{
temp = (node *)malloc(sizeof(node));
if(temp==NULL)
{
printf("Insufficient Memory\n");
}
else
{
printf("Enter a number : ");
scanf("%d", &x);
temp->data = x;
temp->next = NULL;
temp1 = head;
i = 2;
while(i<pos)
{
temp1 = temp1->next;
i++;
}
temp->next = temp1->next;
temp1->next = temp;
}
}
}
void delete_begin()
{
node *temp, *temp1;
if(head==NULL)
{
printf("Underflow\n");
}
else if(head->next==head)
{
temp = head;
printf("Deleted %d\n", temp->data);
head = NULL;
free(temp);
}
else
{
for(temp1=head;temp1->next!=head;temp1=temp1->next);
temp = head;
head = head->next;
temp1->next = head;
printf("Deleted %d\n", temp->data);
free(temp);
}
}
void delete_end()
{
node *temp, *temp1;
if(head==NULL)
{
printf("Underflow\n");
}
else if(head->next==head)
{
temp = head;
printf("Deleted %d\n", temp->data);
head = NULL;
free(temp);
}
else
{
for(temp1=head;(temp1->next)->next!=head;temp1=temp1->next);
temp = temp1->next;
temp1->next = head;
printf("Deleted %d\n", temp->data);
free(temp);
}
}
void delete_pos()
{
node *temp, *temp1;
int count, pos, i;
if(head==NULL)
{
printf("Underflow\n");
}
else
{
for(temp=head,count=1;temp->next!=head;temp=temp->next,count++);
printf("Enter a position : ");
scanf("%d", &pos);
if(pos<1||pos>count)
{
printf("Deletion is not possible\n");
}
else if(pos==1)
{
delete_begin();
}
else if(pos==count)
{
delete_end();
}
else
{
temp = head;
i = 2;
while(i<pos)
{
temp = temp->next;
i++;
}
temp1 = temp->next;
temp->next = temp1->next;
printf("Deleted %d\n", temp1->data);
free(temp1);
}
}
}
void display()
{
node *temp;
if(head==NULL)
{
printf("The List is empty\n");
}
else
{
printf("The list elements are\n");
for(temp=head;temp->next!=head;temp=temp->next)
{
printf("%d\n", temp->data);
}
printf("%d\n", temp->data);
}
}
void search()
{
node *temp;
int key, f=0;
if(head==NULL)
{
printf("List is empty\n");
}
else
{
printf("Enter key : ");
scanf("%d", &key);
temp = head;
while(temp->next!=head)
{
if(temp->data==key)
{
f=1;
break;
}
temp = temp->next;
}
if(temp->data==key)
{
f=1;
}
if(f==1)
{
printf("Search is successful\n");
}
else
{
printf("Search is unsuccessful\n");
}
}
}
void main()
{
int ch;
while(1)
{
printf("\nOperations\n");
printf("1. Insert Begin\n2. Insert End\n3. Insert Position\n");
printf("4. Delete Begin\n5. Delete End\n6. Delete Position\n");
printf("7. Display\n8. Search\n9. Exit\n");
printf("\nEnter your choice : ");
scanf("%d", &ch);
switch(ch)
{
case 1: insert_begin();
break;
case 2: insert_end();
break;
case 3: insert_pos();
break;
case 4: delete_begin();
break;
case 5: delete_end();
break;
case 6: delete_pos();
break;
case 7: display();
break;
case 8: search();
break;
case 9: printf("\n");
exit(0);
default: printf("Invalid Choice\n");
}
}
}
Drawback of circular linked list: The main drawback of circular linked list
is it cannot traverse in a reverse direction. To overcome this drawback
through the double linked list.
Double linked list
A doubly linked list or a two-way linked list is a more complex type of linked list
which contains a pointer to the next as well as the previous node in the sequence.
Therefore, it consists of three parts—data, a pointer to the next node, and a
pointer to the previous node as shown in Figure.
void insert_begin()
{
int x;
dnode *temp;
temp=(dnode*)malloc(sizeof(dnode));
if(temp==NULL)
{
printf("\n Insufficient memory");
}
else
{
printf("\n Enter a value:");
scanf("%d",&x);
temp->data=x;
temp->next=NULL;
temp->prev=NULL;
if(head==NULL)
{
head=temp;
}
else
{
temp->next=head;
head->prev=temp;
head = temp;
}
}
}
void insert_end()
{
int x;
dnode *temp,*last;
temp=(dnode*)malloc(sizeof(dnode));
if(temp==NULL)
{
printf("\n Insufficient memory");
}
else
{
printf("\n Enter a value:");
scanf("%d",&x);
temp->data=x;
temp->next=NULL;
temp->prev=NULL;
if(head==NULL)
{
head=temp;
}
else
{
for(last=head;last->next!=NULL;last=last->next);
last->next=temp;
temp->prev=last;
}
}
}
void insert_pos()
{
dnode *temp,*temp1;
int count,pos,x,i;
for(count=0,temp=head;temp!=NULL;temp=temp->next,count++);
printf("\n Enter position:");
scanf("%d",&pos);
if(pos==1)
{
insert_begin();
}
else if(pos==count+1)
{
insert_end();
}
else
{
temp=(dnode*)malloc(sizeof(dnode));
if(temp!=NULL)
{
printf("\n Enter a value:");
scanf("%d",&x);
temp->data=x;
temp->next=NULL;
temp->prev=NULL;
i=2;
temp1=head;
while(i<pos)
{
temp1=temp1->next;
i++;
}
temp->next=temp1->next;
temp1->next->prev = temp;
temp1->next = temp;
temp->prev = temp1;
}
}
}
void delete_begin()
{
dnode *temp;
if(head==NULL)
{
printf("\n List is empty");
}
else
{
if(head->next==NULL)
{
temp=head;
printf("\n deleted node is %d",temp->data);
head=NULL;
free(temp);
}
else
{
temp=head;
printf("\n deleted node is %d",temp->data);
head=head->next;
head->prev=NULL;
free(temp);
}
}
}
void delete_end()
{
dnode *temp,*temp1;
if(head==NULL)
{
printf("\n List is empty");
}
else
{
if(head->next==NULL)
{
temp=head;
printf("\n deleted node is %d",temp->data);
head=NULL;
free(temp);
}
else
{
for(temp1=head;temp1->next->next!=NULL;temp1=temp1-
>next);
temp=temp1->next;
printf("\n deleted node is %d",temp->data);
temp1->next=NULL;
free(temp);
}
}
}
void delete_pos()
{
dnode *temp,*temp1;
int pos,count,i;
for(count=0,temp=head;temp!=NULL;temp=temp->next,count++);
printf("\n Enter position:");
scanf("%d",&pos);
if(pos==1)
{
delete_begin();
}
else if(pos==count)
{
delete_end();
}
else
{
i=2;
temp1=head;
while(i<pos)
{
temp1=temp1->next;
i++;
}
temp=temp1->next;
printf("\n Deleted element is %d",temp->data);
temp1->next=temp->next;
temp1->next->prev = temp->prev;
free(temp);
}
}
void display_front()
{
dnode *temp;
if(head==NULL)
{
printf("\n List is Empty");
}
else
{
printf("\n");
for(temp=head;temp!=NULL;temp=temp->next)
{
printf(" %d-->",temp->data);
}
printf("\n");
}
}
void display_last()
{
dnode *last;
if(head==NULL)
{
printf("\n List is empty");
}
else
{
for(last=head;last->next!=NULL;last=last->next);
printf("\n");
do
{
printf(" %d-->",last->data);
last=last->prev;
}while(last!=NULL);
printf("\n");
}
}
void main()
{
int ch;
while(1)
{
printf("\n 1. Insert begin \n 2. Insert End \n 3. Insert any position
\n 4. Delete Begin \n 5. Delete End\n 6. Delete Position \n 7. Display from
front \n 8. Display from last \n 9. exit\n");
printf("\n Enter your choice\n");
scanf("%d",&ch);
switch(ch)
{
case 1: insert_begin();
break;
case 2: insert_end();
break;
case 3: insert_pos();
break;
case 4: delete_begin();
break;
case 5: delete_end();
break;
case 6: delete_pos();
break;
case 7: display_front();
break;
case 8: display_last();
break;
case 9: exit(0);
default: printf("\n Enter correct choice\n");
}
}
}
while(temp1!=NULL)
{
temp3->next = temp1;
temp1=temp1->next;
temp3=temp3->next;
}
while(temp2!=NULL)
{
temp3->next = temp2;
temp2=temp2->next;
temp3=temp3->next;
}
temp3->next=NULL;
return head;
}
while(s!=NULL&&f!=NULL)
{
if(head==NULL)
{
head=(poly*)malloc(sizeof(poly));
temp=head;
}
else
{
temp->next = (poly*)malloc(sizeof(poly));
temp=temp->next;
}
while(f!=NULL)
{
temp->next = (poly*)malloc(sizeof(poly));
temp=temp->next;
temp->coef= f->coef;
temp->expo =f->expo;
f=f->next;
}
while(s!=NULL)
{
temp->next = (poly*)malloc(sizeof(poly));
temp=temp->next;
temp->coef= s->coef;
temp->expo =s->expo;
s=s->next;
}
temp->next=NULL;
return head;
}