An Introduction To Data Structures
An Introduction To Data Structures
An Introduction To Data Structures
Since the invention of computers, people have been using the term "Data" to refer to Computer
Information, either transmitted or stored. However, there is data that exists in order types as well.
Data can be numbers or texts written on a piece of paper, in the form of bits and bytes stored
inside the memory of electronic devices, or facts stored within a person's mind. As the world
started modernizing, this data became a significant aspect of everyone's day-to-day life, and
various implementations allowed them to store it differently.
Data is a collection of facts and figures or a set of values or values of a specific format that
refers to a single set of item values. The data items are then classified into sub-items, which is
the group of items that are not known as the simple primary form of the item.
Let us consider an example where an employee name can be broken down into three sub-items:
First, Middle, and Last. However, an ID assigned to an employee will generally be considered a
single item.
PlayNext
Unmute
Duration 18:10
Loaded: 0.37%
Fullscreen
Backward Skip 10sPlay VideoForward Skip 10s
In the example mentioned above, the items such as ID, Age, Gender, First, Middle, Last, Street,
Locality, etc., are elementary data items. In contrast, the Name and the Address are group data
items.
Data Structure is a branch of Computer Science. The study of data structure allows us to
understand the organization of data and the management of the data flow in order to increase the
efficiency of any process or program. Data Structure is a particular way of storing and organizing
data in the memory of the computer so that these data can easily be retrieved and efficiently
utilized in the future when required. The data can be managed in various ways, like the logical or
mathematical model for a specific organization of data is known as a data structure.
1. First, it must be loaded enough into the structure to reflect the definite correlation of the
data with a real-world object.
2. Second, the formation should be so straightforward that one can adapt to process the data
efficiently whenever necessary.
Some examples of Data Structures are Arrays, Linked Lists, Stack, Queue, Trees, etc. Data
Structures are widely used in almost every aspect of Computer Science, i.e., Compiler Design,
Operating Systems, Graphics, Artificial Intelligence, and many more.
Data Structures are the main part of many Computer Science Algorithms as they allow the
programmers to manage the data in an effective way. It plays a crucial role in improving the
performance of a program or software, as the main objective of the software is to store and
retrieve the user's data as fast as possible.
Data Structures are the building blocks of any software or program. Selecting the suitable data
structure for a program is an extremely challenging task for a programmer.
The following are some fundamental terminologies used whenever the data structures are
involved:
1. Data: We can define data as an elementary value or a collection of values. For example,
the Employee's name and ID are the data related to the Employee.
4. Elementary Items: Data Items that are unable to divide into sub-items are known as
Elementary Items. For example, the ID of an Employee.
Entities with similar attributes form an Entity Set. Each attribute of an entity set has a range of
values, the set of all possible values that could be assigned to the specific attribute.
The term "information" is sometimes utilized for data with given attributes of meaningful or
processed data.
2. Record: A collection of different data items are known as a Record. For example, if we
talk about the employee entity, then its name, id, address, and job title can be grouped to
form the record for the employee.
3. File: A collection of different Records of one entity type is known as a File. For example,
if there are 100 employees, there will be 25 records in the related file containing data
about each employee.
As applications are becoming more complex and the amount of data is increasing every day,
which may lead to problems with data searching, processing speed, multiple requests handling,
and many more. Data Structures support different methods to organize, manage, and store data
efficiently. With the help of Data Structures, we can easily traverse the data items. Data
Structures provide Efficiency, Reusability, and Abstraction.
1. Data Structures and Algorithms are two of the key aspects of Computer Science.
2. Data Structures allow us to organize and store data, whereas Algorithms allow us to
process that data meaningfully.
3. Learning Data Structures and Algorithms will help us become better Programmers.
1. Correctness: Data Structures are designed to operate correctly for all kinds of inputs
based on the domain of interest. In order words, correctness forms the primary objective
of Data Structure, which always depends upon the problems that the Data Structure is
meant to solve.
2. Efficiency: Data Structures also requires to be efficient. It should process the data
quickly without utilizing many computer resources like memory space. In a real-time
state, the efficiency of a data structure is a key factor in determining the success and
failure of the process.
1. Robustness: Generally, all computer programmers aim to produce software that yields
correct output for every possible input, along with efficient execution on all hardware
platforms. This type of robust software must manage both valid and invalid inputs.
2. Adaptability: Building software applications like Web Browsers, Word Processors, and
Internet Search Engine include huge software systems that require correct and efficient
working or execution for many years. Moreover, software evolves due to emerging
technologies or ever-changing market conditions.
3. Reusability: The features like Reusability and Adaptability go hand in hand. It is known
that the programmer needs many resources to build any software, making it a costly
enterprise. However, if the software is developed in a reusable and adaptable way, then it
can be applied in most future applications. Thus, by executing quality data structures, it is
possible to build reusable software, which appears to be cost-effective and timesaving.
1. Primitive Data Structures are the data structures consisting of the numbers and the
characters that come in-built into programs.
3. Basic data types like Integer, Float, Character, and Boolean come under the Primitive
Data Structures.
4. These data types are also called Simple data types, as they contain characters that can't
be divided further
1. Non-Primitive Data Structures are those data structures derived from Primitive Data
Structures.
2. These data structures can't be manipulated or operated directly by machine-level
instructions.
3. The focus of these data structures is on forming a set of data elements that is
either homogeneous (same data type) or heterogeneous (different data types).
4. Based on the structure and arrangement of data, we can divide these data structures into
two sub-categories -
A data structure that preserves a linear connection among its data elements is known as a Linear
Data Structure. The arrangement of the data is done linearly, where each element consists of the
successors and predecessors except the first and the last data element. However, it is not
necessarily true in the case of memory, as the arrangement may not be sequential.
Based on memory allocation, the Linear Data Structures are further classified into two types:
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 the 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
The following is the list of Linear Data Structures that we generally use:
1. Arrays
An Array is a data structure used to collect multiple data elements of the same data type into one
variable. Instead of storing multiple values of the same data types in separate variable names, we
could store all of them together into one variable. This statement doesn't imply that we will have
to unite all the values of the same data type in any program into one array of that data type. But
there will often be times when some specific variables of the same data types are all related to
one another in a way appropriate for an array.
An Array is a list of elements where each element has a unique place in the list. The data
elements of the array share the same variable name; however, each carries a different index
number called a subscript. We can access any data element from the list with the help of its
location in the list. Thus, the key feature of the arrays to understand is that the data is stored in
contiguous memory locations, making it possible for the users to traverse through the data
elements of the array using their respective indexes.
Figure 3. An Array
a. One-Dimensional Array: An Array with only one row of data elements is known as a
One-Dimensional Array. It is stored in ascending storage location.
a. We can store a list of data elements belonging to the same data type.
c. The array also helps store data elements of a binary tree of the fixed count.
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.
a. Singly Linked List: A Singly Linked List is the most common type of Linked List. Each
node has data and a pointer field containing an address to the next node.
b. Doubly Linked List: A Doubly Linked List consists of an information field and two
pointer fields. The information field contains the data. The first pointer field contains an
address of the previous node, whereas another pointer field contains a reference to the
next node. Thus, we can go in both directions (backward as well as forward).
c. Circular Linked List: The Circular Linked List is similar to the Singly Linked List. The
only key difference is that the last node contains the address of the first node, forming a
circular loop in the Circular Linked List.
a. The Linked Lists help us implement stacks, queues, binary trees, and graphs of
predefined size.
b. We can also implement Operating System's function for dynamic memory management.
f. Doubly Linked List is utilized to implement forward and backward buttons in a browser
to move forward and backward in the opened pages of a website.
3. Stacks
A Stack is a Linear Data Structure that follows the LIFO (Last In, First Out) principle that
allows operations like insertion and deletion from one end of the Stack, i.e., Top. Stacks can be
implemented with the help of contiguous memory, an Array, and non-contiguous memory, a
Linked List. Real-life examples of Stacks are piles of books, a deck of cards, piles of money, and
many more.
The above figure represents the real-life example of a Stack where the operations are performed
from one end only, like the insertion and removal of new books from the top of the Stack. It
implies that the insertion and deletion in the Stack can be done only from the top of the Stack.
We can access only the Stack's tops at any given time.
a. Push: Operation to insert a new element in the Stack is termed as Push Operation.
b. Pop: Operation to remove or delete elements from the Stack is termed as Pop Operation.
Figure 6. A Stack
b. Stack is also utilized as Auxiliary Storage Structure for function calls, nested operations,
and deferred/postponed functions.
d. Stacks are also utilized to evaluate the arithmetic expressions in different programming
languages.
4. Queues
A Queue is a linear data structure similar to a Stack with some limitations on the insertion and
deletion of the elements. The insertion of an element in a Queue is done at one end, and the
removal is done at another or opposite end. Thus, we can conclude that the Queue data structure
follows FIFO (First In, First Out) principle to manipulate the data elements. Implementation of
Queues can be done using Arrays, Linked Lists, or Stacks. Some real-life examples of Queues
are a line at the ticket counter, an escalator, a car wash, and many more.
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.
a. Enqueue: The insertion or Addition of some data elements to the Queue is called
Enqueue. The element insertion is always done with the help of the rear pointer.
b. Dequeue: Deleting or removing data elements from the Queue is termed Dequeue. The
deletion of the element is always done with the help of the front pointer.
Figure 8. A Queue
b. Queues are also used in Job Scheduler Operations of Operating Systems, like a keyboard
buffer queue to store the keys pressed by users and a print buffer queue to store the
documents printed by the printer.
c. Queues are responsible for CPU scheduling, Job scheduling, and Disk Scheduling.
e. Queues are also used to transfer data between peripheral devices and the CPU.
f. Queues are also responsible for handling interrupts generated by the User Applications
for the CPU.
Non-Linear Data Structures are data structures where the data elements are not arranged in
sequential order. Here, the insertion and removal of data are not feasible in a linear manner.
There exists a hierarchical relationship between the individual data items.
The following is the list of Non-Linear Data Structures that we generally use:
1. Trees
A Tree is a Non-Linear Data Structure and a hierarchy containing a collection of nodes such that
each node of the tree stores a value and a list of references to other nodes (the "children").
The Tree data structure is a specialized method to arrange and collect data in the computer to be
utilized more effectively. It contains a central node, structural nodes, and sub-nodes connected
via edges. We can also say that the tree data structure consists of roots, branches, and leaves
connected.
Figure 9. A Tree
a. Binary Tree: A Tree data structure where each parent node can have at most two
children is termed a Binary Tree.
b. Binary Search Tree: A Binary Search Tree is a Tree data structure where we can easily
maintain a sorted list of numbers.
c. AVL Tree: An AVL Tree is a self-balancing Binary Search Tree where each node
maintains extra information known as a Balance Factor whose value is either -1, 0, or +1.
d. B-Tree: A B-Tree is a special type of self-balancing Binary Search Tree where each node
consists of multiple keys and can have more than two children.
Some Applications of Trees:
a. Trees implement hierarchical structures in computer systems like directories and file
systems.
e. Trees are responsible for implementing priority queues for priority-based OS scheduling
functions.
f. Trees are also responsible for parsing expressions and statements in the compilers of
different programming languages.
g. We can use Trees to store data keys for indexing for Database Management System
(DBMS).
i. Trees are also used in the path-finding algorithm implemented in Artificial Intelligence
(AI), Robotics, and Video Games Applications.
2. Graphs
A Graph is another example of a Non-Linear Data Structure comprising a finite number of nodes
or vertices and the edges connecting them. The Graphs are utilized to address problems of the
real world in which it denotes the problem area as a network such as social networks, circuit
networks, and telephone networks. For instance, the nodes or vertices of a Graph can represent a
single user in a telephone network, while the edges represent the link between them via
telephone.
G = (V,E)
Figure 10. A Graph
The above figure represents a Graph having seven vertices A, B, C, D, E, F, G, and ten edges [A,
B], [A, C], [B, C], [B, D], [B, E], [C, D], [D, E], [D, F], [E, F], and [E, G].
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.
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.
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.
f. Graphs are also used in Utility networks in order to identify the problems posed to local
or municipal corporations.
h. Graphs are also used to make document link maps of the websites in order to display the
connectivity between the pages through hyperlinks.
In the following section, we will discuss the different types of operations that we can perform to
manipulate data in every data structure:
1. Traversal: Traversing a data structure means accessing each data element exactly once
so it can be administered. For example, traversing is required while printing the names of
all the employees in a department.
2. Search: Search is another data structure operation which means to find the location of
one or more data elements that meet certain constraints. Such a data element may or may
not be present in the given set of data elements. For example, we can use the search
operation to find the names of all the employees who have the experience of more than 5
years.
3. Insertion: Insertion means inserting or adding new data elements to the collection. For
example, we can use the insertion operation to add the details of a new employee the
company has recently hired.
4. Deletion: Deletion means to remove or delete a specific data element from the given list
of data elements. For example, we can use the deleting operation to delete the name of an
employee who has left the job.
5. Sorting: Sorting means to arrange the data elements in either Ascending or Descending
order depending on the type of application. For example, we can use the sorting operation
to arrange the names of employees in a department in alphabetical order or estimate the
top three performers of the month by arranging the performance of the employees in
descending order and extracting the details of the top three.
6. Merge: Merge means to combine data elements of two sorted lists in order to form a
single list of sorted data elements.
7. Create: Create is an operation used to reserve memory for the data elements of the
program. We can perform this operation using a declaration statement. The creation of
data structure can take place either during the following:
a. Compile-time
b. Run-time
For example, the malloc() function is used in C Language to create data structure.
Selection: Selection means selecting a particular data from the available data. We can
select any particular data by specifying conditions inside the loop.
Update: The Update operation allows us to update or modify the data in the data
structure. We can also update any particular data by specifying some conditions inside
the loop, like the Selection operation.
Splitting: The Splitting operation allows us to divide data into various subparts
decreasing the overall process completion time.
As per the National Institute of Standards and Technology (NIST), a data structure is an
arrangement of information, generally in the memory, for better algorithm efficiency. Data
Structures include linked lists, stacks, queues, trees, and dictionaries. They could also be a
theoretical entity, like the name and address of a person.
From the definition mentioned above, we can conclude that the operations in data structure
include:
Whenever the data structure does such operations, it is known as an Abstract Data Type
(ADT).
We can define it as a set of data elements along with the operations on the data. The term
"abstract" refers to the fact that the data and the fundamental operations defined on it are being
studied independently of their implementation. It includes what we can do with the data, not how
we can do it.
An ADI implementation contains a storage structure in order to store the data elements and
algorithms for fundamental operation. All the data structures, like an array, linked list, queue,
stack, etc., are examples of ADT.
3. Data Structures allows the implementation of algorithms to search through data (For
example, search engine).
4. We can use the Data Structures to implement the algorithms to manipulate data (For
example, word processors).
5. We can also implement the algorithms to analyse data using Data Structures (For
example, data miners).
6. Data Structures support algorithms to generate the data (For example, a random number
generator).
7. Data Structures also support algorithms to compress and decompress the data (For
example, a zip utility).
8. We can also use Data Structures to implement algorithms to encrypt and decrypt the data
(For example, a security system).
9. With the help of Data Structures, we can build software that can manage files and
directories (For example, a file manager).
10. We can also develop software that can render graphics using Data Structures. (For
example, a web browser or 3D rendering software).
Apart from those, as mentioned earlier, there are many other applications of Data Structures that
can help us build any desired software.
What is an Algorithm?
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.
Let's understand the algorithm through a real-world example. Suppose we want to make a lemon
juice, so following are the steps required to make a lemon juice:
Step 2: Squeeze the lemon as much you can and take out its juice in a container.
Step 5: When sugar gets dissolved, add some water and ice in it.
The above real-world can be directly compared to the definition of the algorithm. We cannot
perform the step 3 before the step 2, we need to follow the specific order to make lemon juice.
An algorithm also says that each and every instruction should be followed in a specific order to
perform a specific task.
The following are the steps required to add two numbers entered by the user:
Step 1: Start
Step 4: Add the values of a and b and store the result in the sum variable, i.e., sum=a+b.
Step 6: Stop
Factors of an Algorithm
The following are the factors that we need to consider for designing an algorithm:
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.
Importance of Algorithms
1. Theoretical importance: When any real-world problem is given to us and we break the
problem into small-small modules. To break down the problem, we should know all the
theoretical aspects.
2. Practical importance: As we know that theory cannot be completed without the
practical implementation. So, the importance of algorithm can be considered as both
theoretical and practical.
Issues of Algorithms
The following are the issues that come while designing an algorithm:
Approaches of Algorithm
The following are the approaches used after considering both the theoretical and practical
importance of designing an algorithm:
o Brute force algorithm: The general logic structure is applied to design an algorithm. It
is also known as an exhaustive search algorithm that searches all the possibilities to
provide the required solution. Such algorithms are of two types:
1. Optimizing: Finding all the solutions of a problem and then take out the best
solution or if the value of the best solution is known then it will terminate if the
best solution is known.
2. Sacrificing: As soon as the best solution is found, then it will stop.
o Divide and conquer: It is a very implementation of an algorithm. It allows you to design
an algorithm in a step-by-step variation. It breaks down the algorithm to solve the
problem in different methods. It allows you to break down the problem into different
methods, and valid output is produced for the valid input. This valid output is passed to
some other function.
o Greedy algorithm: It is an algorithm paradigm that makes an optimal choice on each
iteration with the hope of getting the best solution. It is easy to implement and has a faster
execution time. But, there are very rare cases in which it provides the optimal solution.
o Dynamic programming: It makes the algorithm more efficient by storing the
intermediate results. It follows five different steps to find the optimal solution for the
problem:
1. It breaks down the problem into a subproblem to find the optimal solution.
2. After breaking down the problem, it finds the optimal solution out of these
subproblems.
3. Stores the result of the subproblems is known as memorization.
4. Reuse the result so that it cannot be recomputed for the same subproblems.
5. Finally, it computes the result of the complex program.
o Branch and Bound Algorithm: The branch and bound algorithm can be applied to only
integer programming problems. This approach divides all the sets of feasible solutions
into smaller subsets. These subsets are further evaluated to find the best solution.
o Randomized Algorithm: As we have seen in a regular algorithm, we have predefined
input and required output. Those algorithms that have some defined set of inputs and
required output, and follow some described steps are known as deterministic algorithms.
What happens that when the random variable is introduced in the randomized algorithm?.
In a randomized algorithm, some random bits are introduced by the algorithm and added
in the input to produce the output, which is random in nature. Randomized algorithms are
simpler and efficient than the deterministic algorithm.
o Backtracking: Backtracking is an algorithmic technique that solves the problem
recursively and removes the solution if it does not satisfy the constraints of a problem.
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:
o Priori Analysis: Here, priori analysis is the theoretical analysis of an algorithm which is
done before implementing the algorithm. Various factors can be considered before
implementing the algorithm like processor speed, which has no effect on the
implementation part.
o Posterior Analysis: Here, posterior analysis is a practical analysis of an algorithm. The
practical analysis is achieved by implementing the algorithm using any programming
language. This analysis basically evaluate that how much running time and space taken
by the algorithm.
Algorithm Complexity
o Time complexity: The time complexity of an algorithm is the amount of time required to
complete the execution. The time complexity of an algorithm is denoted by the big O
notation. Here, big O notation is the asymptotic notation to represent the time complexity.
The time complexity is mainly calculated by counting the number of steps to finish the
execution. Let's understand the time complexity through an example.
1. sum=0;
2. // Suppose we have to calculate the sum of n numbers.
3. for i=1 to n
4. sum=sum+i;
5. // when the loop ends then sum holds the sum of the n numbers
6. return sum;
In the above code, the time complexity of the loop statement will be atleast n, and if the value of
n increases, then the time complexity also increases. While the complexity of the code, i.e.,
return sum will be constant as its value is not dependent on the value of n and will provide the
result in one step only. We generally consider the worst-time complexity as it is the maximum
time taken for any given input size.
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,
Types of Algorithms
o Search Algorithm
o Sort Algorithm
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 else it returns -1. 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.
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.
Asymptotic Analysis
As we know that data structure is a way of organizing the data efficiently and that efficiency is
measured either in terms of time or space. So, the ideal data structure is a structure that occupies
the least possible time to perform all its operation and the memory space. Our focus would be on
finding the time complexity rather than space complexity, and by finding the time complexity,
we can decide which data structure is the best for an algorithm.
The main question arises in our mind that on what basis should we compare the time complexity
of data structures?. The time complexity can be compared based on operations performed on
them. Let's consider a simple example.
Suppose we have an array of 100 elements, and we want to insert a new element at the beginning
of the array. This becomes a very tedious task as we first need to shift the elements towards the
right, and we will add new element at the starting of the array.
Suppose we consider the linked list as a data structure to add the element at the beginning. The
linked list contains two parts, i.e., data and address of the next node. We simply add the address
of the first node in the new node, and head pointer will now point to the newly added node.
Therefore, we conclude that adding the data at the beginning of the linked list is faster than the
arrays. In this way, we can compare the data structures and select the best possible data structure
for performing the operations.
How to find the Time Complexity or running time for performing the operations?
The measuring of the actual running time is not practical at all. The running time to perform any
operation depends on the size of the input. Let's understand this statement through a simple
example.
Suppose we have an array of five elements, and we want to add a new element at the beginning
of the array. To achieve this, we need to shift each element towards right, and suppose each
element takes one unit of time. There are five elements, so five units of time would be taken.
Suppose there are 1000 elements in an array, then it takes 1000 units of time to shift. It concludes
that time complexity depends upon the input size.
Therefore, if the input size is n, then f(n) is a function of n that denotes the time complexity.
Calculating the value of f(n) for smaller programs is easy but for bigger programs, it's not that
easy. We can compare the data structures by comparing their f(n) values. We can compare the
data structures by comparing their f(n) values. We will find the growth rate of f(n) because there
might be a possibility that one data structure for a smaller input size is better than the other one
but not for the larger sizes. Now, how to find f(n).
f(n) = 5n2 + 6n + 12
where n is the number of instructions executed, and it depends on the size of the input.
When n=1
From the above calculation, it is observed that most of the time is taken by 12. But, we have to
find the growth rate of f(n), we cannot say that the maximum amount of time is taken by 12.
Let's assume the different values of n to find the growth rate of f(n).
n 5n2 6n 12
As we can observe in the above table that with the increase in the value of n, the running time of
5n2 increases while the running time of 6n and 12 also decreases. Therefore, it is observed that
for larger values of n, the squared term consumes almost 99% of the time. As the n 2 term is
contributing most of the time, so we can eliminate the rest two terms.
Therefore,
f(n) = 5n2
Here, we are getting the approximate time complexity whose result is very close to the actual
result. And this approximate measure of time complexity is known as an Asymptotic complexity.
Here, we are not calculating the exact running time, we are eliminating the unnecessary terms,
and we are just considering the term which is taking most of the time.
It is used to mathematically calculate the running time of any operation inside an algorithm.
Example: Running time of one operation is x(n) and for another operation, it is calculated as
f(n2). It refers to running time will increase linearly with an increase in 'n' for the first operation,
and running time will increase exponentially for the second operation. Similarly, the running
time of both operations will be the same if n is significantly small.
Worst case: It defines the input for which the algorithm takes a huge time.
Best case: It defines the input for which the algorithm takes the lowest time
Asymptotic Notations
The commonly used asymptotic notations used for calculating the running time complexity of an
algorithm is given below:
o Big oh Notation (?)
o Omega Notation (Ω)
o Theta Notation (θ)
It is the formal way to express the upper boundary of an algorithm running time. It measures the
worst case of time complexity or the algorithm's longest amount of time to complete its
operation. It is represented as shown below:
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:
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.
Let's understand through examples
f(n)<=c.g(n)
2*1+3<=5*1
5<=5
If n=2
2*2+3<=5*2
7<=10
We know that for any value of n, it will satisfy the above condition, i.e., 2n+3<=c.n. If the value
of c is equal to 5, then it will satisfy the condition 2n+3<=c.n. We can take any value of n
starting from 1, it will always satisfy. Therefore, we can say that for some constants c and for
some constants n0, it will always satisfy 2n+3<=c.n. As it is satisfying the above condition, so
f(n) is big oh of g(n) or we can say that f(n) grows linearly. Therefore, it concludes that c.g(n) is
the upper bound of the f(n). It can be represented graphically as:
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.
o It basically describes the best-case scenario which is opposite to the big o notation.
o It is the formal way to represent the lower bound of an algorithm's running time. It
measures the best amount of time an algorithm can possibly take to complete or the best-
case time complexity.
o It determines what is the fastest time that an algorithm can run.
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
Is f(n)= Ω (g(n))?
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).
Let f(n) and g(n) be the functions of n where n is the steps required to execute the program then:
f(n)= θg(n)
c1.g(n)<=f(n)<=c2.g(n)
where the function is bounded by two limits, i.e., upper and lower limit, and f(n) comes in
between. The condition f(n)= θg(n) will be true if and only if c1.g(n) is less than or equal to f(n)
and c2.g(n) is greater than or equal to f(n). The graphical representation of theta notation is given
below:
Let's consider the same example where
f(n)=2n+3
g(n)=n
As c1.g(n) should be less than f(n) so c1 has to be 1 whereas c2.g(n) should be greater than f(n)
so c2 is equal to 5. The c1.g(n) is the lower limit of the of the f(n) while c2.g(n) is the upper limit
of the f(n).
c1.g(n)<=f(n)<=c2.g(n)
c1.n <=2n+3<=c2.n
If n=2
1*2<=2*2+3<=2*2
2<=7<=4 // for n=2, it satisfies the condition c1.g(n)<=f(n)<=c2.g(n)
Therefore, we can say that for any value of n, it satisfies the condition c1.g(n)<=f(n)<=c2.g(n).
Hence, it is proved that f(n) is big theta of g(n). So, this is the average-case scenario which
provides the realistic time complexity.
As we know that big omega is for the best case, big oh is for the worst case while big theta is for
the average case. Now, we will find out the average, worst and the best case of the linear search
algorithm.
Suppose we have an array of n numbers, and we want to find the particular element in an array
using the linear search. In the linear search, every element is compared with the searched
element on each iteration. Suppose, if the match is found in a first iteration only, then the best
case would be Ω(1), if the element matches with the last element, i.e., nth element of the array
then the worst case would be O(n). The average case is the mid of the best and the worst-case, so
it becomes θ(n/1). The constant terms can be ignored in the time complexity so average case
would be θ(n).
So, three different analysis provide the proper bounding between the actual functions. Here,
bounding means that we have upper as well as lower limit which assures that the algorithm will
behave between these limits only, i.e., it will not go beyond these limits.
constant - ?(1)
linear - ?(n)
logarithmic - ?(log n)
exponential - 2?(n)
cubic - ?(n3)
polynomial - n?(1)
quadratic - ?(n2)
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.
Linked list
Linked list is a linear data structure that includes a series of connected nodes. Linked list can be
defined as the nodes that are randomly stored in the memory. A node in the linked list contains
two parts, i.e., first is the data part and second is the address part. The last node of the list
contains a pointer to the null. After array, linked list is the second most used data structure. In a
linked list, every link contains a connection to another link.
Linked list can be represented as the connection of nodes in which each node points to the next
node of the list. The representation of the linked list is shown below -
Till now, we have been using array data structure to organize the group of elements that are to be
stored individually in the memory. However, Array has several advantages and disadvantages
that must be known to decide the data structure that will be used throughout the program.
PlayNext
Unmute
Duration 18:10
Loaded: 0.37%
Â
Fullscreen
Now, the question arises why we should use linked list over array?
Linked list is a data structure that overcomes the limitations of arrays. Let's first see some of the
limitations of arrays -
o The size of the array must be known in advance before using it in the program.
o Increasing the size of the array is a time taking process. It is almost impossible to expand
the size of the array at run time.
o All the elements in the array need to be contiguously stored in the memory. Inserting an
element in the array needs shifting of all its predecessors.
o It allocates the memory dynamically. All the nodes of the linked list are non-contiguously
stored in the memory and linked together with the help of pointers.
o In linked list, size is no longer a problem since we do not need to define its size at the
time of declaration. List grows as per the program's demand and limited to the available
memory space.
It is simple to declare an array, as it is of single type, while the declaration of linked list is a bit
more typical than array. Linked list contains two parts, and both are of different types, i.e., one is
the simple variable, while another is the pointer variable. We can declare the linked list by using
the user-defined data type structure.
1. struct node
2. {
3. int data;
4. struct node *next;
5. }
In the above declaration, we have defined a structure named as node that contains two variables,
one is data that is of integer type, and another one is next that is a pointer which contains the
address of next node.
o Singly-linked list - Singly linked list can be defined as the collection of an ordered set of
elements. A node in the singly linked list consists of two parts: data part and link part.
Data part of the node stores actual information that is to be represented by the node,
while the link part of the node stores the address of its immediate successor.
o Doubly linked list - Doubly linked list is a complex type of linked list in which a node
contains a pointer to the previous as well as the next node in the sequence. Therefore, in a
doubly-linked list, a node consists of three parts: node data, pointer to the next node in
sequence (next pointer), and pointer to the previous node (previous pointer).
o Circular singly linked list - In a circular singly linked list, the last node of the list
contains a pointer to the first node of the list. We can have circular singly linked list as
well as circular doubly linked list.
o Circular doubly linked list - Circular doubly linked list is a more complex type of data
structure in which a node contains pointers to its previous node as well as the next node.
Circular doubly linked list doesn't contain NULL in any of the nodes. The last node of the
list contains the address of the first node of the list. The first node of the list also contains
the address of the last node in its previous pointer.
Now, let's see the benefits and limitations of using the Linked list.
o Dynamic data structure - The size of the linked list may vary according to the
requirements. Linked list does not have a fixed size.
o Insertion and deletion - Unlike arrays, insertion, and deletion in linked list is easier.
Array elements are stored in the consecutive location, whereas the elements in the linked
list are stored at a random location. To insert or delete an element in an array, we have to
shift the elements for creating the space. Whereas, in linked list, instead of shifting, we
just have to update the address of the pointer of the node.
o Memory efficient - The size of a linked list can grow or shrink according to the
requirements, so memory consumption in linked list is efficient.
o Implementation - We can implement both stacks and queues using linked list.
o Memory usage - In linked list, node occupies more memory than array. Each node of the
linked list occupies two types of variables, i.e., one is a simple variable, and another one
is the pointer variable.
o Traversal - Traversal is not easy in the linked list. If we have to access an element in the
linked list, we cannot access it randomly, while in case of array we can randomly access
it by index. For example, if we want to access the 3rd node, then we need to traverse all
the nodes before it. So, the time required to access a particular node is large.
o Reverse traversing - Backtracking or reverse traversing is difficult in a linked list. In a
doubly-linked list, it is easier but requires more memory to store the back pointer.
o With the help of a linked list, the polynomials can be represented as well as we can
perform the operations on the polynomial.
o A linked list can be used to represent the sparse matrix.
o The various operations like student's details, employee's details, or product details can be
implemented using the linked list as the linked list uses the structure data type that can
hold different data types.
o Using linked list, we can implement stack, queue, tree, and other various data structures.
o The graph is a collection of edges and vertices, and the graph can be represented as an
adjacency matrix and adjacency list. If we want to represent the graph as an adjacency
matrix, then it can be implemented as an array. If we want to represent the graph as an
adjacency list, then it can be implemented as a linked list.
o A linked list can be used to implement dynamic memory allocation. The dynamic
memory allocation is the memory allocation done at the run-time.
The basic operations that are supported by a list are mentioned as follows -
Now, let's see the time and space complexity of the linked list for the operations search, insert,
and delete.
1. Time Complexity
2. Space Complexity
Deletion O(n)
Search O(n)
So, that's all about the introduction of linked list. Hope the article will be helpful and informative
to you.
Before knowing about the types of a linked list, we should know what is linked list. So, to know
about the linked list, click on the link given below:
It is the commonly used linked list in programs. If we are talking about the linked list, it means it
is a singly linked list. The singly linked list is a data structure that contains two parts, i.e., one is
the data part, and the other one is the address part, which contains the address of the next or the
successor node. The address part in a node is also known as a pointer.
Suppose we have three nodes, and the addresses of these three nodes are 100, 200 and 300
respectively. The representation of three nodes as a linked list is shown in the below figure:
Advertisement
We can observe in the above figure that there are three different nodes having address 100, 200
and 300 respectively. The first node contains the address of the next node, i.e., 200, the second
node contains the address of the last node, i.e., 300, and the third node contains the NULL value
in its address part as it does not point to any node. The pointer that holds the address of the initial
node is known as a head pointer.
The linked list, which is shown in the above diagram, is known as a singly linked list as it
contains only a single link. In this list, only forward traversal is possible; we cannot traverse in
the backward direction as it has only one link in the list.
1. struct node
2. {
3. int data;
4. struct node *next;
5. }
In the above representation, we have defined a user-defined structure named a node containing
two members, the first one is data of integer type, and the other one is the pointer (next) of the
node type.
To know more about a singly linked list, click on the link given below:
https://www.javatpoint.com/singly-linked-list
As the name suggests, the doubly linked list contains two pointers. We can define the doubly
linked list as a linear data structure with three parts: the data part and the other two address part.
In other words, a doubly linked list is a list that has three parts in a single node, includes one data
part, a pointer to its previous node, and a pointer to the next node.
Suppose we have three nodes, and the address of these nodes are 100, 200 and 300, respectively.
The representation of these nodes in a doubly-linked list is shown below:
As we can observe in the above figure, the node in a doubly-linked list has two address parts;
one part stores the address of the next while the other part of the node stores the previous node's
address. The initial node in the doubly linked list has the NULL value in the address part, which
provides the address of the previous node.
1. struct node
2. {
3. int data;
4. struct node *next;
5. struct node *prev;
6. }
In the above representation, we have defined a user-defined structure named a node with three
members, one is data of integer type, and the other two are the pointers, i.e., next and prev of
the node type. The next pointer variable holds the address of the next node, and the prev
pointer holds the address of the previous node. The type of both the pointers, i.e., next and
prev is struct node as both the pointers are storing the address of the node of the struct
node type.
To know more about doubly linked list, click on the link given below:
https://www.javatpoint.com/doubly-linked-list
A circular linked list is a variation of a singly linked list. The only difference between the singly
linked list and a circular linked list is that the last node does not point to any node in a singly
linked list, so its link part contains a NULL value. On the other hand, the circular linked list is a
list in which the last node connects to the first node, so the link part of the last node holds the
first node's address. The circular linked list has no starting and ending node. We can traverse in
any direction, i.e., either backward or forward. The diagrammatic representation of the circular
linked list is shown below:
1. struct node
2. {
3. int data;
4. struct node *next;
5. }
A circular linked list is a sequence of elements in which each node has a link to the next node,
and the last node is having a link to the first node. The representation of the circular linked list
will be similar to the singly linked list, as shown below:
To know more about the circular linked list, click on the link given below:
https://www.javatpoint.com/circular-singly-linked-list
The doubly circular linked list has the features of both the circular linked list and doubly linked
list.
The above figure shows the representation of the doubly circular linked list in which the last
node is attached to the first node and thus creates a circle. It is a doubly linked list also because
each node holds the address of the previous node also. The main difference between the doubly
linked list and doubly circular linked list is that the doubly circular linked list does not contain
the NULL value in the previous field of the node. As the doubly circular linked contains three
parts, i.e., two address parts and one data part so its representation is similar to the doubly linked
list.
1. struct node
2. {
3. int data;
4. struct node *next;
5. struct node *prev;
6. }
Program
Pointer
1. #include <stdio.h>
2.
3. int main( )
4. {
5. int a = 5;
6. int *b;
7. b = &a;
8.
9. printf ("value of a = %d\n", a);
10. printf ("value of a = %d\n", *(&a));
11. printf ("value of a = %d\n", *b);
12. printf ("address of a = %u\n", &a);
13. printf ("address of a = %d\n", b);
14. printf ("address of b = %u\n", &b);
15. printf ("value of b = address of a = %u", b);
16. return 0;
17. }
Output
1. value of a = 5
2. value of a = 5
3. address of a = 3010494292
4. address of a = -1284473004
5. address of b = 3010494296
6. value of b = address of a = 3010494292
Program
Pointer to Pointer
1. #include <stdio.h>
2.
3. int main( )
4. {
5. int a = 5;
6. int *b;
7. int **c;
8. b = &a;
9. c = &b;
10. printf ("value of a = %d\n", a);
11. printf ("value of a = %d\n", *(&a));
12. printf ("value of a = %d\n", *b);
13. printf ("value of a = %d\n", **c);
14. printf ("value of b = address of a = %u\n", b);
15. printf ("value of c = address of b = %u\n", c);
16. printf ("address of a = %u\n", &a);
17. printf ("address of a = %u\n", b);
18. printf ("address of a = %u\n", *c);
19. printf ("address of b = %u\n", &b);
20. printf ("address of b = %u\n", c);
21. printf ("address of c = %u\n", &c);
22. return 0;
23. }
Pointer to Pointer
1. value of a = 5
2. value of a = 5
3. value of a = 5
4. value of a = 5
5. value of b = address of a = 2831685116
6. value of c = address of b = 2831685120
7. address of a = 2831685116
8. address of a = 2831685116
9. address of a = 2831685116
10. address of b = 2831685120
11. address of b = 2831685120
12. address of c = 2831685128
Linked List
o Linked List can be defined as collection of objects called nodes that are randomly stored
in the memory.
o A node contains two fields i.e. data stored at that particular address and the pointer which
contains the address of the next node in the memory.
o The last node of the list contains pointer to the null.
Uses of Linked List
o The list is not required to be contiguously present in the memory. The node can reside
any where in the memory and linked together to make a list. This achieves optimized
utilization of space.
o list size is limited to the memory size and doesn't need to be declared in advance.
o Empty node can not be present in the linked list.
o We can store values of primitive types or objects in the singly linked list.
Till now, we were using array data structure to organize the group of elements that are to be
stored individually in the memory. However, Array has several advantages and disadvantages
which must be known in order to decide the data structure which will be used throughout the
program.
1. The size of array must be known in advance before using it in the program.
2. Increasing size of the array is a time taking process. It is almost impossible to expand the
size of the array at run time.
3. All the elements in the array need to be contiguously stored in the memory. Inserting any
element in the array needs shifting of all its predecessors.
Linked list is the data structure which can overcome all the limitations of an array. Using linked
list is useful because,
1. It allocates the memory dynamically. All the nodes of linked list are non-contiguously
stored in the memory and linked together with the help of pointers.
2. Sizing is no longer a problem since we do not need to define its size at the time of
declaration. List grows as per the program's demand and limited to the available memory
space.
Singly linked list can be defined as the collection of ordered set of elements. The number of
elements may vary according to need of the program. A node in the singly linked list consist of
two parts: data part and link part. Data part of the node stores actual information that is to be
represented by the node while the link part of the node stores the address of its immediate
successor.
Advertisement
One way chain or singly linked list can be traversed only in one direction. In other words, we can
say that each node contains only next pointer, therefore we can not traverse the list in the reverse
direction.
Consider an example where the marks obtained by the student in three subjects are stored in a
linked list as shown in the figure.
In the above figure, the arrow represents the links. The data part of every node contains the
marks obtained by the student in the different subject. The last node in the list is identified by the
null pointer which is present in the address part of the last node. We can have as many elements
we require, in the data part of the list.
Complexity
Singly Linked θ(n) θ(n) θ(1) θ(1) O(n) O(n) O(1) O(1) O(n)
List
There are various operations which can be performed on singly linked list. A list of all such
operations is given below.
Node Creation
1. struct node
2. {
3. int data;
4. struct node *next;
5. };
6. struct node *head, *ptr;
7. ptr = (struct node *)malloc(sizeof(struct node *));
Insertion
The insertion into a singly linked list can be performed at different positions. Based on the
position of the new node being inserted, the insertion is categorized into the following categories.
S Operation Description
N
1 Insertion at It involves inserting any element at the front of the list. We just need to a
beginning few link adjustments to make the new node as the head of the list.
2 Insertion at end of It involves insertion at the last of the linked list. The new node can be
the list inserted as the only node in the list or it can be inserted as the last one.
Different logics are implemented in each scenario.
3 Insertion after It involves insertion after the specified node of the linked list. We need to
specified node skip the desired number of nodes in order to reach the node after which the
new node will be inserted. .
The Deletion of a node from a singly linked list can be performed at different positions. Based on
the position of the node being deleted, the operation is categorized into the following categories.
S Operation Description
N
1 Deletion at It involves deletion of a node from the beginning of the list. This is the
beginning simplest operation among all. It just need a few adjustments in the node
pointers.
2 Deletion at the It involves deleting the last node of the list. The list can either be empty or
end of the list full. Different logic is implemented for the different scenarios.
3 Deletion after It involves deleting the node after the specified node in the list. we need to
specified node skip the desired number of nodes to reach the node after which the node will
be deleted. This requires traversing through the list.
4 Traversing In traversing, we simply visit each node of the list at least once in order to
perform some specific operation on it, for example, printing data part of each
node present in the list.
5 Searching In searching, we match each element of the list with the given element. If the
element is found on any of the location then location of that element is
returned otherwise null is returned. .
1. #include<stdio.h>
2. #include<stdlib.h>
3. struct node
4. {
5. int data;
6. struct node *next;
7. };
8. struct node *head;
9.
10. void beginsert ();
11. void lastinsert ();
12. void randominsert();
13. void begin_delete();
14. void last_delete();
15. void random_delete();
16. void display();
17. void search();
18. void main ()
19. {
20. int choice =0;
21. while(choice != 9)
22. {
23. printf("\n\n*********Main Menu*********\n");
24. printf("\nChoose one option from the following list ...\n");
25. printf("\n===============================================\n");
26. printf("\n1.Insert in begining\n2.Insert at last\n3.Insert at any random location\n4.Delete fro
m Beginning\n
27. 5.Delete from last\n6.Delete node after specified location\n7.Search for an element\
n8.Show\n9.Exit\n");
28. printf("\nEnter your choice?\n");
29. scanf("\n%d",&choice);
30. switch(choice)
31. {
32. case 1:
33. beginsert();
34. break;
35. case 2:
36. lastinsert();
37. break;
38. case 3:
39. randominsert();
40. break;
41. case 4:
42. begin_delete();
43. break;
44. case 5:
45. last_delete();
46. break;
47. case 6:
48. random_delete();
49. break;
50. case 7:
51. search();
52. break;
53. case 8:
54. display();
55. break;
56. case 9:
57. exit(0);
58. break;
59. default:
60. printf("Please enter valid choice..");
61. }
62. }
63. }
64. void beginsert()
65. {
66. struct node *ptr;
67. int item;
68. ptr = (struct node *) malloc(sizeof(struct node *));
69. if(ptr == NULL)
70. {
71. printf("\nOVERFLOW");
72. }
73. else
74. {
75. printf("\nEnter value\n");
76. scanf("%d",&item);
77. ptr->data = item;
78. ptr->next = head;
79. head = ptr;
80. printf("\nNode inserted");
81. }
82.
83. }
84. void lastinsert()
85. {
86. struct node *ptr,*temp;
87. int item;
88. ptr = (struct node*)malloc(sizeof(struct node));
89. if(ptr == NULL)
90. {
91. printf("\nOVERFLOW");
92. }
93. else
94. {
95. printf("\nEnter value?\n");
96. scanf("%d",&item);
97. ptr->data = item;
98. if(head == NULL)
99. {
100. ptr -> next = NULL;
101. head = ptr;
102. printf("\nNode inserted");
103. }
104. else
105. {
106. temp = head;
107. while (temp -> next != NULL)
108. {
109. temp = temp -> next;
110. }
111. temp->next = ptr;
112. ptr->next = NULL;
113. printf("\nNode inserted");
114.
115. }
116. }
117. }
118. void randominsert()
119. {
120. int i,loc,item;
121. struct node *ptr, *temp;
122. ptr = (struct node *) malloc (sizeof(struct node));
123. if(ptr == NULL)
124. {
125. printf("\nOVERFLOW");
126. }
127. else
128. {
129. printf("\nEnter element value");
130. scanf("%d",&item);
131. ptr->data = item;
132. printf("\nEnter the location after which you want to insert ");
133. scanf("\n%d",&loc);
134. temp=head;
135. for(i=0;i<loc;i++)
136. {
137. temp = temp->next;
138. if(temp == NULL)
139. {
140. printf("\ncan't insert\n");
141. return;
142. }
143.
144. }
145. ptr ->next = temp ->next;
146. temp ->next = ptr;
147. printf("\nNode inserted");
148. }
149. }
150. void begin_delete()
151. {
152. struct node *ptr;
153. if(head == NULL)
154. {
155. printf("\nList is empty\n");
156. }
157. else
158. {
159. ptr = head;
160. head = ptr->next;
161. free(ptr);
162. printf("\nNode deleted from the begining ...\n");
163. }
164. }
165. void last_delete()
166. {
167. struct node *ptr,*ptr1;
168. if(head == NULL)
169. {
170. printf("\nlist is empty");
171. }
172. else if(head -> next == NULL)
173. {
174. head = NULL;
175. free(head);
176. printf("\nOnly node of the list deleted ...\n");
177. }
178.
179. else
180. {
181. ptr = head;
182. while(ptr->next != NULL)
183. {
184. ptr1 = ptr;
185. ptr = ptr ->next;
186. }
187. ptr1->next = NULL;
188. free(ptr);
189. printf("\nDeleted Node from the last ...\n");
190. }
191. }
192. void random_delete()
193. {
194. struct node *ptr,*ptr1;
195. int loc,i;
196. printf("\n Enter the location of the node after which you want to perform deletion \n");
197. scanf("%d",&loc);
198. ptr=head;
199. for(i=0;i<loc;i++)
200. {
201. ptr1 = ptr;
202. ptr = ptr->next;
203.
204. if(ptr == NULL)
205. {
206. printf("\nCan't delete");
207. return;
208. }
209. }
210. ptr1 ->next = ptr ->next;
211. free(ptr);
212. printf("\nDeleted node %d ",loc+1);
213. }
214. void search()
215. {
216. struct node *ptr;
217. int item,i=0,flag;
218. ptr = head;
219. if(ptr == NULL)
220. {
221. printf("\nEmpty List\n");
222. }
223. else
224. {
225. printf("\nEnter item which you want to search?\n");
226. scanf("%d",&item);
227. while (ptr!=NULL)
228. {
229. if(ptr->data == item)
230. {
231. printf("item found at location %d ",i+1);
232. flag=0;
233. }
234. else
235. {
236. flag=1;
237. }
238. i++;
239. ptr = ptr -> next;
240. }
241. if(flag==1)
242. {
243. printf("Item not found\n");
244. }
245. }
246.
247. }
248.
249. void display()
250. {
251. struct node *ptr;
252. ptr = head;
253. if(ptr == NULL)
254. {
255. printf("Nothing to print");
256. }
257. else
258. {
259. printf("\nprinting values . . . . .\n");
260. while (ptr!=NULL)
261. {
262. printf("\n%d",ptr->data);
263. ptr = ptr -> next;
264. }
265. }
266. }
267.
Output:
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
Enter value
1
Node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
Enter value?
2
Node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
Node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
printing values . . . . .
1
2
1
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
Enter value?
123
Node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
Enter value
1234
Node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
Enter the location of the node after which you want to perform deletion
1
Deleted node 2
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
printing values . . . . .
1
1
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete node after specified location
7.Search for an element
8.Show
9.Exit
Doubly linked list is a complex type of linked list in which a node contains a pointer to the
previous as well as the next node in the sequence. Therefore, in a doubly linked list, a node
consists of three parts: node data, pointer to the next node in sequence (next pointer) , pointer to
the previous node (previous pointer). A sample node in a doubly linked list is shown in the
figure.
A doubly linked list containing three nodes having numbers from 1 to 3 in their data part, is
shown in the following image.
In C, structure of a node in doubly linked list can be given as :
1. struct node
2. {
3. struct node *prev;
4. int data;
5. struct node *next;
6. }
The prev part of the first node and the next part of the last node will always contain null
indicating end in each direction.
PlayNext
Unmute
Duration 18:10
Loaded: 0.37%
Â
Fullscreen
Backward Skip 10sPlay VideoForward Skip 10s
In a singly linked list, we could traverse only in one direction, because each node contains
address of the next node and it doesn't have any record of its previous nodes. However, doubly
linked list overcome this limitation of singly linked list. Due to the fact that, each node of the list
contains the address of its previous node, we can find all the details about the previous node as
well by using the previous address stored inside the previous part of each node.
Memory Representation of a doubly linked list is shown in the following image. Generally,
doubly linked list consumes more space for every node and therefore, causes more expansive
basic operations such as insertion and deletion. However, we can easily manipulate the elements
of the list since the list maintains pointers in both the directions (forward and backward).
In the following image, the first element of the list that is i.e. 13 stored at address 1. The head
pointer points to the starting address 1. Since this is the first element being added to the list
therefore the prev of the list contains null. The next node of the list resides at address 4
therefore the first node contains 4 in its next pointer.
We can traverse the list in this way until we find any node containing null or -1 in its next part.
Operations on doubly linked list
Node Creation
1. struct node
2. {
3. struct node *prev;
4. int data;
5. struct node *next;
6. };
7. struct node *head;
All the remaining operations regarding doubly linked list are described in the following table.
SN Operation Description
2 Insertion at end Adding the node into the linked list to the end.
3 Insertion after Adding the node into the linked list after the specified
specified node node.
5 Deletion at the end Removing the node from end of the list.
6 Deletion of the node Removing the node which is present just after the node
having given data containing the given data.
Menu Driven Program in C to implement all the operations of doubly linked list
1. #include<stdio.h>
2. #include<stdlib.h>
3. struct node
4. {
5. struct node *prev;
6. struct node *next;
7. int data;
8. };
9. struct node *head;
10. void insertion_beginning();
11. void insertion_last();
12. void insertion_specified();
13. void deletion_beginning();
14. void deletion_last();
15. void deletion_specified();
16. void display();
17. void search();
18. void main ()
19. {
20. int choice =0;
21. while(choice != 9)
22. {
23. printf("\n*********Main Menu*********\n");
24. printf("\nChoose one option from the following list ...\n");
25. printf("\n===============================================\n");
26. printf("\n1.Insert in begining\n2.Insert at last\n3.Insert at any random location\n4.Delete fro
m Beginning\n
27. 5.Delete from last\n6.Delete the node after the given data\n7.Search\n8.Show\n9.Exit\n");
28. printf("\nEnter your choice?\n");
29. scanf("\n%d",&choice);
30. switch(choice)
31. {
32. case 1:
33. insertion_beginning();
34. break;
35. case 2:
36. insertion_last();
37. break;
38. case 3:
39. insertion_specified();
40. break;
41. case 4:
42. deletion_beginning();
43. break;
44. case 5:
45. deletion_last();
46. break;
47. case 6:
48. deletion_specified();
49. break;
50. case 7:
51. search();
52. break;
53. case 8:
54. display();
55. break;
56. case 9:
57. exit(0);
58. break;
59. default:
60. printf("Please enter valid choice..");
61. }
62. }
63. }
64. void insertion_beginning()
65. {
66. struct node *ptr;
67. int item;
68. ptr = (struct node *)malloc(sizeof(struct node));
69. if(ptr == NULL)
70. {
71. printf("\nOVERFLOW");
72. }
73. else
74. {
75. printf("\nEnter Item value");
76. scanf("%d",&item);
77.
78. if(head==NULL)
79. {
80. ptr->next = NULL;
81. ptr->prev=NULL;
82. ptr->data=item;
83. head=ptr;
84. }
85. else
86. {
87. ptr->data=item;
88. ptr->prev=NULL;
89. ptr->next = head;
90. head->prev=ptr;
91. head=ptr;
92. }
93. printf("\nNode inserted\n");
94. }
95.
96. }
97. void insertion_last()
98. {
99. struct node *ptr,*temp;
100. int item;
101. ptr = (struct node *) malloc(sizeof(struct node));
102. if(ptr == NULL)
103. {
104. printf("\nOVERFLOW");
105. }
106. else
107. {
108. printf("\nEnter value");
109. scanf("%d",&item);
110. ptr->data=item;
111. if(head == NULL)
112. {
113. ptr->next = NULL;
114. ptr->prev = NULL;
115. head = ptr;
116. }
117. else
118. {
119. temp = head;
120. while(temp->next!=NULL)
121. {
122. temp = temp->next;
123. }
124. temp->next = ptr;
125. ptr ->prev=temp;
126. ptr->next = NULL;
127. }
128.
129. }
130. printf("\nnode inserted\n");
131. }
132. void insertion_specified()
133. {
134. struct node *ptr,*temp;
135. int item,loc,i;
136. ptr = (struct node *)malloc(sizeof(struct node));
137. if(ptr == NULL)
138. {
139. printf("\n OVERFLOW");
140. }
141. else
142. {
143. temp=head;
144. printf("Enter the location");
145. scanf("%d",&loc);
146. for(i=0;i<loc;i++)
147. {
148. temp = temp->next;
149. if(temp == NULL)
150. {
151. printf("\n There are less than %d elements", loc);
152. return;
153. }
154. }
155. printf("Enter value");
156. scanf("%d",&item);
157. ptr->data = item;
158. ptr->next = temp->next;
159. ptr -> prev = temp;
160. temp->next = ptr;
161. temp->next->prev=ptr;
162. printf("\nnode inserted\n");
163. }
164. }
165. void deletion_beginning()
166. {
167. struct node *ptr;
168. if(head == NULL)
169. {
170. printf("\n UNDERFLOW");
171. }
172. else if(head->next == NULL)
173. {
174. head = NULL;
175. free(head);
176. printf("\nnode deleted\n");
177. }
178. else
179. {
180. ptr = head;
181. head = head -> next;
182. head -> prev = NULL;
183. free(ptr);
184. printf("\nnode deleted\n");
185. }
186.
187. }
188. void deletion_last()
189. {
190. struct node *ptr;
191. if(head == NULL)
192. {
193. printf("\n UNDERFLOW");
194. }
195. else if(head->next == NULL)
196. {
197. head = NULL;
198. free(head);
199. printf("\nnode deleted\n");
200. }
201. else
202. {
203. ptr = head;
204. if(ptr->next != NULL)
205. {
206. ptr = ptr -> next;
207. }
208. ptr -> prev -> next = NULL;
209. free(ptr);
210. printf("\nnode deleted\n");
211. }
212. }
213. void deletion_specified()
214. {
215. struct node *ptr, *temp;
216. int val;
217. printf("\n Enter the data after which the node is to be deleted : ");
218. scanf("%d", &val);
219. ptr = head;
220. while(ptr -> data != val)
221. ptr = ptr -> next;
222. if(ptr -> next == NULL)
223. {
224. printf("\nCan't delete\n");
225. }
226. else if(ptr -> next -> next == NULL)
227. {
228. ptr ->next = NULL;
229. }
230. else
231. {
232. temp = ptr -> next;
233. ptr -> next = temp -> next;
234. temp -> next -> prev = ptr;
235. free(temp);
236. printf("\nnode deleted\n");
237. }
238. }
239. void display()
240. {
241. struct node *ptr;
242. printf("\n printing values...\n");
243. ptr = head;
244. while(ptr != NULL)
245. {
246. printf("%d\n",ptr->data);
247. ptr=ptr->next;
248. }
249. }
250. void search()
251. {
252. struct node *ptr;
253. int item,i=0,flag;
254. ptr = head;
255. if(ptr == NULL)
256. {
257. printf("\nEmpty List\n");
258. }
259. else
260. {
261. printf("\nEnter item which you want to search?\n");
262. scanf("%d",&item);
263. while (ptr!=NULL)
264. {
265. if(ptr->data == item)
266. {
267. printf("\nitem found at location %d ",i+1);
268. flag=0;
269. break;
270. }
271. else
272. {
273. flag=1;
274. }
275. i++;
276. ptr = ptr -> next;
277. }
278. if(flag==1)
279. {
280. printf("\nItem not found\n");
281. }
282. }
283.
284. }
Output
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
printing values...
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
Node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
Node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
Node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
printing values...
1234
123
12
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
Enter value89
node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
printing values...
1234
123
12345
12
89
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
node deleted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
node deleted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
printing values...
123
12345
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
printing values...
123
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
Can't delete
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Insert at any random location
4.Delete from Beginning
5.Delete from last
6.Delete the node after the given data
7.Search
8.Show
9.Exit
Exited..
Circular Singly Linked List
In a circular Singly linked list, the last node of the list contains a pointer to the first node of the
list. We can have circular singly linked list as well as circular doubly linked list.
We traverse a circular singly linked list until we reach the same node where we started. The
circular singly liked list has no beginning and no ending. There is no null value present in the
next part of any of the nodes.
PlayNext
Unmute
Duration 18:10
Loaded: 0.37%
Â
Fullscreen
However, due to the fact that we are considering circular linked list in the memory therefore the
last node of the list contains the address of the first node of the list.
We can also have more than one number of linked list in the memory with the different start
pointers pointing to the different start nodes in the list. The last node is identified by its next part
which contains the address of the start node of the list. We must be able to identify the last node
of any linked list so that we can find out the number of iterations which need to be performed
while traversing the list.
Operations on Circular Singly linked list:
Insertion
SN Operation Description
2 Insertion at the end Adding a node into circular singly linked list at the end.
SN Operation Description
1 Deletion at Removing the node from circular singly linked list at the
beginning beginning.
2 Deletion at the Removing the node from circular singly linked list at the
end end.
3 Searching Compare each element of the node with the given item and
return the location at which the item is present in the list
otherwise return null.
1. #include<stdio.h>
2. #include<stdlib.h>
3. struct node
4. {
5. int data;
6. struct node *next;
7. };
8. struct node *head;
9.
10. void beginsert ();
11. void lastinsert ();
12. void randominsert();
13. void begin_delete();
14. void last_delete();
15. void random_delete();
16. void display();
17. void search();
18. void main ()
19. {
20. int choice =0;
21. while(choice != 7)
22. {
23. printf("\n*********Main Menu*********\n");
24. printf("\nChoose one option from the following list ...\n");
25. printf("\n===============================================\n");
26. printf("\n1.Insert in begining\n2.Insert at last\n3.Delete from Beginning\n4.Delete from last\
n5.Search for an element\n6.Show\n7.Exit\n");
27. printf("\nEnter your choice?\n");
28. scanf("\n%d",&choice);
29. switch(choice)
30. {
31. case 1:
32. beginsert();
33. break;
34. case 2:
35. lastinsert();
36. break;
37. case 3:
38. begin_delete();
39. break;
40. case 4:
41. last_delete();
42. break;
43. case 5:
44. search();
45. break;
46. case 6:
47. display();
48. break;
49. case 7:
50. exit(0);
51. break;
52. default:
53. printf("Please enter valid choice..");
54. }
55. }
56. }
57. void beginsert()
58. {
59. struct node *ptr,*temp;
60. int item;
61. ptr = (struct node *)malloc(sizeof(struct node));
62. if(ptr == NULL)
63. {
64. printf("\nOVERFLOW");
65. }
66. else
67. {
68. printf("\nEnter the node data?");
69. scanf("%d",&item);
70. ptr -> data = item;
71. if(head == NULL)
72. {
73. head = ptr;
74. ptr -> next = head;
75. }
76. else
77. {
78. temp = head;
79. while(temp->next != head)
80. temp = temp->next;
81. ptr->next = head;
82. temp -> next = ptr;
83. head = ptr;
84. }
85. printf("\nnode inserted\n");
86. }
87.
88. }
89. void lastinsert()
90. {
91. struct node *ptr,*temp;
92. int item;
93. ptr = (struct node *)malloc(sizeof(struct node));
94. if(ptr == NULL)
95. {
96. printf("\nOVERFLOW\n");
97. }
98. else
99. {
100. printf("\nEnter Data?");
101. scanf("%d",&item);
102. ptr->data = item;
103. if(head == NULL)
104. {
105. head = ptr;
106. ptr -> next = head;
107. }
108. else
109. {
110. temp = head;
111. while(temp -> next != head)
112. {
113. temp = temp -> next;
114. }
115. temp -> next = ptr;
116. ptr -> next = head;
117. }
118.
119. printf("\nnode inserted\n");
120. }
121.
122. }
123.
124. void begin_delete()
125. {
126. struct node *ptr;
127. if(head == NULL)
128. {
129. printf("\nUNDERFLOW");
130. }
131. else if(head->next == head)
132. {
133. head = NULL;
134. free(head);
135. printf("\nnode deleted\n");
136. }
137.
138. else
139. { ptr = head;
140. while(ptr -> next != head)
141. ptr = ptr -> next;
142. ptr->next = head->next;
143. free(head);
144. head = ptr->next;
145. printf("\nnode deleted\n");
146.
147. }
148. }
149. void last_delete()
150. {
151. struct node *ptr, *preptr;
152. if(head==NULL)
153. {
154. printf("\nUNDERFLOW");
155. }
156. else if (head ->next == head)
157. {
158. head = NULL;
159. free(head);
160. printf("\nnode deleted\n");
161.
162. }
163. else
164. {
165. ptr = head;
166. while(ptr ->next != head)
167. {
168. preptr=ptr;
169. ptr = ptr->next;
170. }
171. preptr->next = ptr -> next;
172. free(ptr);
173. printf("\nnode deleted\n");
174.
175. }
176. }
177.
178. void search()
179. {
180. struct node *ptr;
181. int item,i=0,flag=1;
182. ptr = head;
183. if(ptr == NULL)
184. {
185. printf("\nEmpty List\n");
186. }
187. else
188. {
189. printf("\nEnter item which you want to search?\n");
190. scanf("%d",&item);
191. if(head ->data == item)
192. {
193. printf("item found at location %d",i+1);
194. flag=0;
195. }
196. else
197. {
198. while (ptr->next != head)
199. {
200. if(ptr->data == item)
201. {
202. printf("item found at location %d ",i+1);
203. flag=0;
204. break;
205. }
206. else
207. {
208. flag=1;
209. }
210. i++;
211. ptr = ptr -> next;
212. }
213. }
214. if(flag != 0)
215. {
216. printf("Item not found\n");
217. }
218. }
219.
220. }
221.
222. void display()
223. {
224. struct node *ptr;
225. ptr=head;
226. if(head == NULL)
227. {
228. printf("\nnothing to print");
229. }
230. else
231. {
232. printf("\n printing values ... \n");
233.
234. while(ptr -> next != head)
235. {
236.
237. printf("%d\n", ptr -> data);
238. ptr = ptr -> next;
239. }
240. printf("%d\n", ptr -> data);
241. }
242.
243. }
Output:
*********Main Menu*********
1.Insert in begining
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search for an element
6.Show
7.Exit
node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search for an element
6.Show
7.Exit
Enter Data?20
node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search for an element
6.Show
7.Exit
Enter Data?30
node inserted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search for an element
6.Show
7.Exit
node deleted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search for an element
6.Show
7.Exit
Enter your choice?
4
node deleted
*********Main Menu*********
===============================================
1.Insert in begining
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search for an element
6.Show
7.Exit
===============================================
1.Insert in begining
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search for an element
6.Show
7.Exit
*********Main Menu*********
Choose one option from the following list ...
===============================================
1.Insert in begining
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search for an element
6.Show
7.Exit
Circular doubly linked list is a more complexed type of data structure in which a node contain
pointers to its previous node as well as the next node. Circular doubly linked list doesn't contain
NULL in any of the node. The last node of the list contains the address of the first node of the
list. The first node of the list also contain address of the last node in its previous pointer.
Due to the fact that a circular doubly linked list contains three parts in its structure therefore, it
demands more space per node and more expensive basic operations. However, a circular doubly
linked list provides easy manipulation of the pointers and the searching becomes twice as
efficient.
Memory Management of Circular Doubly linked list
The following figure shows the way in which the memory is allocated for a circular doubly
linked list. The variable head contains the address of the first element of the list i.e. 1 hence the
starting node of the list contains data A is stored at address 1. Since, each node of the list is
supposed to have three parts therefore, the starting node of the list contains address of the last
node i.e. 8 and the next node i.e. 4. The last node of the list that is stored at address 8 and
containing data as 6, contains address of the first node of the list as shown in the image i.e. 1. In
circular doubly linked list, the last node is identified by the address of the first node which is
stored in the next part of the last node therefore the node which contains the address of the first
node, is actually the last node of the list.
Advertisement
Operations on circular doubly linked list :
There are various operations which can be performed on circular doubly linked list. The node
structure of a circular doubly linked list is similar to doubly linked list. However, the operations
on circular doubly linked list is described in the following table.
SN Operation Description
2 Insertion at end Adding a node in circular doubly linked list at the end.
Traversing and searching in circular doubly linked list is similar to that in the circular singly
linked list.
1. #include<stdio.h>
2. #include<stdlib.h>
3. struct node
4. {
5. struct node *prev;
6. struct node *next;
7. int data;
8. };
9. struct node *head;
10. void insertion_beginning();
11. void insertion_last();
12. void deletion_beginning();
13. void deletion_last();
14. void display();
15. void search();
16. void main ()
17. {
18. int choice =0;
19. while(choice != 9)
20. {
21. printf("\n*********Main Menu*********\n");
22. printf("\nChoose one option from the following list ...\n");
23. printf("\n===============================================\n");
24. printf("\n1.Insert in Beginning\n2.Insert at last\n3.Delete from Beginning\n4.Delete from la
st\n5.Search\n6.Show\n7.Exit\n");
25. printf("\nEnter your choice?\n");
26. scanf("\n%d",&choice);
27. switch(choice)
28. {
29. case 1:
30. insertion_beginning();
31. break;
32. case 2:
33. insertion_last();
34. break;
35. case 3:
36. deletion_beginning();
37. break;
38. case 4:
39. deletion_last();
40. break;
41. case 5:
42. search();
43. break;
44. case 6:
45. display();
46. break;
47. case 7:
48. exit(0);
49. break;
50. default:
51. printf("Please enter valid choice..");
52. }
53. }
54. }
55. void insertion_beginning()
56. {
57. struct node *ptr,*temp;
58. int item;
59. ptr = (struct node *)malloc(sizeof(struct node));
60. if(ptr == NULL)
61. {
62. printf("\nOVERFLOW");
63. }
64. else
65. {
66. printf("\nEnter Item value");
67. scanf("%d",&item);
68. ptr->data=item;
69. if(head==NULL)
70. {
71. head = ptr;
72. ptr -> next = head;
73. ptr -> prev = head;
74. }
75. else
76. {
77. temp = head;
78. while(temp -> next != head)
79. {
80. temp = temp -> next;
81. }
82. temp -> next = ptr;
83. ptr -> prev = temp;
84. head -> prev = ptr;
85. ptr -> next = head;
86. head = ptr;
87. }
88. printf("\nNode inserted\n");
89. }
90.
91. }
92. void insertion_last()
93. {
94. struct node *ptr,*temp;
95. int item;
96. ptr = (struct node *) malloc(sizeof(struct node));
97. if(ptr == NULL)
98. {
99. printf("\nOVERFLOW");
100. }
101. else
102. {
103. printf("\nEnter value");
104. scanf("%d",&item);
105. ptr->data=item;
106. if(head == NULL)
107. {
108. head = ptr;
109. ptr -> next = head;
110. ptr -> prev = head;
111. }
112. else
113. {
114. temp = head;
115. while(temp->next !=head)
116. {
117. temp = temp->next;
118. }
119. temp->next = ptr;
120. ptr ->prev=temp;
121. head -> prev = ptr;
122. ptr -> next = head;
123. }
124. }
125. printf("\nnode inserted\n");
126. }
127.
128. void deletion_beginning()
129. {
130. struct node *temp;
131. if(head == NULL)
132. {
133. printf("\n UNDERFLOW");
134. }
135. else if(head->next == head)
136. {
137. head = NULL;
138. free(head);
139. printf("\nnode deleted\n");
140. }
141. else
142. {
143. temp = head;
144. while(temp -> next != head)
145. {
146. temp = temp -> next;
147. }
148. temp -> next = head -> next;
149. head -> next -> prev = temp;
150. free(head);
151. head = temp -> next;
152. }
153.
154. }
155. void deletion_last()
156. {
157. struct node *ptr;
158. if(head == NULL)
159. {
160. printf("\n UNDERFLOW");
161. }
162. else if(head->next == head)
163. {
164. head = NULL;
165. free(head);
166. printf("\nnode deleted\n");
167. }
168. else
169. {
170. ptr = head;
171. if(ptr->next != head)
172. {
173. ptr = ptr -> next;
174. }
175. ptr -> prev -> next = head;
176. head -> prev = ptr -> prev;
177. free(ptr);
178. printf("\nnode deleted\n");
179. }
180. }
181.
182. void display()
183. {
184. struct node *ptr;
185. ptr=head;
186. if(head == NULL)
187. {
188. printf("\nnothing to print");
189. }
190. else
191. {
192. printf("\n printing values ... \n");
193.
194. while(ptr -> next != head)
195. {
196.
197. printf("%d\n", ptr -> data);
198. ptr = ptr -> next;
199. }
200. printf("%d\n", ptr -> data);
201. }
202.
203. }
204.
205. void search()
206. {
207. struct node *ptr;
208. int item,i=0,flag=1;
209. ptr = head;
210. if(ptr == NULL)
211. {
212. printf("\nEmpty List\n");
213. }
214. else
215. {
216. printf("\nEnter item which you want to search?\n");
217. scanf("%d",&item);
218. if(head ->data == item)
219. {
220. printf("item found at location %d",i+1);
221. flag=0;
222. }
223. else
224. {
225. while (ptr->next != head)
226. {
227. if(ptr->data == item)
228. {
229. printf("item found at location %d ",i+1);
230. flag=0;
231. break;
232. }
233. else
234. {
235. flag=1;
236. }
237. i++;
238. ptr = ptr -> next;
239. }
240. }
241. if(flag != 0)
242. {
243. printf("Item not found\n");
244. }
245. }
246.
247. }
Output:
*********Main Menu*********
===============================================
1.Insert in Beginning
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search
6.Show
7.Exit
Node inserted
*********Main Menu*********
===============================================
1.Insert in Beginning
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search
6.Show
7.Exit
Enter value234
node inserted
*********Main Menu*********
===============================================
1.Insert in Beginning
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search
6.Show
7.Exit
Node inserted
*********Main Menu*********
===============================================
1.Insert in Beginning
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search
6.Show
7.Exit
Enter value80
node inserted
*********Main Menu*********
===============================================
1.Insert in Beginning
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search
6.Show
7.Exit
*********Main Menu*********
1.Insert in Beginning
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search
6.Show
7.Exit
node deleted
*********Main Menu*********
===============================================
1.Insert in Beginning
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search
6.Show
7.Exit
*********Main Menu*********
===============================================
1.Insert in Beginning
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search
6.Show
7.Exit
============================================
1.Insert in Beginning
2.Insert at last
3.Delete from Beginning
4.Delete from last
5.Search
6.Show
7.Exit