DS Unit 1 Notes 24-25
DS Unit 1 Notes 24-25
1. Static Data Structures: The data structures having a fixed size are known as
Static Data Structures. The memory for these data structures is allocated at the
compiler time, and their size cannot be changed by the user after being
compiled; however, the data stored in them can be altered.
The Array is the best example of the Static Data Structure as they have a fixed
size, and its data can be modified later.
2. Dynamic Data Structures: The data structures having a dynamic size are
known as Dynamic Data Structures. The memory of these data structures is
allocated at the run time, and their size varies during the run time of the code.
Moreover, the user can change the size as well as t he data elements stored in
these data structures at the run time of the code.
Linked Lists, Stacks, and Queues are common examples of dynamic data
structures
2. Linked Lists:
A Linked List is another example of a linear data structure used to store a collection
of data elements dynamically. Data elements in this data structure are represented by
the Nodes connected using links or pointers. Each node contains two fields, the
information field consists of the actual data, and the pointer field consists of the
address of the subsequent nodes in the list. The pointer of the last node of the linked
list consists of a null pointer, as it points to nothing. Unlike the Arrays, the user can
dynamically adjust the size of a Linked List as per the requirements.
The above image is a real-life illustration of a movie ticket counter that can help us
understand the Queue where the customer who comes first is always served first. The
customer arriving last will undoubtedly be served last. Both ends of the Queue are
open and can execute different operations. Another example is a food court line
where the customer is inserted from the rear end while the customer is removed at
the front end after providing the service they asked for.
G = (V, E)
Depending upon the position of the vertices and edges, the Graphs can be
classified into different types:
a) Null Graph: A Graph with an empty set of edges is termed a Null Graph.
b) Trivial Graph: A Graph having only one vertex is termed a Trivial Graph.
c) Simple Graph: A Graph with neither self-loops nor multiple edges is known
as a Simple Graph.
d) Multi Graph: A Graph is said to be Multi if it consists of multiple edges but
no self-loops.
e) Pseudo Graph: A Graph with self-loops and multiple edges is termed a
Pseudo Graph.
f) Non-Directed Graph: A Graph consisting of non-directed edges is known as
a Non-Directed Graph.
g) Directed Graph: A Graph consisting of the directed edges between the
vertices is known as a Directed Graph.
h) Connected Graph: A Graph with at least a single path between every pair
of vertices is termed a Connected Graph.
i) Disconnected Graph: A Graph where there does not exist any path
between at least one pair of vertices is termed a Disconnected Graph.
j) Regular Graph: A Graph where all vertices have the same degree is termed
a Regular Graph.
k) Complete Graph: A Graph in which all vertices have an edge between every
pair of vertices is known as a Complete Graph.
l) Cycle Graph: A Graph is said to be a Cycle if it has at least three vertices
and edges that form a cycle.
m) Cyclic Graph: A Graph is said to be Cyclic if and only if at least one cycle
exists.
n) Acyclic Graph: A Graph having zero cycles is termed an Acyclic Graph.
o) Finite Graph: A Graph with a finite number of vertices and edges is known
as a Finite Graph.
p) Infinite Graph: A Graph with an infinite number of vertices and edges is
known as an Infinite Graph.
q) Bipartite Graph: A Graph where the vertices can be divided into
independent sets A and B, and all the vertices of set A should only be
connected to the vertices present in set B with some edges is termed a
Bipartite Graph.
r) Planar Graph: A Graph is said to be a Planar if we can draw it in a single
plane with two edges intersecting each other.
s) Euler Graph: A Graph is said to be Euler if and only if all the vertices are
even degrees.
t) Hamiltonian Graph: A Connected Graph consisting of a Hamiltonian circuit
is known as a Hamiltonian 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, and 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.
1. List ADT
The data is generally stored in key sequence in a list which has a head structure
consisting of count, pointers and address of compare function needed to compare
the data in the list.
The data node contains the pointer to a data structure and a self-referential
pointer which points to the next node in the list.
The List ADT Functions is given below:
get() – Return an element from the list at any given position.
insert() – Insert an element at any position of the list.
remove() – Remove the first occurrence of any element from a non-empty list.
removeAt() – Remove the element at a specified location from a non-empty list.
replace() – Replace an element at any position by another element.
size() – Return the number of elements in the list.
isEmpty() – Return true if the list is empty, otherwise return false.
isFull() – Return true if the list is full, otherwise return false.
2. Stack ADT
In Stack ADT Implementation instead of data being stored in each node, the
pointer to data is stored.
The program allocates memory for the data and address is passed to the stack
ADT.
The head node and the data nodes are encapsulated in the ADT. The calling
function can only see the pointer to the stack.
The stack head structure also contains a pointer to top and count of number of
entries currently in stack.
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.
3. Queue ADT
The queue abstract data type (ADT) follows the basic design of the stack abstract
data type.
Each node contains a void pointer to the data and the link pointer to the next
element in the queue. The program’s responsibility is to allocate memory for
storing the data.
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.
Advantages:
Encapsulation: ADTs provide a way to encapsulate data and operations into a
single unit, making it easier to manage and modify the data structure.
Abstraction: ADTs allow users to work with data structures without having to
know the implementation details, which can simplify programming and reduce
errors.
Data Structure Independence: ADTs can be implemented using different data
structures, which can make it easier to adapt to changing needs and
requirements.
Information Hiding: ADTs can protect the integrity of data by controlling access
and preventing unauthorized modifications.
Modularity: ADTs can be combined with other ADTs to form more complex data
structures, which can increase flexibility and modularity in programming.
Disadvantages:
Overhead: Implementing ADTs can add overhead in terms of memory and
processing, which can affect performance.
Complexity: ADTs can be complex to implement, especially for large and
complex data structures.
Learning Curve: Using ADTs requires knowledge of their implementation and
usage, which can take time and effort to learn.
Limited Flexibility: Some ADTs may be limited in their functionality or may not
be suitable for all types of data structures.
Algorithm
An algorithm is a process or a set of rules required to perform calculations or some
other problem-solving operations especially by a computer. The formal definition of
an algorithm is that it contains the finite set of instructions which are being carried in
a specific order to perform the specific task. It is not the complete program or code; it
is just a solution (logic) of a problem, which can be represented either as an informal
description using a Flowchart or Pseudo code.
Characteristics of an Algorithm
o Input: An algorithm has some input values. We can pass 0 or some input value
to an algorithm.
o Output: We will get 1 or more output at the end of an algorithm.
o Unambiguity: An algorithm should be unambiguous which means that the
instructions in an algorithm should be clear and simple.
o Finiteness: An algorithm should have finiteness. Here, finiteness means that
the algorithm should contain a limited number of instructions, i.e., the
instructions should be countable.
o Effectiveness: An algorithm should be effective as each instruction in an
algorithm affects the overall process.
o Language independent: An algorithm must be language-independent so that
the instructions in an algorithm can be implemented in any of the languages
with the same output.
Dataflow of an Algorithm
o Problem: A problem can be a real-world problem or any instance from the
real-world problem for which we need to create a program or the set of
instructions. The set of instructions is known as an algorithm.
o Algorithm: An algorithm will be designed for a problem which is a step by step
procedure.
o Input: After designing an algorithm, the required and the desired inputs are
provided to the algorithm.
o Processing unit: The input will be given to the processing unit, and the
processing unit will produce the desired output.
o Output: The output is the outcome or the result of the program.
o Modularity: If any problem is given and we can break that problem into small -
small modules or small-small steps, which is a basic definition of an algorithm,
it means that this feature has been perfectly designed for the algorithm.
o Correctness: The correctness of an algorithm is defined as when the given
inputs produce the desired output, which means that the algorithm has been
designed algorithm. The analysis of an algorithm has been done correctly.
o Maintainability: Here, maintainability means that the algorithm should be
designed in a very simple structured way so that when we redefine the
algorithm, no major change will be done in the algorithm.
o Functionality: It considers various logical steps to solve the real-world
problem.
o Robustness: Robustness means that how an algorithm can clearly define our
problem.
o User-friendly: If the algorithm is not user-friendly, then the designer will not
be able to explain it to the programmer.
o Simplicity: If the algorithm is simple then it is easy to understand.
o Extensibility: If any other algorithm designer or programmer wants to use
your algorithm then it should be extensible.
Algorithm Analysis
The algorithm can be analyzed in two levels, i.e., first is before creating the
algorithm, and second is after creating the algorithm. The following are the two
analysis of an algorithm:
Auxiliary space: The extra space required by the algorithm, excluding the input size,
is known as an auxiliary space. The space complexity considers both the spaces, i. e.,
auxiliary space, and space used by the input.
So,
Space complexity = Auxiliary space + Input size.
For example:
If f(n) and g(n) are the two functions defined for positive integers,
then f(n) = O(g(n)) as f(n) is big oh of g(n) or f(n) is on the order of g(n)) if
there exists constants c and no such that:
f(n)≤c.g(n) for all n≥no
This implies that f(n) does not grow faster than g(n), or g(n) is an upper bound on
the function f(n). In this case, we are calculating the growth rate of the function
which eventually calculates the worst time complexity of a function, i.e., how worst
an algorithm can perform.
The idea of using big o notation is to give an upper bound of a particular function, and
eventually it leads to give a worst-time complexity. It provides an assurance that a
particular function does not behave suddenly as a quadratic or a cubic fashion, it just
behaves in a linear manner in a worst-case.
If we required that an algorithm takes at least certain amount of time without using
an upper bound, we use big- Ω notation i.e. the Greek letter "omega". It is used to
bound the growth of running time for large input size.
If f(n) and g(n) are the two functions defined for positive integers,
then f(n) = Ω (g(n)) as f(n) is Omega of g(n) or f(n) is on the order of g(n)) if
there exists constants c and no such that:
f(n)>=c.g(n) for all n≥no and c>0
Let's consider a simple example.
If f(n) = 2n+3, g(n) = n,
Is f(n)= Ω (g(n))?
It must satisfy the condition:
f(n)>=c.g(n)
To check the above condition, we first replace f(n) by 2n+3 and g(n) by n.
2n+3>=c*n
Suppose c=1
2n+3>=n (This equation will be true for any value of n starting from 1).
Therefore, it is proved that g(n) is big omega of 2n+3 function.
As we can see in the above figure that g(n) function is the lower bound of the f(n)
function when the value of c is equal to 1. Therefore, this notation gives the fastest
running time. But, we are not more interested in finding the fastest running time, we
are interested in calculating the worst-case scenarios because we want to check our
algorithm for larger input that what is the worst time that it will take so that we can
take further decision in the further process.
Search Algorithm
On each day, we search for something in our day to day life. Similarly, with the case
of computer, huge data is stored in a computer that whenever the user asks for any
data then the computer searches for that data in the memory and provides that data
to the user. There are mainly two techniques available to search the data in an array:
o Linear search
o Binary search
Linear Search
Linear search is a very simple algorithm that starts searching for an element or a
value from the beginning of an array until the required element is not found. It
compares the element to be searched with all the elements in an array, if the match
is found, then it returns the index of the element. This algorithm can be implemented
on the unsorted list.
Binary Search
A Binary algorithm is the simplest algorithm that searches the element very quickly.
It is used to search the element from the sorted list. The elements must be stored in
sequential order or the sorted manner to implement the binary algorithm. Binary
search cannot be implemented if the elements are stored in a random manner. It is
used to find the middle element of the list. In this technique we have arranged the
data in particular order before searching and compare the searching element with
middle element of the list and have to perform searching accordingly.
Sorting Algorithms
Sorting algorithms are used to rearrange the elements in an array or a given data
structure either in an ascending or descending order. The comparison operator
decides the new order of the elements.
Why do we need a sorting algorithm?
o An efficient sorting algorithm is required for optimizing the efficiency of other
algorithms like binary search algorithm as a binary search algorithm requires an
array to be sorted in a particular order, mainly in ascending order.
o It produces information in a sorted order, which is a human-readable format.
o Searching a particular element in a sorted list is faster than the unsorted list.
Sorting Algorithms:
Selection Sort
Bubble Sort
Insertion Sort
Merge Sort
Quick Sort
Heap Sort
Counting Sort
Radix Sort
Bucket Sort
Pointer
Pointer is used to points the address of the value stored anywhere in the computer
memory. To obtain the value stored at the location is known as dereferencing the
pointer. Pointer improves the performance for repetitive process such as:
o Traversing String
o Lookup Tables
o Control Tables
o Tree Structures
Pointer Details
o Pointer arithmetic: There are four arithmetic operators that can be used in
pointers: ++, --, +, -
o Array of pointers: You can define arrays to hold a number of pointers.
o Pointer to pointer: C allows you to have pointer on a pointer and so on.
o Passing pointers to functions in C: Passing an argument by reference or by
address enable the passed argument to be changed in the calling function by
the called function.
o Return pointer from functions in C: C allows a function to return a pointer to
the local variable, static variable and dynamically allocated memory as well.
Structure
A structure is a composite data type that defines a grouped list of variables that are
to be placed under one name in a block of memory. It allows different variables to be
accessed by using a single pointer to the structure.
Syntax
struct structure_name
{
data_type member1;
data_type member2;
.
.
data_type memeber;
}
Program
#include<stdio.h>
#include<conio.h>
void main( )
{
struct employee
{
int id ;
float salary ;
int mobile ;
};
struct employee e1,e2,e3 ;
clrscr();
printf ("\nEnter ids, salary & mobile no. of 3 employee\n"
scanf ("%d %f %d", &e1.id, &e1.salary, &e1.mobile);
scanf ("%d%f %d", &e2.id, &e2.salary, &e2.mobile);
scanf ("%d %f %d", &e3.id, &e3.salary, &e3.mobile);
printf ("\n Entered Result ");
printf ("\n%d %f %d", e1.id, e1.salary, e1.mobile);
printf ("\n%d%f %d", e2.id, e2.salary, e2.mobile);
printf ("\n%d %f %d", e3.id, e3.salary, e3.mobile);
getch();
}
Advantages
o It can hold variables of different data types.
o We can create objects containing different types of attributes.
o It allows us to re-use the data layout across programs.
o It is used to implement other data structures like linked lists, stacks, queues,
trees, graphs etc.
Array
An array is a fundamental data structure in computer science that stores a collection
of elements in contiguous memory locations. Each element in an array is of the same
data type and can be accessed using an index, starting from 0. Arrays are widely
used for organizing and manipulating data due to their simplicity and efficiency.
Syntax:
data_type array_name[array_size]={elements separated by commas}
or,
data_type array_name[array_size];
Operations on Arrays
Traversal: Visiting each element of an array in a specific order.
Insertion: Adding a new element to an array at a specific index.
Deletion: Removing an element from an array at a specific index.
Searching: Finding the index of an element in an array.
Update: Modifying an existing element at a specific index.
Applications
Arrays are used in a wide variety of applications, including:
Storing data for processing
Implementing data structures such as stacks and queues
Representing data in tables and matrices
Creating dynamic data structures such as linked lists and trees.
Advantages:
Easy to remember the name of all elements.
Simple traversal process.
Direct access to any element using the index.
Disadvantages:
Homogeneous nature: Only elements of the same data type can be stored.
Static memory allocation: The size of an array cannot be altered once declared.
Potential memory wastage if fewer elements are stored than the declared size.
Array Representation:
Arrays are represented as a collection of buckets where each bucket stores one
element. These buckets are indexed from '0' to 'n-1', where n is the size of that
particular array. For example, an array with size 10 will have buckets indexed from 0
to 9.
Example
int main() {
int LA[3] = {}, i;
printf("Array Before Insertion:\n");
for(i = 0; i < 3; i++)
printf("LA[%d] = %d \n", i, LA[i]);
printf("Inserting Elements.. \n");
printf("The array elements after insertion :\n"); // prints array values
for(i = 0; i < 3; i++) {
LA[i] = i + 2;
printf("LA[%d] = %d \n", i, LA[i]);
}
return 0; }
Example
void main(){
int LA[] = {1,3,5};
int n = 3;
int i;
printf("The original array elements are :\n");
for(i = 0; i<n; i++)
printf("LA[%d] = %d \n", i, LA[i]);
for(i = 1; i<n; i++) {
LA[i] = LA[i+1];
n = n - 1;
}
printf("The array elements after deletion :\n");
for(i = 0; i<n; i++)
printf("LA[%d] = %d \n", i, LA[i]); }
Algorithm:
Consider LA is a linear array with N elements and K is a positive integer such that
K<=N. Following is the algorithm to find an element with a value of ITEM using
sequential search.
1. Start
2. Set J = 0
3. Repeat steps 4 and 5 while J < N
4. IF LA[J] is equal ITEM THEN GOTO STEP 6
5. Set J = J +1
6. PRINT J, ITEM
7. Stop
Example
void main(){
int LA[] = {1,3,5,7,8};
int item = 5, n = 5;
int i = 0, j = 0;
printf("The original array elements are :\n");
for(i = 0; i<n; i++) {
printf("LA[%d] = %d \n", i, LA[i]);
}
for(i = 0; i<n; i++) {
if( LA[i] == item ) {
printf("Found element %d at position %d\n", item, i+1); }}}
Algorithm:
Following is the algorithm to traverse through all the elements present in a Linear
Array
1. Start
2. Initialize an Array of certain size and data type.
3. Initialize another variable ‘i’ with 0.
4. Print the i’th value in the array and increment i.
5. Repeat Step 4 until the end of the array is reached.
6. End
Example
int main(){
int LA[] = {1,3,5,7,8};
int item = 10, k = 3, n = 5;
int i = 0, j = n;
printf("The original array elements are :\n");
for(i = 0; i<n; i++) {
printf("LA[%d] = %d \n", i, LA[i]); }}
Types of Array
Arrays can be classified based on their dimensions, which refer to the number of
indices required to access an element in the array. In this section, we will discuss t wo
types of arrays: single-dimensional arrays and multi-dimensional arrays.
1. Single Dimensional Array
2. Multi-Dimensional Array
2. Multi-Dimensional Array
A multi-dimensional array is an array that stores elements in a two-dimensional or
higher-dimensional structure. It is also known as a two-dimensional or higher-
dimensional array, and each element in the array can be accessed using multiple
indices. Multi-dimensional arrays can be of different dimensions, including two-
dimensional, three-dimensional, or higher-dimensional arrays.
I. Two-Dimensional Array
A multidimensional array with two dimensions is known as a two-dimensional array. A
two-dimensional array is an array of arrays, where each element in the array is itself
an array. It is also known as a matrix, and can be used to represent tables, grids, or
other two-dimensional structures.
Syntax and Declaration of Two Dimensional Arrays
data_type array_name[sizeof_1st_dimension][sizeof_2nd_dimension];
//Example
int array2d[5][10];
II. Three-Dimensional Array
A three-dimensional array is a type of multi-dimensional array that stores elements in
a three-dimensional structure. It is often used to represent three-dimensional
structures such as cubes, rectangular prisms, or other geometric shapes. In a three -
dimensional array, each element is accessed using three indices, one for the depth or
"layer", one for the row, and one for the column.
Syntax for Declaration of Three Dimensional Arrays
data_type
array_name[sizeof_1st_dimension][sizeof_2nd_dimension][sizeof_3rd_dimension];
//Example
int array[5][10][15];
To find the address of the element using row-major order uses the following formula:
Address of A[i][j] = B + (i*n+j)*w (when index starts with 0) Or
Address of A[i][j] = B + [(i-1)*n+(j-1)]*w (when index starts with 1) Or
Address of A[i][j] = B + w * [n(i – LR) + (j – LC)] (when index is other than (0,1))
Where:
i = Row Subset of an element whose address to be found,
j = Column Subset of an element whose address to be found,
B = Base address,
w = Storage size of one element store in an array (in byte),
LR = Lower Limit of row/start row index of the matrix (If not given assume it as
zero),
LC = Lower Limit of column/start column index of the matrix (If not given assume it
as zero),
n = Number of column given in the matrix.
Example:
Given an array, arr[1………10][1………15] with base value 100 and the size of each
element is 1 Byte in memory. Find the address of arr[8][6] with the help of row-
major order.
Solution:
Given: Base address B = 100, Storage size of one element store in any array w = 1
Bytes, Row Subset of an element whose address to be found i = 8, Column Subset of
an element whose address to be found j = 6, Lower Limit of row/start row index of
matrix LR = 1, Lower Limit of column/start column index of matrix = 1
Number of column given in the matrix N = Upper Bound – Lower Bound + 1
= 15 – 1 + 1
= 15
Formula: Address of A[i][j] = B + w * ((i – LR) * n + (j – LC))
Address of A[8][6] = 100 + 1 * ((8 – 1) * 15 + (6 – 1))
= 100 + 1 * ((7) * 15 + (5))
= 100 + 1 * (110)
Address of A[I][J] = 210
To find the address of the element using column-major order use the following
formula:
Where:
Address of A[i][j] = B + (j*m+i)*w (when index starts with 0) Or
Address of A[i][j] = B + [(j-1)*m+(i-1)]*w (when index starts with 1) Or
Address of A[i][j] = B + w [(i – LR) +m (j – LC)] (when index is other than (0,1))
Example: Given an array arr[1………10][1………15] with a base value of 100 and the
size of each element is 1 Byte in memory find the address of arr[8][6] with the help
of column-major order.
Solution:
Given: Base address B = 100, Storage size of one element store in any array w = 1
Bytes, Row Subset of an element whose address to be found i = 8, Column Subset of
an element whose address to be found j = 6, Lower Limit of row/start row index of
matrix LR = 1, Lower Limit of column/start column index of matrix = 1.
Number of Rows given in the matrix M = Upper Bound – Lower Bound + 1
= 10 – 1 + 1
= 10
Formula: Address of A[i][j] = B + w * ((j – LC) * M + (i – LR))
Address of A[8][6] = 100 + 1 * ((6 – 1) * 10 + (8 – 1))
= 100 + 1 * ((5) * 10 + (7))
= 100 + 1 * (57)
Address of A[i][j] = 157
Example:
00304
00570
00000
02600
Sparse Matrix Representations can be done in many ways following are two
common representations:
1. Array representation
2. Linked list representation
Linked List
Linked List is a linear data structure which looks like a series of nodes, where
each node has two parts: data and next pointer. Unlike Arrays, Linked List
elements are not stored at a contiguous location. In the linked list there is a head
pointer, which points to the first element of the linked list, and if the list is empty
then it simply points to null or nothing.
int main() {
struct Node* head = NULL; // Initialize an empty list
return 0;
}
o Inserting At End of the list
#include <stdio.h>
#include <stdlib.h>
int main() {
struct Node* head = NULL;
return 0;
}
o Inserting At Specific location in the list
#include <stdio.h>
#include <stdlib.h>
// Node structure
struct Node {
int data;
struct Node* next;
};
if (temp == NULL) {
printf("Position out of bounds\n");
return;
}
newNode->next = temp->next;
temp->next = newNode;
}
int main() {
struct Node* head = NULL;
// Insert at positions
insertAtPosition(&head, 10, 1); // Insert 10 at position 1
insertAtPosition(&head, 20, 2); // Insert 20 at position 2
insertAtPosition(&head, 30, 3); // Insert 30 at position 3
insertAtPosition(&head, 25, 4); // Insert 25 at position 4
return 0;
}
Deletion: The deletion operation can be performed in three ways. They are as
follows:
o Deleting from the Beginning of the list
#include <stdio.h>
#include <stdlib.h>
// If list is empty
if (last == NULL)
printf("\nList is empty.\n");
// Driver Code
int main()
{
// Initialize the list
addatlast(10);
addatlast(20);
addatlast(30);
printf("Before deletion:\n");
viewList();
// Function Call
deletefirst();
// Print list
printf("\n\nAfter deletion:\n");
viewList();
return 0;
}
o Deleting from the End of the list
o Deleting a Specific Node
Traverse: This process displays the elements of a Single-linked list.
Search: It is a process of determining and retrieving a specific node either from
the front, the end or anywhere in the list.
// Node structure
struct Node {
int data;
struct Node* prev;
struct Node* next;
};
// If the list is not empty, set the previous head's prev to new node
if (*head != NULL)
(*head)->prev = newNode;
int main() {
struct Node* head = NULL;
return 0;
}
o Inserting after a given node.
#include <stdio.h>
#include <stdlib.h>
if (temp == NULL) {
printf("Position out of range\n");
return;
}
newNode->next = temp->next;
newNode->prev = temp;
if (temp->next != NULL) {
temp->next->prev = newNode;
}
temp->next = newNode;
}
int main() {
struct Node* head = NULL;
return 0;
}
o Inserting at the end.
Deletion: The deletion operation can be performed in three ways as follows…
o Deleting from the Beginning of the list
o Deleting from the End of the list
o Deleting a Specific Node
Display: This process displays the elements of a double-linked list.
// If start is NULL
if (start == NULL) {
start = temp;
}
// Changes Links
else {
while (trav->next != NULL)
trav = trav->next;
temp->prev = trav;
trav->next = temp;
}
}
// If start==NULL,
if (start == NULL) {
start = newnode;
newnode->prev = NULL;
newnode->next = NULL;
}
// If position==1,
else if (pos == 1) {
// this is author method its correct but we can simply call insertAtfront()
function for this special case
/* newnode->next = start;
newnode->next->prev = newnode;
newnode->prev = NULL;
start = newnode; */
// now this is improved by Jay Ghughriwala on geeksforgeeks
insertAtFront();
}
// Change links
else {
printf("\nEnter number to be inserted: ");
scanf("%d", &data);
newnode->info = data;
temp = start;
while (i < pos - 1) {
temp = temp->next;
i++;
}
newnode->next = temp->next;
newnode->prev = temp;
temp->next = newnode;
temp->next->prev = newnode;
}
}
// If DLL is empty
if (start == NULL)
printf("\nList is empty\n");
// Otherwise
else {
// Position to be deleted
printf("\nEnter position : ");
scanf("%d", &pos);
// Free memory
free(position);
}
}
// Driver Code
int main()
{
int choice;
while (1) {
switch (choice) {
case 1:
traverse();
break;
case 2:
insertAtFront();
break;
case 3:
insertAtEnd();
break;
case 4:
insertAtPosition();
break;
case 5:
deleteFirst();
break;
case 6:
deleteEnd();
break;
case 7:
deletePosition();
break;
case 8:
exit(1);
break;
default:
printf("Incorrect Choice. Try Again \n");
continue;
} }
return 0;}
// Driver Code
int main()
{
// Function Call
insertAtFront(10);
insertAtFront(20);
insertAtFront(30);
// Print list
viewList();
return 0;
}
o Insertion at the end of the list
o Insertion in between the nodes
#include <stdio.h>
#include <stdlib.h>
// Input data
printf("\nEnter data to be inserted : \n");
scanf("%d", &data);
// Input data
printf("\nEnter number after which"
" you want to enter number: \n");
scanf("%d", &value);
temp = last->next;
do {
// Driver Code
int main()
{
// Initialize the list
addatlast();
addatlast();
addatlast();
// Function Call
insertafter();
// Print list
viewList();
return 0;
}
Deletion: The deletion operation can be performed in three ways:
// If list is empty
if (last == NULL)
printf("\nList is empty.\n");
// Driver Code
int main()
{
// Initialize the list
addatlast(10);
addatlast(20);
addatlast(30);
printf("Before deletion:\n");
viewList();
// Function Call
deletefirst();
// Print list
printf("\n\nAfter deletion:\n");
viewList();
return 0;
}
o Deleting from the End of the list
// C program for the above operation
#include <stdio.h>
#include <stdlib.h>
// If list is empty
if (last == NULL)
printf("\nList is empty.\n");
temp = last->next;
// Traverse the list till
// the second last node
while (temp->next != last)
temp = temp->next;
// Driver Code
int main()
{
// Initialize the list
addatlast(10);
addatlast(20);
addatlast(30);
printf("Before Deletion:\n");
viewList();
// Function Call
deletelast();
return 0;}
// Input data
printf("\nEnter data to be inserted : \n");
scanf("%d", &data);
// If list is empty
if (last == NULL)
printf("\nList is empty.\n");
// Else
else {
// Input Data
printf("\nEnter index : ");
scanf("%d", &pos);
// Reassigning links
position = temp->next;
temp->next = position->next;
free(position);
}}
// Driver Code
int main()
{
// Initialize the list
addatlast();
addatlast();
addatlast();
// Function Call
deleteAtIndex();
return 0;
}
Display: This process displays the elements of a Circular linked list.
Only store elements no extra reference / It stores both data and address of next
pointer. node.
Insertion and deletion at end O(N) (If we maintain only head) O(1)