0% found this document useful (0 votes)
3 views

Data Structure UNIT-1

Data structures are systematic ways to organize data for efficient use, characterized by correctness, time complexity, and space complexity. They address common problems in complex applications, such as data search and multiple requests, by optimizing data organization. The document also covers algorithms, their characteristics, efficiency analysis, and the importance of arrays in data structures.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

Data Structure UNIT-1

Data structures are systematic ways to organize data for efficient use, characterized by correctness, time complexity, and space complexity. They address common problems in complex applications, such as data search and multiple requests, by optimizing data organization. The document also covers algorithms, their characteristics, efficiency analysis, and the importance of arrays in data structures.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 29

UNIT 1

Data Structure:
Data Structure is a systematic way to organize data in order to use it efficiently. Following terms are the
foundation terms of a data structure.
 Interface − each data structure has an interface. Interface represents the set of operations that
a data structure supports. An interface only provides the list of supported operations, type of
parameters they can accept and return type of these operations.
 Implementation − Implementation provides the internal representation of a data structure.
Implementation also provides the definition of the algorithms used in the operations of the data
structure.

Characteristics of a Data Structure


 Correctness − Data structure implementation should implement its interface correctly.
 Time Complexity − Running time or the execution time of operations of data structure must be
as small as possible.
 Space Complexity − Memory usage of a data structure operation should be as little as possible.

Need for Data Structure


As applications are getting complex and data rich, there are three common problems that applications
face now-a-days.

 Data Search − Consider an inventory of 1 million(106) items of a store. If the application is to


search an item, it has to search an item in 1 million(106) items every time slowing down the
search. As data grows, search will become slower.
 Processor speed − Processor speed although being very high, falls limited if the data grows to
billion records.
 Multiple requests − As thousands of users can search data simultaneously on a web server, even
the fast server fails while searching the data.
 To solve the above-mentioned problems, data structures come to rescue. Data can be organized
in a data structure in such a way that all items may not be required to be searched, and the
required data can be searched almost instantly.

Execution Time Cases

There are three cases which are usually used to compare various data structure's execution time in a
relative manner.

 Worst Case − This is the scenario where a particular data structure operation takes maximum
time it can take. If an operation's worst case time is ƒ(n) then this operation will not take more
than ƒ(n) time where ƒ(n) represents function of n.
 Average Case − This is the scenario depicting the average execution time of an operation of a
data structure. If an operation takes ƒ(n) time in execution, then m operations will take mƒ(n)
time.
 Best Case − This is the scenario depicting the least possible execution time of an operation of a
data structure. If an operation takes ƒ(n) time in execution, then the actual operation may take
time as the random number which would be maximum as ƒ(n).

Data Types in C
Each variable in C has an associated data type. Each data type requires different amounts of memory
and has some specific operations which can be performed over it. It specifies the type of data that the
variable can store like integer, character, floating, double, etc. The data type is a collection of data with
values having fixed values, meaning as well as its characteristics.

The data types in C can be classified as follows:

Types Description

Primitive Data
Types Arithmetic types can be further classified into integer and floating data types.

The data type has no value or operator and it does not provide a result to its caller. But
Void Types void comes under Primitive data types.

User Defined It is mainly used to assign names to integral constants, which make a program easy to
DataTypes read and maintain

The data types that are derived from the primitive or built-in datatypes are referred to
Derived types as Derived Data Types.
Different data types also have different ranges up to which they can store numbers. These ranges may
vary from compiler to compiler. Below is a list of ranges along with the memory requirement and format
specifiers.

Data Type Memory (bytes) Range Format Specifier

short int 2 -32,768 to 32,767 %hd

unsigned short int 2 0 to 65,535 %hu

unsigned int 4 0 to 4,294,967,295 %u

Int 4 -2,147,483,648 to 2,147,483,647 %d

long int 4 -2,147,483,648 to 2,147,483,647 %ld

unsigned long int 4 0 to 4,294,967,295 %lu


long long int 8 -(2^63) to (2^63)-1 %lld

unsigned long long int 8 0 to 18,446,744,073,709,551,615 %llu

signed char 1 -128 to 127 %c

unsigned char 1 0 to 255 %c

float 4 1.2E-38 to 3.4E+38 %f

Double 8 1.7E-308 to 1.7E+308 %lf

long double 16 3.4E-4932 to 1.1E+4932 %Lf

Algorithm:
Algorithm is a step-by-step procedure, which defines a set of instructions to be executed in a certain
order to get the desired output. Algorithms are generally created independent of underlying languages,
i.e. an algorithm can be implemented in more than one programming language.

From the data structure point of view, following are some important categories of algorithms −

 Search − Algorithm to search an item in a data structure.

 Sort − Algorithm to sort items in a certain order.

 Insert − Algorithm to insert item in a data structure.

 Update − Algorithm to update an existing item in a data structure.

 Delete − Algorithm to delete an existing item from a data structure.

Characteristics of an Algorithm

Not all procedures can be called an algorithm. An algorithm should have the following characteristics −
 Unambiguous − Algorithm should be clear and unambiguous. Each of its steps (or phases), and
their inputs/outputs should be clear and must lead to only one meaning.

 Input − an algorithm should have 0 or more well-defined inputs.

 Output − an algorithm should have 1 or more well-defined outputs, and should match the
desired output.

 Finiteness − Algorithms must terminate after a finite number of steps.

 Feasibility − should be feasible with the available resources.

 Independent − an algorithm should have step-by-step directions, which should be independent


of any programming code.

Efficiency of Algorithm:
Efficiency of an algorithm can be analyzed at two different stages, before implementation and after
implementation. They are the following −

 A Priori Analysis − This is a theoretical analysis of an algorithm. Efficiency of an algorithm is


measured by assuming that all other factors, for example, processor speed, are constant and
have no effect on the implementation.

 A Posterior Analysis − This is an empirical analysis of an algorithm. The selected algorithm is


implemented using programming language. This is then executed on target computer machine.
In this analysis, actual statistics like running time and space required, are collected.

Algorithm Complexity

Suppose X is an algorithm and n is the size of input data, the time and space used by the algorithm X are
the two main factors, which decide the efficiency of X.

 Time Factor − Time is measured by counting the number of key operations such as comparisons
in the sorting algorithm.

 Space Factor − Space is measured by counting the maximum memory space required by the
algorithm.

The complexity of an algorithm f(n) gives the running time and/or the storage space required by the
algorithm in terms of n as the size of input data.

Space Complexity

Space complexity of an algorithm represents the amount of memory space required by the algorithm in
its life cycle. The space required by an algorithm is equal to the sum of the following two components −
 A fixed part that is a space required to store certain data and variables, that are independent of
the size of the problem. For example, simple variables and constants used, program size, etc.

 A variable part is a space required by variables, whose size depends on the size of the problem.
For example, dynamic memory allocation, recursion stack space, etc.

Space complexity S(P) of any algorithm P is S(P) = C + SP(I), where C is the fixed part and S(I) is the
variable part of the algorithm, which depends on instance characteristic I. Following is a simple example
that tries to explain the concept −

Algorithm: SUM(A, B)

Step 1 - START

Step 2 - C ← A + B + 10

Step 3 - Stop

Here we have three variables A, B, and C and one constant. Hence S(P) = 1 + 3. Now, space depends on
data types of given variables and constant types and it will be multiplied accordingly.

Time Complexity

Time complexity of an algorithm represents the amount of time required by the algorithm to run to
completion. Time requirements can be defined as a numerical function T(n), where T(n) can be
measured as the number of steps, provided each step consumes constant time.

For example, addition of two n-bit integers takes n steps. Consequently, the total computational time is
T(n) = c ∗ n, where c is the time taken for the addition of two bits. Here, we observe that T(n) grows
linearly as the input size increases.

Asymptotic Analysis:
Asymptotic analysis of an algorithm refers to defining the mathematical boundation/framing of its run-
time performance. Using asymptotic analysis, we can very well conclude the best case, average case,
and worst case scenario of an algorithm.

Asymptotic analysis is input bound i.e., if there's no input to the algorithm, it is concluded to work in a
constant time. Other than the "input" all other factors are considered constant.

Asymptotic analysis refers to computing the running time of any operation in mathematical units of
computation. For example, the running time of one operation is computed as f(n) and may be for
another operation it is computed as g(n2). This means the first operation running time will increase
linearly with the increase in n and the running time of the second operation will increase exponentially
when n increases. Similarly, the running time of both operations will be nearly the same if n is
significantly small.
Usually, the time required by an algorithm falls under three types −

 Best Case − Minimum time required for program execution.

 Average Case − Average time required for program execution.

 Worst Case − Maximum time required for program execution.

Asymptotic Notations
Following are the commonly used asymptotic notations to calculate the running time complexity of an
algorithm.

 Ο Notation

 Ω Notation

 θ Notation

Big Oh Notation, Ο

The notation Ο(n) is the formal way to express the upper bound of an algorithm's running time. It
measures the worst case time complexity or the longest amount of time an algorithm can possibly take
to complete.

For example, for a function f(n)

Ο(f(n)) = { g(n) : there exists c > 0 and n0 such that f(n) ≤ c.g(n) for all n > n0. }

Omega Notation, Ω

The notation Ω(n) is the formal way to express the lower bound of an algorithm's running time. It
measures the best case time complexity or the best amount of time an algorithm can possibly take to
complete.
For example, for a function f(n)

Ω(f(n)) ≥ { g(n) : there exists c > 0 and n0 such that g(n) ≤ c.f(n) for all n > n0. }

Theta Notation, θ

The notation θ(n) is the formal way to express both the lower bound and the upper bound of an
algorithm's running time. It is represented as follows −

θ(f(n)) = { g(n) if and only if g(n) = Ο(f(n)) and g(n) = Ω(f(n)) for all n > n0. }

Common Asymptotic Notations

Following is a list of some common asymptotic notations −

constant − Ο(1)

logarithmic − Ο(log n)
linear − Ο(n)

n log n − Ο(n log n)

quadratic − Ο(n2)

cubic − Ο(n3)

polynomial − nΟ(1)

exponential − 2Ο(n)

Time-Space Trade-Off in Algorithms:


A tradeoff is a situation where one thing increases and another thing decreases. It is a way to solve a
problem in:

 Either in less time and by using more space, or

 In very little space by spending a long amount of time.

The best Algorithm is that which helps to solve a problem that requires less space in memory and also
takes less time to generate the output. But in general, it is not always possible to achieve both of these
conditions at the same time. The most common condition is an algorithm using a lookup table. This
means that the answers to some questions for every possible value can be written down. One way of
solving this problem is to write down the entire lookup table, which will let you find answers very
quickly but will use a lot of space. Another way is to calculate the answers without writing down
anything, which uses very little space, but might take a long time. Therefore, the more time-efficient
algorithms you have, that would be less space-efficient.

Array in Data Structure


Arrays are defined as the collection of similar types of data items stored at contiguous memory
locations. It is one of the simplest data structures where each data element can be randomly accessed
by using its index number.

In C programming, they are the derived data types that can store the primitive type of data such as int,
char, double, float, etc. For example, if we want to store the marks of a student in 6 subjects, then we
don't need to define a different variable for the marks in different subjects. Instead, we can define an
array that can store the marks in each subject at the contiguous memory locations.

Properties of array

There are some of the properties of an array that are listed as follows -

o Each element in an array is of the same data type and carries the same size that is 4 bytes.

o Elements in the array are stored at contiguous memory locations from which the first element is
stored at the smallest memory location.

o Elements of the array can be randomly accessed since we can calculate the address of each
element of the array with the given base address and the size of the data element.

Representation of an array

We can represent an array in various ways in different programming languages. As an illustration, let's
see the declaration of array in C language -

As per the above illustration, there are some of the following important points -

o Index starts with 0.

o The array's length is 10, which means we can store 10 elements.

o Each element in the array can be accessed via its index.

Why are arrays required?


Arrays are useful because -

o Sorting and searching a value in an array is easier.

o Arrays are best to process multiple values quickly and easily.

o Arrays are good for storing multiple values in a single variable - In computer programming,
most cases require storing a large number of data of a similar type. To store such an amount of
data, we need to define a large number of variables. It would be very difficult to remember the
names of all the variables while writing the programs. Instead of naming all the variables with a
different name, it is better to define an array and store all the elements into it.

Memory allocation of an array

As stated above, all the data elements of an array are stored at contiguous locations in the main
memory. The name of the array represents the base address or the address of the first element in the
main memory. Each element of the array is represented by proper indexing.

We can define the indexing of an array in the below ways -

1. 0 (zero-based indexing): The first element of the array will be arr[0].

2. 1 (one-based indexing): The first element of the array will be arr[1].

3. n (n - based indexing): The first element of the array can reside at any random index number.

In the above image, we have shown the memory allocation of an array arr of size 5. The array follows a
0-based indexing approach. The base address of the array is 100 bytes. It is the address of arr[0]. Here,
the size of the data type used is 4 bytes; therefore, each element will take 4 bytes in the memory.

The formula to calculate the address to access an array element -

Byte address of element A[i] = base address + size * (i - first index)

Basic operations:
Now, let's discuss the basic operations supported in the array -

o Traversal - This operation is used to print the elements of the array.

o Insertion - It is used to add an element at a particular index.

o Deletion - It is used to delete an element from a particular index.

o Search - It is used to search an element using the given index or by the value.
o Update - It updates an element at a particular index.

Two Dimensional Array:


The two-dimensional array can be defined as an array of arrays. The 2D array is organized as matrices
which can be represented as the collection of rows and columns. However, 2D arrays are created to
implement a relational database lookalike data structure. It provides ease of holding the bulk of data at
once which can be passed to any number of functions wherever required.

Declaration of two dimensional Array in C

The syntax to declare the 2D array is given below.

data_type array_name[rows][columns];

Consider the following example.

int arr[4][3];

Here, 4 is the number of rows, and 3 is the number of columns.

Calculate the address of any element in the 2-D array:

The 2-dimensional array can be defined as an array of arrays. The 2-Dimensional arrays are organized as
matrices which can be represented as the collection of rows and columns as array[M][N] where M is the
number of rows and N is the number of columns.

Example:

To find the address of any element in a 2-Dimensional array there are the following two ways-

1. Row Major Order

2. Column Major Order

1. Row Major Order:


Row Major ordering assigns successive elements, moving across the rows and then down the next row,
to successive memory locations. In simple language, the elements of an array are stored in a Row-Wise
fashion.
To find the address of the element using row-major order uses the following formula:

Address of A[I][J] = B + W * ((I – LR) * N + (J – LC))

I = Row Subset of an element whose address to be found,


J = Column Subset of an element whose address to be found,
B = Base address,
W = Storage size of one element store in an array(in byte),
LR = Lower Limit of row/start row index of the matrix(If not given assume it as zero),
LC = Lower Limit of column/start column index of the matrix(If not given assume it as zero),
N = Number of column given in the matrix.

Example: Given an array, arr[1………10][1………15] with base value 100 and the size of each element is 1
Byte in memory. Find the address of arr[8][6] with the help of row-major order.

Solution:

Given:
Base address B = 100
Storage size of one element store in any array W = 1 Bytes
Row Subset of an element whose address to be found I = 8
Column Subset of an element whose address to be found J = 6
Lower Limit of row/start row index of matrix LR = 1
Lower Limit of column/start column index of matrix = 1
Number of column given in the matrix N = Upper Bound – Lower Bound + 1
= 15 – 1 + 1
= 15

Formula:
Address of A[I][J] = B + W * ((I – LR) * N + (J – LC))

Solution:
Address of A[8][6] = 100 + 1 * ((8 – 1) * 15 + (6 – 1))
= 100 + 1 * ((7) * 15 + (5))
= 100 + 1 * (110)
Address of A[I][J] = 210

2. Column Major Order:

If elements of an array are stored in a column-major fashion means moving across the column and then
to the next column then it’s in column-major order. To find the address of the element using column-
major order use the following formula:
Address of A[I][J] = B + W * ((J – LC) * M + (I – LR))

I = Row Subset of an element whose address to be found,


J = Column Subset of an element whose address to be found,
B = Base address,
W = Storage size of one element store in any array(in byte),
LR = Lower Limit of row/start row index of matrix(If not given assume it as zero),
LC = Lower Limit of column/start column index of matrix(If not given assume it as zero),
M = Number of rows given in the matrix.

Example: Given an array arr[1………10][1………15] with a base value of 100 and the size of each element
is 1 Byte in memory find the address of arr[8][6] with the help of column-major order.

Solution:

Given:
Base address B = 100
Storage size of one element store in any array W = 1 Bytes
Row Subset of an element whose address to be found I = 8
Column Subset of an element whose address to be found J = 6
Lower Limit of row/start row index of matrix LR = 1
Lower Limit of column/start column index of matrix = 1
Number of Rows given in the matrix M = Upper Bound – Lower Bound + 1
= 10 – 1 + 1
= 10

Formula: used
Address of A[I][J] = B + W * ((J – LC) * M + (I – LR))
Address of A[8][6] = 100 + 1 * ((6 – 1) * 10 + (8 – 1))
= 100 + 1 * ((5) * 10 + (7))
= 100 + 1 * (57)
Address of A[I][J] = 157

From the above examples, it can be observed that for the same position two different address locations
are obtained that’s because in row-major order movement is done across the rows and then down to
the next row, and in column-major order, first move down to the first column and then next column. So
both the answers are right.

So it’s all based on the position of the element whose address is to be found for some cases the same
answers is also obtained with row-major order and column-major order and for some cases, different
answers are obtained.
Applications of Array Data Structure:

Below are some applications of arrays.

 Storing and accessing data: Arrays are used to store and retrieve data in a specific order. For
example, an array can be used to store the scores of a group of students, or the temperatures
recorded by a weather station.

 Sorting: Arrays can be used to sort data in ascending or descending order. Sorting algorithms
such as bubble sort, merge sort, and quicksort rely heavily on arrays.

 Searching: Arrays can be searched for specific elements using algorithms such as linear search
and binary search.

 Matrices: Arrays are used to represent matrices in mathematical computations such as matrix
multiplication, linear algebra, and image processing.

 Stacks and queues: Arrays are used as the underlying data structure for implementing stacks
and queues, which are commonly used in algorithms and data structures.

 Graphs: Arrays can be used to represent graphs in computer science. Each element in the array
represents a node in the graph, and the relationships between the nodes are represented by the
values stored in the array.

 Dynamic programming: Dynamic programming algorithms often use arrays to store


intermediate results of subproblems in order to solve a larger problem.

Below are some real-time applications of arrays.

 Signal Processing: Arrays are used in signal processing to represent a set of samples that are
collected over time. This can be used in applications such as speech recognition, image
processing, and radar systems.
 Multimedia Applications: Arrays are used in multimedia applications such as video and audio
processing, where they are used to store the pixel or audio samples. For example, an array can
be used to store the RGB values of an image.
 Data Mining: Arrays are used in data mining applications to represent large datasets. This allows
for efficient data access and processing, which is important in real-time applications.
 Robotics: Arrays are used in robotics to represent the position and orientation of objects in 3D
space. This can be used in applications such as motion planning and object recognition.
 Real-time Monitoring and Control Systems: Arrays are used in real-time monitoring and control
systems to store sensor data and control signals. This allows for real-time processing and
decision-making, which is important in applications such as industrial automation and aerospace
systems.
 Financial Analysis: Arrays are used in financial analysis to store historical stock prices and other
financial data. This allows for efficient data access and analysis, which is important in real-time
trading systems.
 Scientific Computing: Arrays are used in scientific computing to represent numerical data, such
as measurements from experiments and simulations. This allows for efficient data processing
and visualization, which is important in real-time scientific analysis and experimentation.

Advantages of array data structure:

 Efficient access to elements: Arrays provide direct and efficient access to any element in the
collection. Accessing an element in an array is an O(1) operation, meaning that the time
required to access an element is constant and does not depend on the size of the array.

 Fast data retrieval: Arrays allow for fast data retrieval because the data is stored in contiguous
memory locations. This means that the data can be accessed quickly and efficiently without the
need for complex data structures or algorithms.

 Memory efficiency: Arrays are a memory-efficient way of storing data. Because the elements of
an array are stored in contiguous memory locations, the size of the array is known at compile
time. This means that memory can be allocated for the entire array in one block, reducing
memory fragmentation.

 Versatility: Arrays can be used to store a wide range of data types, including integers, floating-
point numbers, characters, and even complex data structures such as objects and pointers.

 Easy to implement: Arrays are easy to implement and understand, making them an ideal choice
for beginners learning computer programming.

 Compatibility with hardware: The array data structure is compatible with most hardware
architectures, making it a versatile tool for programming in a wide range of environments.

Disadvantages of array data structure:

 Fixed size: Arrays have a fixed size that is determined at the time of creation. This means that if
the size of the array needs to be increased, a new array must be created and the data must be
copied from the old array to the new array, which can be time-consuming and memory-
intensive.

 Memory allocation issues: Allocating a large array can be problematic, particularly in systems
with limited memory. If the size of the array is too large, the system may run out of memory,
which can cause the program to crash.

 Insertion and deletion issues: Inserting or deleting an element from an array can be inefficient
and time-consuming because all the elements after the insertion or deletion point must be
shifted to accommodate the change.
 Wasted space: If an array is not fully populated, there can be wasted space in the memory
allocated for the array. This can be a concern if memory is limited.

 Limited data type support: Arrays have limited support for complex data types such as objects
and structures, as the elements of an array must all be of the same data type.

 Lack of flexibility: The fixed size and limited support for complex data types can make arrays
inflexible compared to other data structures such as linked lists and trees.

Sparse Matrix & Its Representation:

A matrix is a two-dimensional data object made of m rows and n columns, therefore having total m x n
values. If most of the elements of the matrix have 0 value, then it is called a sparse matrix.

Why to use Sparse Matrix instead of simple matrix ?

Storage: There are lesser non-zero elements than zeros and thus lesser memory can be used to store
only those elements.

Computing time: Computing time can be saved by logically designing a data structure traversing only
non-zero elements.

Example:
00304
00570
00000
02600

Representing a sparse matrix by a 2D array leads to wastage of lots of memory as zeroes in the matrix
are of no use in most of the cases. So, instead of storing zeroes with non-zero elements, we only store
non-zero elements. This means storing non-zero elements with triples- (Row, Column, value).

2D array is used to represent a sparse matrix in which there are three rows named as

 Row: Index of row, where non-zero element is located

 Column: Index of column, where non-zero element is located

 Value: Value of the non zero element located at index – (row,column)


Linked List:
A linked list is a linear data structure, in which the elements are not stored at contiguous
memory locations. The elements in a linked list are linked using pointers as shown in the below
image:

In simple words, a linked list consists of nodes where each node contains a data field and a
reference (link) to the next node in the list.

Why use linked list over array?

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.

Array contains following limitations:

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.

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.
SN Operation Description

1 Insertion at It involves inserting any element at the front of the list. We just need to a few link
beginning 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 inserted as
the list 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 skip the
specified node desired number of nodes in order to reach the node after which the new node will
be inserted. .

Deletion and Traversing

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.

SN Operation Description

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. .
Linked list program
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
};
struct node *head;
void beginsert ();
void lastinsert ();
void randominsert();
void begin_delete();
void last_delete();
void random_delete();
void display();
void search();
void main ()
{
int choice =0;
while(choice != 9)
{
printf("\n\n*********Main Menu*********\n");
printf("\nChoose one option from the following list ...\n");
printf("\n===============================================\n");
printf("\n 1.Insert in begining\n 2.Insert at last\n 3.Insert at any random location\n4.Delete from
Beginning\n 5.Delete from last\n 6.Delete node after specified location\n 7.Search for an element\n
8.Show\n 9.Exit\n");
printf("\nEnter your choice?\n");
scanf("\n%d",&choice);
switch(choice)
{
case 1: beginsert();
break;
case 2: lastinsert();
break;
case 3: randominsert();
break;
case 4: begin_delete();
break;
case 5: last_delete();
break;
case 6: random_delete();
break;
case 7: search();
break;
case 8: display();
break;
case 9: exit(0);
break;
default: printf("Please enter valid choice..");
}
}
}
void beginsert()
{
struct node *ptr;
int item;
ptr = (struct node *) malloc(sizeof(struct node ));
if(ptr == NULL)
{
printf("\nOVERFLOW");
}
else
{
printf("\nEnter value\n");
scanf("%d",&item);
ptr->data = item;
ptr->next =head;
head = ptr;
printf("\nNode inserted");
}
}
void lastinsert()
{
struct node *ptr,*temp;
int item;
ptr = (struct node*)malloc(sizeof(struct node));
if(ptr == NULL)
{
printf("\nOVERFLOW");
}
else
{
printf("\nEnter value?\n");
scanf("%d",&item);
ptr->data = item;
if(head == NULL)
{
ptr -> next = NULL;
head = ptr;
printf("\nNode inserted");
}
else
{
temp = head;
while (temp -> next != NULL)
{
temp = temp -> next;
}
temp->next = ptr;
ptr->next = NULL;
printf("\nNode inserted");
}
}
}
void randominsert()
{
int i,loc,item;
struct node *ptr, *temp;
ptr = (struct node *) malloc (sizeof(struct node));
if(ptr == NULL)
{
printf("\nOVERFLOW");
}
else
{
printf("\nEnter element value");
scanf("%d",&item);
ptr->data = item;
printf("\nEnter the location after which you want to insert ");
scanf("\n%d",&loc);
temp=head;
for(i=0;i<loc;i++)
{
temp = temp->next;
if(temp == NULL)
{
printf("\n can't insert\n");
return;
}
}
ptr ->next = temp ->next;
temp ->next = ptr;
printf("\nNode inserted");
}
}
void begin_delete()
{
struct node *ptr;
if(head == NULL)
{
printf("\n List is empty\n");
}
else
{
ptr = head;
head = ptr->next;
free(ptr);
printf("\nNode deleted from the begining ...\n");
}
}
void last_delete()
{
struct node *ptr,*ptr1;
if(head == NULL)
{
printf("\nlist is empty");
}
else if(head -> next == NULL)
{
head = NULL;
free(head);
printf("\nOnly node of the list deleted ...\n");
}
else
{
ptr = head;
while(ptr->next != NULL)
{
ptr1 = ptr;
ptr = ptr ->next;
}
ptr1->next = NULL;
free(ptr);
printf("\nDeleted Node from the last ...\n");
}
}
void random_delete()
{
struct node *ptr,*ptr1;
int loc,i;
printf("\n Enter the location of the node after which you want to perform deletion \n");
scanf("%d",&loc);
ptr=head;
for(i=0;i<=loc;i++)
{
ptr1 = ptr;
ptr = ptr->next;

if(ptr == NULL)
{
printf("\nCan't delete");
return;
}
}
ptr1 ->next = ptr ->next;
free(ptr);
printf("\nDeleted node %d ",loc+1);
}
void search()
{
struct node *ptr;
int item,i=0,flag;
ptr = head;
if(ptr == NULL)
{
printf("\nEmpty List\n");
}
else
{
printf("\nEnter item which you want to search?\n");
scanf("%d",&item);
while (ptr!=NULL)
{
if(ptr->data == item)
{
printf("item found at location %d ",i+1);
flag=0;
}
else
{
flag=1;
}
i++;
ptr = ptr -> next;
}
if(flag==1)
{
printf("Item not found\n");
}
}
}
void display()
{
struct node *ptr;
ptr = head;
if(ptr == NULL)
{
printf("Nothing to print");
}
else
{
printf("\nprinting values . . . . .\n");
while (ptr!=NULL)
{
printf("\n%d",ptr->data);
ptr = ptr -> next;
}
}
}
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) , 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 :

struct node
{
struct node *prev;
int data;
struct node *next;
}
The prev part of the first node and the next part of the last node will always contain null indicating end
in each direction.

In a singly linked list, we could traverse only in one direction, because each node contains address of the
next node and it doesn't have any record of its previous nodes. However, doubly 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

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).

Operations on doubly linked list

Node Creation

struct node
{
struct node *prev;
int data;
struct node *next;
};
struct node *head;

All the remaining operations regarding doubly linked list are described in the following table.

SN Operation Description

1 Insertion at beginning Adding the node into the linked list at beginning.

2 Insertion at end Adding the node into the linked list to the end.

3 Insertion after specified Adding the node into the linked list after the specified node.
node

4 Deletion at beginning Removing the node from beginning of the list

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 containing
having given data the given data.

7 Searching Comparing each node data with the item to be searched and return
the location of the item in the list if the item found else return null.

8 Traversing Visiting each node of the list at least once in order to perform some
specific operation like searching, sorting, display, etc.
Circular Linked List
The circular linked list is a linked list where all nodes are connected to form a circle. In a circular linked
list, the first node and the last node are connected to each other which forms a circle. There is no NULL
at the end.

There are generally two types of circular linked lists:

 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 traverse the circular singly linked list until we reach the
same node where we started. The circular singly linked list has no beginning or end. No null
value is present in the next part of any of the nodes.

Representation of Circular singly linked list

 Circular Doubly linked list: Circular Doubly Linked List has properties of both doubly linked list
and circular linked list in which two consecutive elements are linked or connected by the
previous and next pointer and the last node points to the first node by the next pointer and also
the first node points to the last node by the previous pointer.

Representation of circular doubly linked list

You might also like