UNIT-I DS Notes
UNIT-I DS Notes
UNIT-I
What is Data Structure
In computer terms, a data structure is a Specific way to store and organize data
in a computer's memory so that these data can be used efficiently later. Data
may be arranged in many different ways such as the logical or mathematical
model for a particular organization of data is termed as a data structure.
• A data structure is said to be linear if its elements combine to form any specific
order. There are two techniques of representing such linear structure within
memory.
• The first way is to provide the linear relationships among all the elements
represented using linear memory location. These linear structures are termed as
arrays.
• Arrays
• Queues
• Stacks
• Linked lists
This structure is mostly used for representing data that contains a hierarchical
relationship among various elements.
• Graphs
• table of contents
Tree: In this case, data often contain a hierarchical relationship among various
elements. The data structure that reflects this relationship is termed as a rooted
tree graph or a tree.
Graph: In this case, data sometimes hold a relationship between the pairs of
elements which is not necessarily following the hierarchical structure. Such a
data structure is termed as a Graph
The user of data type does not need to know how that data type is implemented,
for example, we have been using Primitive values like int, float, char data types
only with the knowledge that these data type can operate and be performed on
without any idea of how they are implemented. So a user only needs to know
what a data type can do, but not how it will be implemented. Think of ADT as a
black box which hides the inner structure and design of the data type. Now
we’ll define three ADTs namely List ADT, Stack ADT, Queue ADT.
List ADT
A list contains elements of the same type arranged in sequential order and
following operations can be performed on the list.
remove() – Remove the first occurrence of any element from a non-empty list.
Stack ADT
A Stack contains elements of the same type arranged in sequential order. All
operations take place at a single end that is top of the stack and following
operations can be performed:
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.
Queue ADT
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.
From these definitions, we can clearly see that the definitions do not specify
how these ADTs will be represented and how the operations will be carried out.
There can be different ways to implement an ADT, for example, the List ADT
can be implemented using arrays, or singly linked list or doubly linked list.
Similarly, stack ADT and Queue ADT can be implemented using arrays or
linked lists.
Singly linked list is a basic linked list type. Singly linked list is a collection of
nodes linked together in a sequential way where each node of singly linked list
contains a data field and an address field which contains the reference of the
next node. Singly linked list can contain multiple data fields but should contain
at least single address field pointing to its connected next node.
To perform any operation on a linked list we must keep track/reference of the
first node which may be referred by head pointer variable. In singly linked list
address field of last node must contain a NULL value specifying end of the list.
Each node of a singly linked list follows a common basic structure. In a node
we can store more than one data fields but we need at least single address field
to store the address of next connected node.
struct node {
int data; // Data
struct node * next; // Address
};
There are several points about singly linked list that makes it an important data
structure.
Singly linked list is probably the most easiest data structure to implement.
Insertion and deletion of element can be done easily.
Insertion and deletion of elements doesn't requires movement of all
elements when compared to an array.
Requires less memory when compared to doubly, circular or doubly
circular linked list.
Can allocate or deallocate memory easily when required during its
execution.
It is one of most efficient data structure to implement when traversing in
one direction is required.
After seeing the advantages of singly linked list. Singly linked list also has some
disadvantages over other data structures.
1. The first step of creating linked list of n nodes starts from defining node
structure. We need a custom type to store our data and location of next
linked node. Let us define our custom node structure
2. struct node {
3. int data;
4. struct node *next;
};
Where data is the data you want to store in list. *next is pointer to the
same structure type. The *next will store location of next node if exists
otherwise NULL.
Note: The node structure may vary based on your requirement. You can
also have user defined types as node data section.
5. Declare a pointer to node type variable to store link of first node of linked
list. Say struct node *head;.
Note: You can also declare variable of node type along with node
structure definition.
6. Input number of nodes to create from user, store it in some variable say n.
7. Declare two more helper variable of node type, say struct node
*newNode, *temp;.
8. If n > 0 then, create our first node i.e. head node. Use dynamic memory
allocation to allocate memory for a node. Say head = (struct
node*)malloc(sizeof(struct node));.
9. If there is no memory to allocate for head node i.e. head == NULL. Then
print some error message and terminate program, otherwise move to
below step.
10.Input data from user and assign to head using head->data = data;.
11.At first head node points to NULL. Hence, assign head->next = NULL;.
12.Now, we are done with head node we should move to creation of other
nodes. Copy reference of head to some other temporary variable, say
temp = head;. We will use temp to store reference of previous node.
13.Allocate memory and assign memory reference to newNode, say
newNode = (struct node*)malloc(sizeof(node));.
14.If memory got allocated successfully then read data from user and assign
to data section of new node. Say newNode->data = data;.
15.Make sure new node points to NULL.
16.Now link previous node with newly created node i.e. temp->next =
newNode;.
17.Make current node as previous node using temp = temp->next;.
18.Repeat step 10-14 for remaining n - 2 other nodes.
How to insert a node in the beginning of the singly linked list. Algorithm to
insert a node at the beginning of Singly linked list. Steps to insert a new node at
the start of a singly linked list.
1. Create a new node, say newNode points to the newly created node.
2. Link the newly created node with the head node, i.e. the newNode will
now point to head node.
3. Make the new node as the head node, i.e. now head node will point to
newNode.
Algorithm to insert node at the end of singly linked list. Steps to insert a new
node at the end of singly linked list.
1. Create a new node and make sure that the address part of the new node
points to NULL i.e. newNode->next=NULL
2. Traverse to the last node of the linked list and connect the last node of the
list with the new node, i.e. last node will now point to new node.
(lastNode->next = newNode).
2. Traverse to the n-1th position of the linked list and connect the new node
with the n+1th node. Means the new node should also point to the same
node that the n-1th node is pointing to. (newNode->next = temp->next
where temp is the n-1th node).
3. Now at last connect the n-1th node with the new node i.e. the n-1th node
will now point to new node. (temp->next = newNode where temp is the
n-1th node).
Algorithm to delete first node from singly linked list in C. Steps to delete first
node from singly linked list.
Algorithm to delete first node from Singly Linked List
1. Copy the address of first node i.e. head node to some temp variable say
toDelete.
2. Move the head to the second node of the linked list i.e. head = head-
>next.
Algorithm to delete last node from a singly linked list. Steps to remove last
node from singly linked list.
1. Traverse to the last node of the linked list keeping track of the second last
node in some temp variable say secondLastNode.
2. If the last node is the head node then make the head node as NULL else
disconnect the second last node with the last node i.e. secondLastNode-
>next = NULL.
3. Free the memory occupied by the last node.
Algorithm to delete middle node from singly linked list. Steps to delete middle
node from singly linked list.
1. Traverse to the nth node of the singly linked list and also keep reference of
n-1th node in some temp variable say prevnode.
2. Reconnect the n-1th node with the n+1th node i.e. prevNode->next =
toDelete->next (Where prevNode is n-1th node and toDelete node is the
nth node and toDelete->next is the n+1th node).
3. Free the memory occupied by the nth node i.e. toDelete node.
Search an element in linked list is fairly similar to how you search an element in
arrays. Here we will learn to perform linear search on singly linked list.
Dou
bly Linked List
Since doubly linked list allows the traversal of nodes in both direction hence we
can keep track of both first and last nodes.
The basic structure of a doubly linked list contains a data field and two address
fields. Here is how it can be represented in C programming language.
struct node {
int data; // Data field
struct node * prev; // Address of previous node
struct node * next; // Address of next node
};
Doubly linked list is one of the important data structures. Here are various
advantages of doubly linked list.
As like singly linked list it is the easiest data structures to implement.
Allows traversal of nodes in both direction which is not possible in singly
linked list.
Deletion of nodes is easy when compared to singly linked list, as in singly
linked list deletion requires a pointer to the node and previous node to be
deleted. Which is not in case of doubly linked list we only need the
pointer which is to be deleted.
Reversing the list is simple and straightforward.
Can allocate or de-allocate memory easily when required during its
execution.
It is one of most efficient data structure to implement when traversing in
both direction is required.
Not many but doubly linked list has few disadvantages also which can be listed
below:
It uses extra memory when compared to array and singly linked list.
Since elements in memory are stored randomly, hence elements are
accessed sequentially no direct access is allowed.
There are various application of doubly linked list in the real world. Some of
them can be listed as:
Doubly linked list can be used in navigation systems where both front
and back navigation is required.
It is used by browsers to implement backward and forward navigation of
visited web pages i.e. back and forward button.
It is also used by various application to implement Undo and Redo
functionality.
It can also be used to represent deck of cards in games.
It is also used to represent various states of a game.
1. Create a head node and assign some data to its data field.
2. Make sure that the previous and next address field of the head node must
point to NULL.
3. Make the head node as last node.
1. Create a new node and assign some data to its data field.
2. Make sure that the next address field of new node must point to NULL.
3. Link the new node previous address field with lastNode.
5. Move the lastNode to newNode i.e. last node will now point to new node.
6. Repeat Steps 4-8 if you want to add more nodes to the list.
head.prev ← newNode;
head ← newNode;
write('Node added successfully at the beginning of List')
End else
End
last.next ← newNode;
last ← newNode;
write ('Node added successfully at the end of List')
End else
End
Algorithm to insert node in a doubly linked list.
newNode.data ← data;
newNode.next ← temp.next
newNode.prev ← temp
If (temp.next != NULL) then
temp.next.prev ← newNode;
End if
temp.next ← newNode;
write('Node added successfully')
End if
End
1. Traverse to N-1 node in the list. Where N is the position to insert. Say
temp now points to N-1th node.
2. Create a newNode that is to be inserted and assign some data to its data
field.
3. Connect the next address field of newNode with the node pointed by next
address field of temp node.
4. Connect the previous address field of newNode with the temp node.
6. Connect the next address field of temp node to newNode i.e. temp->next
will now point to newNode.
7. Final list
End if
End
1. Traverse to Nth node of the linked list, lets say a pointer current points to
Nth node in our case 2 node.
2. Link the node behind current node with the node ahead of current node,
which means now the N-1th node will point to N+1th node of the list.
Which can be implemented as current->prev->next = current->next.
3. If N+1th node is not NULL then link the N+1th node with N-1th node i.e.
now the previous address field of N+1th node will point to N-1th node.
Which can be implemented as current->next->prev = current->prev.
4. Finally delete the current node from memory and you are done.
Dou
bly Linked List Reversed
current ← temp;
End while
temp ← head;
head ← last;
last ← temp;
End
There are various methods to reverse a doubly linked list. Here I am using the
simplest approach to reverse the list.
1. To reverse the list we start with the first node. Say a pointer current keeps
track of the current node. Now initially current points to head node.
3. Move the position of current pointer to its next node. In general, now
current.prev holds the address of next node.
4. Repeat Step 2-3 until current pointer becomes NULL.
5. When, the current pointer becomes NULL then the list will look
something like.
6. Finally, swap the head and last pointers and you are done.
7. Now, if you compare the above image to the below given image you will
find both are similar linked list.
A circular linked list is basically a linear linked list that may be singly or
doubly. The only difference is that there is no any NULL value terminating the
list. In fact in the list every node points to the next node and last node points to
the first node, thus forming a circle. Since it forms a circle with no end to stop
hence it is called as circular linked list.
In circular linked list there can be no starting or ending node, whole node can be
traversed from any node. In order to traverse the circular linked list only once
we need to traverse entire list until the starting node is not traversed again.
A circular linked list can be implemented using both singly linked list and
doubly linked list. Here is the logical structure of a circular linked list.
Circ
ular Linked List
Dou
bly Circular Linked List
Creation of list
Traversal of list
Insertion of node
o At the beginning of list
o At any position in the list
Deletion of node
o Deletion of first node
o Deletion of node from middle of the list
o Deletion of last node Counting total number of nodes
Searching
Reversing of list
How to create a circular linked list of n nodes and display all elements of the list
The creation steps of a circular linked list is almost similar as of singly linked
list. Circular linked list only differs only differs in the last stage of its creation
as of singly linked list.
1. Create a head node and assign some data to its data field.
2. Make sure that the next pointer field of head node should point to NULL.
3. Now head node has been created that points to the first node of the list.
Lets take another pointer that also points to the first node say prevNode
pointer.
4. Create a newNode and assign some more value to its data field and also
do sure that next pointer field of newNode should point to NULL.
5. Now connect the previous node with newly created node i.e. connect the
next pointer field of prevNode with the newNode. So that next pointer
field of prevNode points to newNode.
6. Move the prevNode ahead i.e. prevNode should point to newNode which
can be done as prevNode = prevNode.next.
9. After all nodes have been created. Now at final stage we have to connect
the last node with the first node in order to make it circular. Therefore,
now connect the next pointer field of prevNode with the head node i.e.
prevNode.next = head (Since prevNode points to last node of the list) and
you are done.
DATA STRUCTURE – STACK:
A real-world stack allows operations at one end only. For example, we can
place or remove a card or plate from top of the stack only. Likewise, Stack ADT
allows all data operations at one end only. At any given time, We can only
access the top element of a stack.
This feature makes it LIFO data structure. LIFO stands for Last-in-first-out.
Here, the element which is placed inserted or added last, is accessed first. In
stack terminology, insertion operation is called PUSH operation and removal
operation is called POP operation.
Stack Representation
Below given diagram tries to depict a stack and its operations −
A stack can be implemented in two ways
By using Arrays
By using Linked List
Stack can either be a fixed size one or it may have a sense of dynamic
resizing.
Basic Operations
Stack operations may involve initializing the stack, using it and then de-
initializing it. Apart from these basic stuffs, a stack is used for the
following two primary operations –
push − pushing storing an element on the stack.
pop − removing accessing an element from the stack.
When data is PUSHed onto stack.
To use a stack efficiently we need to check status of stack as well. For the same
peek − get the top data element of the stack, without removing it.
int peek()
return stack[top];
isfull
Algorithm of isfull function –
begin
return true
else
return false
endif
end
bool isfull()
if(top == MAXSIZE)
return true;
else
return false;
isempty
Algorithm of isempty function –
Begin
If top is less than 0
Return true
Else
Return false
Endif
end
bool isempty()
{
if(top == -1)
return true;
else
return false;
}
The process of putting a new data element onto stack is known as PUSH
Operation. Push operation involves series of steps −
Step 1 − Check if stack is full.
Step 2 − If stack is full, produce error and exit.
Step 3 − If stack is not full, increment top to point next empty space.
Step 4 − Add data element to the stack location, where top is pointing.
Step 5 − return success.
top←top+1
Pop Operation
Accessing the content while removing it from stack, is known as pop
operation. In array implementation of pop operation, data element is not
actually removed, instead top is decremented to a lower position in stack to
point to next value. But in linked-list implementation, pop actually removes
data element and deallocates memory space.
A POP operation may involve the following steps −
Step 1 − Check if stack is empty.
Step 2 − If stack is empty, produce error and exit.
Step 3 − If stack is not empty, access the data element at which top is
pointing.
Step 4 − Decrease the value of top by 1.
Step 5 − return success.
Algorithm for POP operation
A simple algorithm for Pop operation can be derivedas follows −
begin:
if stack is empty
return null
endif
data ← stack[top]
top ← top - 1
Test case 1:
Enter no.of students
3
Enter id name marks
11 gopal 520
Enter id name marks
24 teja 560
Enter id name marks
41 dell 420
data in stack
41 dell 420
24 teja 560
11 gopal 520
after pop the top node
24 teja 560
11 gopal 520
Test case 2:
Enter no.of students
5
Enter id name marks
501 manoj 540
Enter id name marks
504 ajay 420
Enter id name marks
530 abhishek 390
Enter id name marks
564 sirish 400
Enter id name marks
581 mahesh 480
data in stack
581 mahesh 480
564 sirish 400
530 abhishek 390
504 ajay 420
501 manoj 540
after pop the top node
564 sirish 400
530 abhishek 390
504 ajay 420
501 manoj 540
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stack {
int sno;
char sname[20];
int tmarks;
struct stack* next;
};
struct stack* top=NULL;
void push(int no,char name[],int marks){
struct stack* temp=(struct stack*)malloc(sizeof(struct stack));
temp->sno=no;
strcpy(temp->sname,name);
temp->tmarks=marks;
temp->next=top;
top=temp;
}
void pop(){
struct stack* dtemp;
//dtemp=top;
if(top==NULL){
printf("Stack is empty\n");
}
else{
dtemp=top;
top=top->next;
dtemp->next=NULL;
free(dtemp);
}
}
void display(){
struct stack* disp;
if(top==NULL){
printf("stack is empty\n");
}
else{
disp=top;
while(disp!=NULL){
printf("%d %s %d\n",disp->sno,disp->sname,disp->tmarks);
disp=disp->next;
}
}
}
int main(){
int i,n,id,marks;
char sname[10];
printf("Enter no.of students\n");
scanf("%d",&n);
for(i=0;i<n;i++){
printf("Enter id name marks\n");
scanf("%d %s %d",&id,sname,&marks);
push(id,sname,marks);
}
printf("data in stack\n");
display();
pop();
printf("after pop the top node\n");
display();
}
2.
/*
write a c program to implement stack using array and perform the
operations
i) push ii) pop iii) display
Test case 1:
Enter n value
5
Enter elements to stack
45
Enter elements to stack
23
Enter elements to stack
67
Enter elements to stack
12
Enter elements to stack
86
Elements in stack
86
12
67
23
45
After pop the top element
12
67
23
45
*/
#include <stdio.h>
#define SIZE 100
int stack[SIZE];
int top = -1;
void push(int value)
{
if(top<SIZE-1)
{
if (top < 0)
{
stack[0] = value;
top = 0;
}
else
{
stack[top+1] = value;
top++;
}
}
else
{
printf("Stackoverflow!!!!\n");
}
}
int pop()
{
if(top >= 0)
{
int n = stack[top];
top--;
return n;
}
}
void display()
{
int i;
if(top==0){
printf("stack is empty\n");
}
else{
for(i=top;i>=0;i--)
{
printf("%d\n",stack[i]);
}
}
}
int main()
{
int i,n,x;
printf("Enter n value\n");
scanf("%d",&n);
for(i=0;i<n;i++){
printf("Enter elements to stack\n");
scanf("%d",&x);
push(x);
}
printf("Elements in stack\n");
display();
pop();
printf("After pop the top element\n");
display();
return 0;
}
Stack Applications
Applications of stack:
Balancing of symbols
Infix to Postfix /Prefix conversion
Redo-undo features at many places like editors, photoshop.
Forward and backward feature in web browsers
Used in many algorithms like Tower of Hanoi, tree traversals, stock span
problem, histogram problem.
Other applications can be Backtracking, Knight tour problem, rat in a
maze, N queen problem and sudoku solver
In Graph Algorithms like Topological Sorting and Strongly Connected
Components
Algorithm:
Declare a character stack S.
Now traverse the expression string exp.
1. If the current character is a starting bracket (‘(‘ or ‘{‘ or ‘[‘) then push it
to stack.
2. If the current character is a closing bracket (‘)’ or ‘}’ or ‘]’) then pop
from stack and if the popped character is the matching starting bracket
then fine else parenthesis are not balanced.
After complete traversal, if there is some starting bracket left in stack then
“not balanced”
Infix to Postfix /Prefix conversion
In the above image, since 1 was kept in the queue before 2, it was the first to be
removed from the queue as well. It follows the FIFO rule.
In programming terms, putting an item in the queue is called an "enqueue" and
removing an item from the queue is called "dequeue".
We can implement queue in any programming language like C, C++, Java,
Python or C#, but the specification is pretty much the same.
Queue Specifications
A queue is an object or more specifically an abstract data structure(ADT) that
allows the following operations:
• Enqueue: Add element to end of queue
• Dequeue: Remove element from front of queue
• IsEmpty: Check if queue is empty
• IsFull: Check if queue is full
• Peek: Get the value of the front of queue without removing it
1. write a c program to implement queue using single linked list and perform
the operations i) enqueue ii) dequeue iii) display
Test case 1:
Enter no.of students
3
Enter id name marks
534 venkat 590
Enter id name marks
512 gnanesh 460
Enter id name marks
561 sudha 420
data in queue
534 venkat 590
512 gnanesh 460
561 sudha 420
after removing the front node
512 gnanesh 460
561 sudha 420
Test case 2:
input=
Enter no.of students
5
Enter id name marks
111 gopal 360
Enter id name marks
130 pawan 600
Enter id name marks
191 mahesh 240
Enter id name marks
244 suresh 610
Enter id name marks
275 akhil 401
data in queue
111 gopal 360
130 pawan 600
191 mahesh 240
244 suresh 610
275 akhil 401
after removing the front node
130 pawan 600
191 mahesh 240
244 suresh 610
275 akhil 401
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct queue {
int sno;
char sname[20];
int tmarks;
struct queue* next;
};
struct queue* front=NULL;
struct queue* rear=NULL;
void enqueue(int no,char name[],int marks){
struct queue* temp=(struct queue*)malloc(sizeof(struct queue));
temp->sno=no;
strcpy(temp->sname,name);
temp->tmarks=marks;
temp->next=NULL;
if(front==NULL){
front=rear=temp;
}
else{
rear->next=temp;
rear=temp;
}
}
void dequeue(){
struct queue* dtemp=front;
if(front==NULL){
printf("queue is empty\n");
}
else{
front=front->next;
free(dtemp);
}
}
void display(){
struct queue* disp=front;
if(front==NULL){
printf("queue is empty\n");
}
else{
while(disp!=NULL){
printf("%d %s %d\n",disp->sno,disp->sname,disp->tmarks);
disp=disp->next;
}
}
}
int main(){
int i,n,id,marks;
char sname[10];
printf("Enter no.of students\n");
scanf("%d",&n);
for(i=0;i<n;i++){
printf("Enter id name marks\n");
scanf("%d %s %d",&id,sname,&marks);
enqueue(id,sname,marks);
}
printf("data in queue\n");
display();
dequeue();
printf("after removing the front node\n");
display();
}
Limitation of this implementation
As you can see in the image below, after a bit of enqueueing and dequeueing,
the size of the queue has been reduced.
The indexes 0 and 1 can only be used after the queue is reset when all the
elements have been dequeued.
By tweaking the code for queue, we can use the space by implementing a
modified queue called circular queue.