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

Data Structure Typed Notes-Unit-1

This document provides lecture notes on data structures from July 2020. It introduces data structures and defines them as a way to organize and store data to allow for efficient operations. It discusses basic and complex data structure types like arrays, linked lists, trees, and graphs. It also covers algorithms, defining them as step-by-step solutions to problems and discussing their properties. Finally, it introduces asymptotic analysis and notations like Big-O, Big-Theta, and Big-Omega used to analyze algorithms' time and space complexity.

Uploaded by

Shyam Verma
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
141 views

Data Structure Typed Notes-Unit-1

This document provides lecture notes on data structures from July 2020. It introduces data structures and defines them as a way to organize and store data to allow for efficient operations. It discusses basic and complex data structure types like arrays, linked lists, trees, and graphs. It also covers algorithms, defining them as step-by-step solutions to problems and discussing their properties. Finally, it introduces asymptotic analysis and notations like Big-O, Big-Theta, and Big-Omega used to analyze algorithms' time and space complexity.

Uploaded by

Shyam Verma
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 113

LECTURE NOTES ON

DATA STRUCTURE
JULY 2020

SHYAM BAHADUR VERMA


Assistant Professor

Department of Computer Science and Engineering

UNITED COLLEGE OF ENGINEERING AND


RESEARCH
Prayagraj
Introduction to Data Structures
Data Structure is a way of collecting and organising data in such a way that we can perform operations
on these data in an effective way.

 Data Structures is about rendering data elements in terms of some relationship, for better
organization and storage. For example, we have some data which has, player's name "Virat"
and age 26. Here "Virat" is of String data type and 26 is of integer data type.

 We can organize this data as a record like Player record, which will have both player's name
and age in it. Now we can collect and store player's records in a file or database as a data
structure. For example: "Dhoni" 30, "Gambhir" 31, "Sehwag" 33

 If you are aware of Object Oriented programming concepts, then a class also does the same
thing, it collects different type of data under one single entity. The only difference being, data
structures provides for techniques to access and manipulate data efficiently.

 In simple language, Data Structures are structures programmed to store ordered data, so that
various operations can be performed on it easily. It represents the knowledge of data to be
organized in memory. It should be designed and implemented in such a way that it reduces the
complexity and increases the efficiency.

Basic types of Data Structures


As we have discussed above, anything that can store data can be called as a data structure, hence
Integer, Float, Boolean, Char etc, all are data structures. They are known as Primitive Data Structures.

Then we also have some complex Data Structures, which are used to store large and connected data.
Some example of Abstract Data Structure are :

 Linked List

 Tree

 Graph

 Stack, Queue etc.

 All these data structures allow us to perform different operations on data. We select these data
structures based on which type of operation is required.
Characteristic Description

Linear In Linear data structures,the data items are arranged in a linear sequence.
Example: Array

Non-Linear In Non-Linear data structures,the data items are not in sequence.


Example: Tree, Graph

Homogeneous In homogeneous data structures,all the elements are of same type. Example: Array

Non- In Non-Homogeneous data structure, the elements may or may not be of the same
Homogeneous type. Example: Structures

Static Static data structures are those whose sizes and structures associated memory
locations are fixed, at compile time. Example: Array

Dynamic Dynamic structures are those which expands or shrinks depending upon the
program need and its execution. Also, their associated memory locations changes.
Example: Linked List created using pointers
What is an Algorithm?
An algorithm is a finite set of instructions or logic, written in order, to accomplish a certain predefined
task. Algorithm is not the complete code or program, it is just the core logic(solution) of a problem,
which can be expressed either as an informal high level description as pseudo code or using
a flowchart.

 Every Algorithm must satisfy the following properties:

 Input- There should be 0 or more inputs supplied externally to the algorithm.

 Output- There should be at least 1 output obtained.

 Definiteness- Every step of the algorithm should be clear and well defined.

 Finiteness- The algorithm should have finite number of steps.

 Correctness- Every step of the algorithm must generate a correct output.

 An algorithm is said to be efficient and fast, if it takes less time to execute and consumes less
memory space. The performance of an algorithm is measured on the basis of following
properties :

 Time Complexity

 Space Complexity

Space Complexity
It is the amount of memory space required by the algorithm, during the course of its execution. Space
complexity must be taken seriously for multi-user systems and in situations where limited memory is
available.

 An algorithm generally requires space for following components :

 Instruction Space: It is the space required to store the executable version of the program. This
space is fixed, but varies depending upon the number of lines of code in the program.

 Data Space: It is the space required to store all the constants and variables(including temporary
variables) value.

 Environment Space: It is the space required to store the environment information needed to
resume the suspended function.

Time Complexity
Time Complexity is a way to represent the amount of time required by the program to run till its
completion. It's generally a good practice to try to keep the time required minimum, so that our
algorithm completes it's execution in the minimum time possible. We will study about Time
Complexity in details in later sections.

Asymptotic Notations
When it comes to analysing the complexity of any algorithm in terms of time and space, we can never
provide an exact number to define the time required and the space required by the algorithm, instead
we express it using some standard notations, also known as Asymptotic Notations.

 When we analyze any algorithm, we generally get a formula to represent the amount of time
required for execution or the time required by the computer to run the lines of code of the
algorithm, number of memory accesses, number of comparisons, temporary variables
occupying memory space etc. This formula often contains unimportant details that don't really
tell us anything about the running time.

 Let us take an example, if some algorithm has a time complexity of T(n) = (n 2 + 3n + 4), which is
a quadratic equation. For large values of n, the 3n + 4 part will become insignificant compared
to the n2 part.

 For n = 1000, n2 will be 1000000 while 3n + 4 will be 3004.

 Also, when we compare the execution times of two algorithms the constant coefficients of
higher order terms are also neglected.

 An algorithm that takes a time of 200n2 will be faster than some other algorithm that
takes n3 time, for any value of n larger than 200. Since we're only interested in the asymptotic
behavior of the growth of the function, the constant factor can be ignored too.
What is Asymptotic Behavior
The word Asymptotic means approaching a value or curve arbitrarily closely (i.e., as some sort of limit
is taken). Remember studying about Limits in High School, this is the same.

 The only difference being, here we do not have to find the value of any expression where n is
approaching any finite number or infinity, but in case of Asymptotic notations, we use the same
model to ignore the constant factors and insignificant parts of an expression, to device a better
way of representing complexities of algorithms, in a single coefficient, so that comparison
between algorithms can be done easily.

 Let's take an example to understand this:

 If we have two algorithms with the following expressions representing the time required by
them for execution, then:

 Expression 1: (20n2 + 3n - 4)

 Expression 2: (n3 + 100n - 2)

 Now, as per asymptotic notations, we should just worry about how the function will grow as
the value of n(input) will grow, and that will entirely depend on n2 for the Expression 1, and
on n3 for Expression 2. Hence, we can clearly say that the algorithm for which running time is
represented by the Expression 2, will grow faster than the other one, simply by analyzing the
highest power coefficient and ignoring the other constants(20 in 20n2) and insignificant parts of
the expression(3n - 4 and 100n - 2).

 The main idea behind casting aside the less important part is to make things manageable.

 All we need to do is, first analyze the algorithm to find out an expression to define it's time
requirements and then analyse how that expression will grow as the input (n) will grow.

Types of Asymptotic Notations


We use three types of asymptotic notations to represent the growth of any algorithm, as input
increases:

 Big Theta (Θ)

 Big Oh(O)

 Big Omega (Ω)

Tight Bounds: Theta


When we say tight bounds, we mean that the time compexity represented by the Big-Θ notation is like
the average value or range within which the actual time of execution of the algorithm will be.
 For example, if for some algorithm the time complexity is represented by the expression 3n 2 +
5n, and we use the Big-Θ notation to represent this, then the time complexity would be Θ(n2),
ignoring the constant coefficient and removing the insignificant part, which is 5n.

 Here, in the example above, complexity of Θ(n2) means, that the avaerage time for any
input n will remain in between, k1 * n2 and k2 * n2, where k1, k2 are two constants, therby
tightly binding the expression rpresenting the growth of the algorithm.

Upper Bounds: Big-O


This notation is known as the upper bound of the algorithm, or a Worst Case of an algorithm. It tells us
that a certain function will never exceed a specified time for any value of input n.

 The question is why we need this representation when we already have the big-Θ notation,
which represents the tightly bound running time for any algorithm. Let's take a small example
to understand this.

 Consider Linear Search algorithm, in which we traverse an array elements, one by one to search
a given number.

 In Worst case, starting from the front of the array, we find the element or number we are
searching for at the end, which will lead to a time complexity of n, where n represents the
number of total elements.

 But it can happen, that the element that we are searching for is the first element of the array, in
which case the time complexity will be 1.

 Now in this case, saying that the big-Θ or tight bound time complexity for Linear search is Θ(n),
will mean that the time required will always be related to n, as this is the right way to represent
the average time complexity, but when we use the big-O notation, we mean to say that the
time complexity is O(n), which means that the time complexity will never exceed n, defining the
upper bound, hence saying that it can be less than or equal to n, which is the correct
representation.

 This is the reason, most of the time you will see Big-O notation being used to represent the
time complexity of any algorithm, because it makes more sense.

Lower Bounds: Omega


Big Omega notation is used to define the lower bound of any algorithm or we can say the best case of
any algorithm.

 This always indicates the minimum time required for any algorithm for all input values,
therefore the best case of any algorithm.

 In simple words, when we represent a time complexity for any algorithm in the form of big-Ω,
we mean that the algorithm will take at least this much time to complete its execution. It can
definitely take more time than this too.

Space Complexity of Algorithms


Whenever a solution to a problem is written some memory is required to complete. For any algorithm
memory may be used for the following:

 Variables (This include the constant values, temporary values)

 Program Instruction

 Execution

 Space complexity is the amount of memory used by the algorithm (including the input values to
the algorithm) to execute and produce the result.

 Sometime Auxiliary Space is confused with Space Complexity. But Auxiliary Space is the extra
space or the temporary space used by the algorithm during it's execution.

 Space Complexity = Auxiliary Space + Input space

Memory Usage while Execution


While executing, algorithm uses memory space for three reasons:

 Instruction Space: It's the amount of memory used to save the compiled version of instructions.

 Environmental Stack: Sometimes an algorithm(function) may be called inside another


algorithm(function). In such a situation, the current variables are pushed onto the system stack,
where they wait for further execution and then the call to the inside algorithm(function) is
made.

 For example, If a function A() calls function B() inside it, then all th variables of the
function A() will get stored on the system stack temporarily, while the function B() is called and
executed inside the funciton A().

 Data Space: Amount of space used by the variables and constants.

But while calculating the Space Complexity of any algorithm, we usually consider only Data Space and
we neglect the Instruction Space and Environmental Stack.

Calculating the Space Complexity


For calculating the space complexity, we need to know the value of memory used by different type of
data type variables, which generally varies for different operating systems, but the method for
calculating the space complexity remains the same.

Type Size

bool, char, unsigned char, signed char, __int8 1 byte

__int16, short, unsigned short, wchar_t, __wchar_t 2 bytes

float, __int32, int, unsigned int, long, unsigned long 4 bytes

double, __int64, long double, long long 8 bytes

Now let's learn how to compute space complexity by taking a few examples:

{ int z = a + b + c;

return(z);

In the above expression, variables a, b, c and z are all integer types, hence they will take up 4 bytes
each, so total memory requirement will be (4(4) + 4) = 20 bytes, this additional 4 bytes is for return
value. And because this space requirement is fixed for the above example, hence it is called Constant
Space Complexity.
 Let's have another example, this time a bit complex one, n is the length of array a[]

int sum(int a[], int n)

int x = 0; // 4 bytes for x

for(int i = 0; i < n; i++) // 4 bytes for i

x = x + a[i];

return(x);

In the above code, 4*n bytes of space is required for the array a[] elements.

 4 bytes each for x, n, i and the return value.

 Hence the total memory requirement will be (4n + 12), which is increasing linearly with the
increase in the input value n, hence it is called as Linear Space Complexity.

 Similarly, we can have quadratic and other complex space complexity as well, as the complexity
of an algorithm increases.

 But we should always focus on writing algorithm code in such a way that we keep the space
complexity minimum.

Time Complexity of Algorithms


For any defined problem, there can be N number of solution. This is true in general. If I have a problem
and I discuss about the problem with all of my friends, they will all suggest me different solutions. And I
am the one who has to decide which solution is the best based on the circumstances.

 Similarly for any problem which must be solved using a program, there can be infinite number
of solutions.

 Let's take a simple example to understand this. Below we have two different algorithms to find
square of a number:

 One solution to this problem can be, running a loop for n times, starting with the number n and
adding n to it, every time.
for i=1 to n do

n=n+n

// when the loop ends n will hold its square return n

 Or, we can simply use a mathematical operator * to find the square and return n*n

 In the above two simple algorithms, you saw how a single problem can have many solutions.
While the first solution required a loop which will execute for n number of times, the second
solution used a mathematical operator * to return the result in one line. So which one is the
better approach, of course the second one.

What is Time Complexity?


Time complexity of an algorithm signifies the total time required by the program to run till its
completion.

 The time complexity of algorithms is most commonly expressed using the big O notation. It's an
asymptotic notation to represent the time complexity. We will study about it in detail in the
next tutorial.

 Time Complexity is most commonly estimated by counting the number of elementary steps
performed by any algorithm to finish execution. Like in the example above, for the first code
the loop will run n number of times, so the time complexity will be n at least and as the value
of n will increase the time taken will also increase. While for the second code, time complexity
is constant, because it will never be dependent on the value of n, it will always give the result in
1 step.

 And since the algorithm's performance may vary with different types of input data, hence for
an algorithm we usually use the worst-case Time complexity of an algorithm because that is
the maximum time taken for any input size.

for(i=0; i < N; i++)

statement;

The time complexity for the above algorithm will be Linear. The running time of the loop is directly
proportional to N. When N doubles, so does the running time.
for(i=0; i < N; i++)

for(j=0; j < N;j++)

statement;

 This time, the time complexity for the above code will be Quadratic. The running time of the
two loops is proportional to the square of N. When N doubles, the running time increases by N
* N.

while(low <= high)

mid = (low + high) / 2;

if (target < list[mid])

high = mid - 1;

else if (target > list[mid])

low = mid + 1;

else break;

 This is an algorithm to break a set of numbers into halves, to search a particular field. Now, this
algorithm will have a Logarithmic Time Complexity. The running time of the algorithm is
proportional to the number of times N can be divided by 2(N is high-low here). This is because
the algorithm divides the working area in half with each iteration.
void quicksort(int list[], int left, int right)

int pivot = partition(list, left, right);

quicksort(list, left, pivot - 1);

quicksort(list, pivot + 1, right);

Taking the previous algorithm forward, above we have a small logic of Quick Sort. Now in Quick Sort,
we divide the list into halves every time, but we repeat the iteration N times (where N is the size of
list). Hence time complexity will be N*log( N ). The running time consists of N loops that are
logarithmic, thus the algorithm is a combination of linear and logarithmic.

 NOTE: In general, doing something with every item in one dimension is linear, doing something
with every item in two dimensions is quadratic, and dividing the working area in half is
logarithmic.

Types of Notations for Time Complexity


Now we will discuss and understand the various notations used for Time Complexity.Big Oh denotes
"fewer than or the same as" <expression> iterations.

 Big Omega denotes "more than or the same as" <expression> iterations.

 Big Theta denotes "the same as" <expression> iterations.

 Little Oh denotes "fewer than" <expression> iterations.

 Little Omega denotes "more than" <expression> iterations.

Understanding Notations of Time Complexity with Example


 O(expression) is the set of functions that grow slower than or at the same rate as expression. It
indicates the maximum required by an algorithm for all input values. It represents the worst
case of an algorithm's time complexity.

 Omega(expression) is the set of functions that grow faster than or at the same rate as
expression. It indicates the minimum time required by an algorithm for all input values. It
represents the best case of an algorithm's time complexity.

 Theta(expression) consist of all the functions that lie in both O(expression) and
Omega(expression). It indicates the average bound of an algorithm. It represents the average
case of an algorithm's time complexity.
 Suppose you've calculated that an algorithm takes f(n) operations, where,

 f(n) = 3*n^2 + 2*n + 4. // n^2 means square of n

 Since this polynomial grows at the same rate as n2, then you could say that the function f lies in
the set Theta(n2). (It also lies in the sets O(n2) and Omega(n2) for the same reason.)

 The simplest explanation is, because Theta denotes the same as the expression. Hence,
as f(n) grows by a factor of n2, the time complexity can be best represented as Theta(n2).

Complexity and Space-Time Tradeoff


In computer science, a space-time or time-memory tradeoff is a way of solving a problem or
calculation in less time by using more storage space (or memory), or by solving a problem in very little
space by spending a long time.

 Most computers have a large amount of space, but not infinite space. Also, most people are
willing to wait a little while for a big calculation, but not forever. So if your problem is taking a
long time but not much memory, a space-time tradeoff would let you use more memory and
solve the problem more quickly. Or, if it could be solved very quickly but requires more memory
than you have, you can try to spend more time solving the problem in the limited memory.

 The most common condition is an algorithm using a lookup table. This means that the answers
for some question 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.

 A space-time tradeoff can be used with the problem of data storage. If data is stored
uncompressed, it takes more space but less time than if the data were stored compressed
(since compressing the data decreases the amount of space it takes, but it takes time to run
the compression algorithm).

 Larger code size can be used to increase program speed when using loop unwinding.
This technique makes the program code longer for each iteration of a loop, but saves the
computation time needed for jumping back to the beginning of the loop at the end of each
iteration.

 In the field of cryptography, using space-time tradeoff, the attacker is decreasing


the exponential time required for a brute force attack.

 Dynamic programming is another example where the time of solving problems can be
decreased by using more memory.
Abstract Data Type (ADT)
The abstract data type is special kind of data type, whose behavior is defined by a set of values and set
of operations. The keyword “Abstract” is used as we can use these data types, we can perform
different operations. But how those operations are working that is totally hidden from the user. The
ADT is made of with primitive data types, but operation logics are hidden.

 Some examples of ADT are Stack, Queue, List etc.

 Let us see some operations of those mentioned ADT −

Stack −
 isFull(), This is used to check whether stack is full or not

 isEmpry(), This is used to check whether stack is empty or not

 push(x), This is used to push x into the stack

 pop(), This is used to delete one element from top of the stack

 peek(), This is used to get the top most element of the stack

 size(), this function is used to get number of elements present into the stack

Queue −
 isFull(), This is used to check whether queue is full or not

 isEmpry(), This is used to check whether queue is empty or not

 insert(x), This is used to add x into the queue at the rear end

 delete(), This is used to delete one element from the front end of the queue

 size(), this function is used to get number of elements present into the queue

List −
 size(), this function is used to get number of elements present into the list

 insert(x), this function is used to insert one element into the list

 remove(x), this function is used to remove given element from the list

 get(i), this function is used to get element at position i

 replace(x, y), this function is used to replace x with y value


Data Structure: ARRAY
Introduction to Arrays
An array is collection of items stored at contiguous memory locations. The idea is to store multiple
items of same type together. This makes it easier to calculate the position of each element by simply
adding an offset to a base value, i.e., the memory location of the first element of the array (generally
denoted by the name of the array).

 For simplicity, we can think of an array a fleet of stairs where on each step is placed a value
(let’s say one of your friends). Here, you can identify the location of any of your friends by
simply knowing the count of the step they are on.
Remember: “Location of next index depends on the data type we use”.

 Arrays can be declared in various ways in different languages. For illustration, let's take C array
declaration.

As per the above illustration, following are the important points to be considered.

1. Index starts with 0.

2. Array length is 10 which means it can store 10 elements.

3. Each element can be accessed via its index. For ex, we can fetch an element at index 6 as 27.

Calculation of address
 The address of any particular element in the 1D array can be calculated by the following
equation,

 Address of element a[k] = B+W*k


 Here, B is the base address of the array, W is the size of each element of the array, and the
number of elements needed in the array is k (i.e. index of element).

 Example

For example, we wish to find the address of a 6th element of the one dimensional array ‘a[10]’,
whose base address is 1000. The size of each element in this example is 4 byte. So, the calculation
based on this can be performed in the following way.

Address of element a[6] = 1000+4*6

= 1000+24

= 1024

Basic Operations
Following are the basic operations supported by an array.

 Traverse − print all the array elements one by one.

 Insertion − Adds an element at the given index.

 Deletion − Deletes an element at the given index.

 Search − Searches an element using the given index or by the value.

 Update − Updates an element at the given index.

Traversal of Array
The method of processing each element in the array exactly once is known as Traversal. In array
Traversal starts from first element in the array and ends at the last element of the array.

 Traversing an array means accessing each and every element of the array for a specific purpose.

 Traversing the data elements of an array A can include printing every element, counting the
total number of elements, or performing any process on these elements. Since, array is a linear
data structure (because all its elements form a sequence), traversing its elements is very simple and
straightforward.

Algorithm for Array Traversal


Step 1: START = 0
Step 2: Repeat Step3 while (START<N)
Step 3: Read A [START]
START = START + 1
Program for Array Traversal
#include<stdio.h>
#include<conio.h>
#define N 5
void main()
{
int a[N]={10,20,30,40,50};
void traverse(int *a);
clrscr();
traverse(a);
getch();
}
void traverse(int *a)
{
int START=0;
while(START<N)
{
printf("%d\n",a[START]);
START=START+1;
}
}

Insertion in an Array
Following can be a situation with array insertion:

 Insertion at the beginning of an array

 Insertion at the given index of an array

 Insertion after/before the given index of an array

Insertion at the Beginning of an Array


 When the insertion happens at the beginning, it causes all the existing data items to shift one
step downward. Here, we design and implement an algorithm to insert an element at the
beginning of an array.

 We assume A is an array with N elements. The maximum numbers of elements it can store is
defined by MAX. We shall first check if an array has any empty space to store any element and
then we proceed with the insertion process.
Algorithm
1. Begin

2. IF N == MAX

return

3. ELSE N = N + 1

For all Elements in A Move to next adjacent location

A[FIRST] = New Element

4. End
Implementation in C
#include <stdio.h>

#define MAX 5

void main()

int array[MAX] = {2, 3, 4, 5};

int N = 4; // number of elements in array

int i = 0; // loop variable

int value = 1; // new data element to be stored in array

// print array before insertion

printf("Printing array before insertion −\n");

for(i = 0; i < N; i++)

printf("array[%d] = %d \n", i, array[i]);

// now shift rest of the elements downwards

for(i = N; i >= 0; i--)

array[i+1] = array[i];

// add new element at first position

array[0] = value;

// increase N to reflect number of elements

N++;

// print to confirm

printf("Printing array after insertion −\n");


for(i = 0; i < N; i++)

printf("array[%d] = %d\n", i, array[i]);

Insertion at the given index of an array


In this scenario, we are given the exact location (index) of an array where a new data element (value) needs to
be inserted. First we shall check if the array is full, if it is not, then we shall move all data elements from that
location to one step downward. This will make room for a new data element.

 We assume A is an array with N elements. The maximum numbers of elements it can store is defined
by MAX.

Algorithm
1. Begin

2. IF N = MAX

Return

3. ELSE N = N + 1
SEEK Location index
For All Elements from A[index] to A[N]
Move to next adjacent location
A[index] = New Element

End
Implementation in C
#include <stdio.h>

#define MAX 5

void main()

int array[MAX] = {1, 2, 4, 5}, N = 4,i = 0,index = 2,value = 3;

// print array before insertion

printf("Printing array before insertion −\n");

for(i = 0; i < N; i++)

{ printf("array[%d] = %d \n", i, array[i]);

// now shift rest of the elements downwards

for(i = N; i >= index; i--)

{ array[i+1] = array[i];

}
// add new element at first position

array[index] = value;

// increase N to reflect number of elements

N++;

// print to confirm

printf("Printing array after insertion −\n");

for(i = 0; i < N; i++)

printf("array[%d] = %d\n", i, array[i]);

Insertion after the given index of an array


 In this scenario we are given a location (index) of an array after which a new data element
(value) has to be inserted. Only the seek process varies, the rest of the activities are the same
as in the previous example.

 We assume A is an array with N elements. The maximum numbers of elements it can store is
defined by MAX.

Algorithm
1. Begin

2. IF N = MAX

return

3. ELSE

N=N+1

SEEK Location index

For All Elements from A[index + 1] to A[N]

Move to next adjacent location

A[index + 1] = New_Element

4. End
Implementation in C
#include <stdio.h>

#define MAX 5

void main()

int array[MAX] = {1, 2, 4, 5}, N = 4, i = 0,index = 1,value = 3;

printf("Printing array before insertion −\n");

for(i = 0; i < N; i++)

printf("array[%d] = %d \n", i, array[i]);

for(i = N; i >= index + 1; i--)

array[i + 1] = array[i];

array[index + 1] = value;

N++;

for(i = 0; i < N; i++)

printf("array[%d] = %d\n", i, array[i]);

Deletion in Array
Deletion refers to removing an existing element from the array and re-organizing all elements of an
array.
Algorithm
 Consider LA is a linear array with N elements and K is a positive integer such that K<=N.
Following is the algorithm to delete an element available at the Kth position of LA.

1. Start

2. Set J = K

3. Repeat steps 4 and 5 while J < N

4. Set LA[J] = LA[J + 1]

5. Set J = J+1

6. Set N = N-1

7. Stop

 Deleting the first element of the array

 Deleting the specified element of the array


Implementation in C
#include <stdio.h>

void main()

int LA[ ] = {1,3,5,7,8};

int k = 3, n = 5, i, j;

printf("The original array elements are :\n");

for(i = 0; i<n; i++)

printf("LA[%d] = %d \n", i, LA[i]);

j = k;

while( j < n)

LA[j-1] = LA[j];
j = j + 1;

n = n -1;

printf("The array elements after deletion :\n");

for(i = 0; i<n; i++)

printf("LA[%d] = %d \n", i, LA[i]);

Searching in Array
Not even a single day pass, when we do not have to search for something in our day to day life, car
keys, books, pen, mobile charger and what not. Same is the life of a computer, there is so much data
stored in it, that whenever a user asks for some data, computer has to search it's memory to look for
the data and make it available to the user. And the computer has it's own techniques to search through
it's memory fast. What if you have to write a program to search a given number in an array? How will
you do it?

Well, to search an element in a given array, there are two popular algorithms available:

 Linear Search

 Binary Search

Algorithm
Consider LA is a linear array with N elements and K is a positive integer such that K<=N.
Following is the algorithm to find an element with a value of ITEM using sequential search.
1. Start
2. Set J = 0
3. Repeat steps 4 and 5 while J < N
4. IF LA[J] is equal ITEM THEN GOTO STEP 6
5. Set J = J +1
6. PRINT J, ITEM
7. Stop
Linear Search
Linear search is a very basic and simple search algorithm. In Linear search, we search an element or
value in a given array by traversing the array from the starting, till the desired element or value is
found.
It compares the element to be searched with all the elements present in the array and when the
element is matched successfully, it returns the index of the element in the array, else it return -1.
Linear Search is applied on unsorted or unordered lists, when there are fewer elements in a list.

Features of Linear Search Algorithm

1. It is used for unsorted and unordered small list of elements.


2. It has a time complexity of O(n), which means the time is linearly dependent on the number of
elements, which is not bad, but not that good too.
3. It has a very simple implementation.

Implementation in C
#include <stdio.h>

void main()
{
int LA[] = {1,3,5,7,8};
int item = 5, n = 5;
int i = 0, j = 0;

printf("The original array elements are :\n");

for(i = 0; i<n; i++) {


printf("LA[%d] = %d \n", i, LA[i]);
}

while( j < n)
{
if( LA[j] == item )
{
break;
}
j = j + 1;
}
if(j==n)
printf(“Search Unsuccessful”);
else
printf("Found element %d at position %d\n", item, j+1);
}

Binary Search
Binary Search is useful when there are large numbers of elements in an array and they are sorted. So a
necessary condition for Binary search to work is that the list/array should be sorted.

Features of Binary Search

1. It is great to search through large sorted arrays.


2. It has a time complexity of O(log n) which is a very good time complexity. We will discuss this in details

in the Binary Search.

3. It has a simple implementation.

Algorithm
Binary Search is applied on the sorted array or list of large size. It's time complexity of O(log n) makes it
very fast as compared to other sorting algorithms. The only limitation is that the array or list of
elements must be sorted for the binary search algorithm to work on it.
Following are the steps of implementation that we will be following:

1. Start with the middle element:


o If the target value is equal to the middle element of the array, then return the
index of the middle element.
o If not, then compare the middle element with the target value,
▪ If the target value is greater than the number in the middle index, then
pick the elements to the right of the middle index, and start with Step 1.
▪ If the target value is less than the number in the middle index, then

Implementation
pickin
theCelements to the left of the middle index, and start with Step 1.
2. When a match is found, return the index of the element matched.
3. If no match is found, then return -1
#include<stdio.h>
int binarySearch(int values[ ], int len, int target)
{
int max = (len - 1);
int min = 0;
int guess; // this will hold the index of middle elements
int step = 0; // to find out in how many steps we completed the search
while(max >= min)
{
guess = (max + min) / 2;
// we made the first guess, incrementing step by 1
step++;
if(values[guess] == target)
{
printf("Number of steps required for search: %d \n", step);
return guess;
}
else if(values[guess] > target)
{
// target would be in the left half
max = (guess - 1);
}
else
{
// target would be in the right half
min = (guess + 1);
}
}

// We reach here when element is not


// present in array
return -1;
}
int main(void)
{
int values[] = {13, 21, 54, 81, 90};
int n = sizeof(values) / sizeof(values[0]);
int target = 81;
int result = binarySearch(values, n, target);
if(result == -1)
{
printf("Element is not present in the given array.");
}
else
{
printf("Element is present at index: %d", result);
}
return 0;
}

Updating an Array
Update operation refers to updating an existing element from the array at a given index.

Algorithm
Consider LA is a linear array with N elements and K is a positive integer such that K<=N. Following is
the algorithm to update an element available at the Kth position of LA.
1. Start
2. Set LA[K-1] = ITEM
3. Stop

Implementation in C

#include <stdio.h>

void main() {
int LA[] = {1,3,5,7,8};
int k = 3, n = 5, item = 10;
int i, j;

printf("The original array elements are :\n");

for(i = 0; i<n; i++) {


printf("LA[%d] = %d \n", i, LA[i]);
}

LA[k-1] = item;

printf("The array elements after updation :\n");

for(i = 0; i<n; i++) {


printf("LA[%d] = %d \n", i, LA[i]);
}
}

Time complexity of Array Operations


Lets take a look at the time complexity of various operations on arrays.

Operation Best Case Worst Case Average Case

Read O(1) O(1) O(1)

Insert O(n) O(n) O(n)

Delete O(n) O(n) O(n)

Linear Search O(1) O(n) O(n)

Binary Search O(1) O(n) O(logn)

Traverse O(n) O(n) O(n)

Advantages and disadvantages of Arrays


Advantages
• Reading an array element is simple and efficient. As shown in the above table, the read time
of array is O(1) in both best and worst cases. This is because any element can be instantly
read using indexes (base address calculation behind the scene) without traversing the
whole array.
• Array is a foundation of other data structures. For example other data structures such as
LinkedList, Stack, Queue etc. are implemented using array.
• All the elements of an array can be accessed using a single name (array name) along with
the index, which is readable, user-friendly and efficient rather than storing those elements
in different-2 variables.

Disadvantages
1. While using array, we must need to make the decision of the size of the array in the beginning,
so if we are not aware how many elements we are going to store in array, it would make the task
difficult.

2. The size of the array is fixed so if at later point, if we need to store more elements in it then it
can’t be done. On the other hand, if we store less number of elements than the declared size, the
remaining allocated memory is wasted.

Two dimensional (2D) arrays


An array of arrays is known as 2D array. The two dimensional (2D) array is also known as matrix. A
matrix can be represented as a table of rows and columns.

A two-dimensional array is stored in the form of the row-column matrix, where the first index
designates the row and second index shows the column. The second or the rightmost index of
an array alters very fast as compared to the first or left-most index while accessing the
elements of an array.
These matrices are stored in the memory as given below.

➢ Row-Major order Implementation


➢ Column-Major order Implementation

In Row-Major Implementation of the arrays, the arrays are stored in the memory in terms of the row
design, i.e. first the first row of the array is stored in the memory then second and so on. Suppose we
have an array named arr having 4 rows and 3 columns then it can be stored in the memory in the
following manner:

8 6 5 4

2 1 9 7

3 6 4 2

In Column-Major Implementation of the arrays, the arrays are stored in the memory in the term of the
column design, i.e. the first column of the array is stored in the memory then the second and so on.

Row-major implementation
As we know, in this implementation, rows are stored one by one from first to the last. The formula
used for computing the address of the elements in 2D array using row-major is:
Address of element a[i][j] = B + W (n(i-L1)+(j-L2))

Row-major implementation
As we know, in this implementation, rows are stored one by one from first to the last. The formula
used for computing the address of the elements in 2D array using row-major is:

Address of element a[i][j] = B + W (n(i-L1)+(j-L2))

Where B denotes the base address and W designates the size of each array element, and n is the number of
columns. L1 specifies the lower bound of the row and L2 designates lower bound of the column.

Example

Suppose we need to find the address of the two-dimensional array residing at the location a[6,2]
having base address 100 and defined as a[4…8, -2…3], which stores 2 bytes for each element.

Firstly we need to find the number of columns n, for which the upper bound of the defined array is
subtracted from the lower bound, i.e., 3-(-2) and one is added to the result (as the indexing starts from
0), which gives 6.

Address of a[6][2] = 100+2(6(6-4)+(2-(-2)))


= 100+2(6×2+4)
= 100+32 = 132
Column-major implementation
The column-major implementation mainly differs from the row-major in the sense that it does the
storage column by column. Its formula for calculating the address of an element is similar to the row-
major except the column specifications has changed with row specifications.

Address of element a[i][j] = B + W (m(j-L2)+(i-L1))

Here, ‘m’ shows the number of rows.

Multidimensional Array
An n dimensional matrix can be of any dimension. Adding a dimension is adding one more index
number (to access the element). In 1-D array you the elements are linearly arranged and can be
addressed as a[0], a[1], .. . in 2-D array elements are logically in the form of matrix having row and
column index and can be represented as a[0][0], a[0][1] etc. and they are stored row wise in the
memory, because the memory is linear.

Now a 3-D array is nothing but a logical structure which can be accessed by 3 index numbers. If the
array is of size 3 in all the dimensions (int a[3][3][3] then it is stored in the memory in the following
order:

a[0][0][0], a[0][0][1], a[0][0][2], a[0][1][0], a[0][1][1], a[0][1][2], a[0][2][0], a[0][2][1], a[0][2][2],


a[1][0][0], a[1][0][1] and so on.
Now to find the address of any element of the array based on the given index number and given
dimension size, given element size (data type size) and given base address:

Suppose the array is of n-dimensions having the size of each dimension as S1, S2, S3, ..., Sn and the
element size is ES, Base Address is BA and the index number of the element to find the address is given
as i1, i2, i3, . . . .in .

Then the formula will be:

Address of A[i1][ i2][ i3]. . . .[ in] = BA + ES*[ i1*( S2* S3* S4 *. . ..* Sn) + i2*( S3* S4* S5 *.. .. * Sn) + ... + in-
2*( Sn-1*Sn) + in-1*Sn + in ]

Example-1: An int array A[100][50] is stored at the address 1000. Find the address of A[40][25].
Solution: BA=1000, ES=2, S1=100, S2=50, i1=40, i2=25 Address of A[40][25]=1000+2*[40*50 + 25]=1450

Example-2: An int array A[50][40][30] is stored at the address 1000. Find the address of A[40][20][10].
Solution: BA=1000, ES=2, S1=50, S2=40, S3=30, i1=40, i2=20, i3=10 Address of
A[40][20][10]=1000+2*[40*40*30 + 20*30 + 10]=98220

Applications of Array
Apart from being widely used in programming, arrays have additional applications as well:

• Used in mathematical problems like matrices etc.


• They are used in implementation of other data structures like linked lists, stack, queue etc.
• Database records are usually implemented as arrays.
• Used in lookup tables by computer.
• It effectively executes memory addressing logic wherein indices act as addresses to the one
dimensional array of memory.

Sparse Matrix
In computer programming, a matrix can be defined with a 2-dimensional array. Any array with 'm'
columns and 'n' rows represent a m X n matrix. There may be a situation in which a matrix contains
more number of ZERO values than NON-ZERO values. Such matrix is known as sparse matrix.
Sparse matrix is a matrix which contains very few non-zero elements.
When a sparse matrix is represented with a 2-dimensional array, we waste a lot of space to represent
that matrix. For example, consider a matrix of size 100 X 100 containing only 10 non-zero elements. In
this matrix, only 10 spaces are filled with non-zero values and remaining spaces of the matrix are filled
with zero. That means, totally we allocate 100 X 100 X 2 = 20000 bytes of space to store this integer
matrix. And to access these 10 non-zero elements we have to make scanning for 10000 times. To make
it simple we use the following sparse matrix representation.

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

Sparse Matrix Representations


A sparse matrix can be represented by using TWO representations, those are as follows...

1. Triplet Representation (Array Representation)


2. Linked Representation

Triplet Representation (Array Representation)


In this representation, we consider only non-zero values along with their row and column index values.
In this representation, the 0th row stores the total number of rows, total number of columns and the
total number of non-zero values in the sparse matrix.

For example, consider a matrix of size 5 X 6 containing 6 number of non-zero values. This matrix can be
represented as shown in the image:

In above example matrix, there are only 6 non-zero elements ( those are 9, 8, 4, 2, 5 & 2) and matrix
size is 5 X 6. We represent this matrix as shown in the above image. Here the first row in the right side
table is filled with values 5, 6 & 6 which indicates that it is a sparse matrix with 5 rows, 6 columns & 6
non-zero values. The second row is filled with 0, 4, & 9 which indicates the non-zero value 9 is at the
0th-row 4th column in the Sparse matrix. In the same way, the remaining non-zero values also follow a
similar pattern. Eg.

Implementation in C

#include<stdio.h>

int main()
{
// Assume 4x5 sparse matrix
int sparseMatrix[4][5] =
{
{0, 0, 3, 0, 4},
{0, 0, 5, 7, 0},
{0, 0, 0, 0, 0},
{0, 2, 6, 0, 0}
};

int size = 0;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 5; j++)
if (sparseMatrix[i][j] != 0)
size++;

/* number of columns in compactMatrix (size) must be equal to number of non-zero elements in


sparseMatrix*/

int compactMatrix[3][size];

// Making of new matrix


int k = 0;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 5; j++)
if (sparseMatrix[i][j] != 0)
{
compactMatrix[0][k] = i;
compactMatrix[1][k] = j;
compactMatrix[2][k] = sparseMatrix[i][j];
k++;
}

for (int i=0; i<3; i++)


{
for (int j=0; j<size; j++)
printf("%d ", compactMatrix[i][j]);

printf("\n");
}
return 0;
}

Linked Representation
In linked representation, we use a linked list data structure to represent a sparse matrix. In linked list,
each node has four fields. These four fields are defined 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)
• Next node: Address of the next node
Implementation in C
#include<stdio.h>
#include<stdlib.h>

// Node to represent sparse matrix


struct Node
{
int value;
int row_position;
int column_postion;
struct Node *next;
};

// Function to create new node


void create_new_node(struct Node** start, int non_zero_element, int row_index, int column_index )
{
struct Node *temp, *r;
temp = *start;
if (temp == NULL)
{
// Create new node dynamically
temp = (struct Node *) malloc (sizeof(struct Node));
temp->value = non_zero_element;
temp->row_position = row_index;
temp->column_postion = column_index;
temp->next = NULL;
*start = temp;

}
else
{
while (temp->next != NULL)
temp = temp->next;

// Create new node dynamically


r = (struct Node *) malloc (sizeof(struct Node));
r->value = non_zero_element;
r->row_position = row_index;
r->column_postion = column_index;
r->next = NULL;
temp->next = r;
}
}
// This function prints contents of linked list
// starting from start
void PrintList(struct Node* start)
{
struct Node *temp, *r, *s;
temp = r = s = start;

printf("row_position: ");
while(temp != NULL)
{

printf("%d ", temp->row_position);


temp = temp->next;
}
printf("\n");

printf("column_postion: ");
while(r != NULL)
{
printf("%d ", r->column_postion);
r = r->next;
}
printf("\n");
printf("Value: ");
while(s != NULL)
{
printf("%d ", s->value);
s = s->next;
}
printf("\n");
}

int main()
{
// Assume 4x5 sparse matrix
int sparseMatric[4][5] =
{
{0 , 0 , 3 , 0 , 4 },
{0 , 0 , 5 , 7 , 0 },
{0 , 0 , 0 , 0 , 0 },
{0 , 2 , 6 , 0 , 0 }
};
/* Start with the empty list */
struct Node* start = NULL;

for (int i = 0; i < 4; i++)


for (int j = 0; j < 5; j++)

// Pass only those values which are non - zero


if (sparseMatric[i][j] != 0)
create_new_node(&start, sparseMatric[i][j], i, j);

PrintList(start);

return 0;
}

Upper Triangular Matrix

A triangular matrix of the form

(1)

Written explicitly,

(2)

A square matrix with elements Uij = 0 for j < i is termed upper triangular matrix. In other words, a
square matrix is upper triangular if all its entries below the main diagonal are zero.

A strictly upper triangular matrix is an upper triangular matrix having 0s along the diagonal as well,
i.e., Uij for .

Triangular Matrix
There are two types of triangular matrices: upper triangular matrix and lower triangular matrix.
Triangular matrices have the same number of rows as they have columns; that is, they have n rows and
n columns.
A square matrix with elements sij = 0 for j < i is termed upper triangular matrix. In other words, a
square matrix is upper triangular if all its entries below the main diagonal are zero.
Example of a 3 × 3 upper triangular matrix:

s11 s12 s13


0 s22 s23
0 0 s33

A square matrix with elements sij = 0 for j > i is termed lower triangular matrix. In other words, a
square matrix is lower triangular if all its entries above the main diagonal are zero.

Example of a 3 × 3 lower triangular matrix:

s11 0 0
s21 s22 0
s31 s32 s33

If we have NxN matrix then a lower/upper triangular matrix without the main diagonal will have (N - 1)
* N / 2 elements, or (N + 1) * N / 2 elements with the main diagonal.

Representation of lower tringular matrix in 1-D array


Without the main diagonal:
Index of (I, J) (I,J ∈ 0..N-1, I > J) ⇒ (I * (I - 1) / 2 + J).

With the main diagonal:


Index of (I,J ∈ 0..N-1, I ≥ J) ⇒ ((I + 1) * I / 2 + J).

Representation of upper tringular matrix in 1-D array


For a upper diagonal matrix flip I and J in the equations above. For a diagonal matrix just choose either
I>=J or J>=I.

Diagonal matrices are both upper and lower triangular since they have zeroes above and below the
main diagonal.

s11 0 0
0 s22 0
0 0 s33

Tridiagonal matrix
A tridiagonal matrix is a band matrix that has nonzero elements on the main diagonal, the first diagonal
below this, and the first diagonal above the main diagonal only.

For example, the following matrix is tridiagonal:

s11 s12 0 0
s21 s22 s23 0
0 s32 s33 ss34
0 0 s43 s44

Representation of tridiagonal matrix in 1-D array


Total non-zero elements = (n-1) + n + (n-1)

= (3n-2) where n is the dimension of matrix.

Index of (i,j) = [3*(i-2)+2] + [j-i+2]

= 2*i + j - 2

Notes:
1. The inverse of a lower triangular matrix is also lower triangular.
2. The product of two or more lower triangular matrices is also lower triangular.
3. The transpose of a lower triangular matrix is upper triangular.
4. The inverse of an upper triangular matrix is also upper triangular.
5. The product of two or more upper triangular matrices is also upper triangular.
6. The transpose of an upper triangular matrix is lower triangular.

Linked lists
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:
Each node holds its own data and the address of the next node hence forming a chain like structure.

Advantages of Linked Lists


• They are a dynamic in nature which allocates the memory when required.
• Insertion and deletion operations can be easily implemented.
• Stacks and queues can be easily executed.
• Linked List reduces the access time.

Disadvantages of Linked Lists


• The memory is wasted as pointers require extra memory for storage.
• No element can be accessed randomly; it has to access each node sequentially.
• Reverse Traversing is difficult in linked list.

Applications of Linked Lists


• Linked lists are used to implement stacks, queues, graphs, etc.
• Linked lists let you insert elements at the beginning and end of the list.
• In Linked Lists we don't need to know the size in advance.

Basic Operations
Following are the basic operations supported by a list.

• Insertion − Adds an element in the list.

• Deletion − Deletes an element from the list.

• Display − Displays the complete list.

• Search − Searches an element using the given key.

• Delete − Deletes an element using the given key.

Types of Linked Lists


There are 3 different implementations of Linked List available, they are:

• Singly Linked List


• Doubly Linked List
• Circular Linked List

Let's know more about them and how they are different from each other.

Singly Linked List


Singly linked lists contain nodes which have a data part as well as an address part i.e. next, which
points to the next node in the sequence of nodes.

A singly linked list is formed when many such nodes are linked together to form a chain. Each node
points to the next node present in the order. The first node is always used as a reference to traverse
the list and is called HEAD. The last node points to NULL.

Declaring a Linked list


In C language, a linked list can be implemented using structure and pointers .

struct LinkedList
{
int data;
struct LinkedList *next;
};

The above definition is used to create every node in the list. The data field stores the element and the
next is a pointer to store the address of the next node.

Noticed something unusual with next?


In place of a data type, struct LinkedList is written before next. That's because its a self-referencing
pointer. It means a pointer that points to whatever it is a part of. Here next is a part of a node and it
will point to the next node.

Creating a Node
Let's define a data type of struct LinkedListto make code cleaner.

typedef struct LinkedList *node; //Define node as pointer of data type struct LinkedList

node createNode()
{
node temp; // declare a node

// allocate memory using malloc()


temp = (node)malloc(sizeof(struct LinkedList));
temp->next = NULL;// make next point to NULL
return temp;//return the new node
}

typedef is used to define a data type in C.

malloc() is used to dynamically allocate a single block of memory in C, it is available in the header
file stdlib.h. sizeof() is used to determine size in bytes of an element in C. Here it is used to
determine size of each node and sent as a parameter to malloc. The above code will create a
node with data as value and next pointing to NULL.

Inserting a node in singly linked list


A node can be added in three ways
1) At the front (beginning) of the linked list
2) After a given node.
3) At the end of the linked list.

1. Insertion at the beginning of the list


In the first case, we make a new node and points its next to the head of the existing list and then
change the head to the newly added node. It is similar to picture given below.
Algorithm
1. Start
2. Create a new node
3. new->next = Head
4. Head = new
5. End

Time complexity of this algorithm is O(1) as it does constant amount of work.

Implementation in C
#include <stdlib.h>

/* Structure of a node */
struct node {
int data; // Data
struct node *next; // Address
}*head;

void createList(int n);


void insertNodeAtBeginning(int data);
void displayList();

int main()
{
int n, data;

/* Create a singly linked list of n nodes */


printf("Enter the total number of nodes: ");
scanf("%d", &n);
createList(n);
printf("\nData in the list \n");
displayList();

/* Insert data at the beginning of the singly linked list */


printf("\nEnter data to insert at beginning of the list: ");
scanf("%d", &data);
insertNodeAtBeginning(data);

printf("\nData in the list \n");


displayList();

return 0;
}

/* Create a list of n nodes */


void createList(int n)
{
struct node *newNode, *temp;
int data, i;

head = (struct node *)malloc(sizeof(struct node));

/* If unable to allocate memory for head node */


if(head == NULL)
{
printf("Unable to allocate memory.");
}
else
{
/* Input data of node from the user */
printf("Enter the data of node 1: ");
scanf("%d", &data);

head->data = data; // Link data field with data


head->next = NULL; // Link address field to NULL

temp = head;

/* Create n nodes and adds to linked list */


for(i=2; i<=n; i++)
{
newNode = (struct node *)malloc(sizeof(struct node));
/* If memory is not allocated for newNode */
if(newNode == NULL)
{
printf("Unable to allocate memory.");
break;
}
else
{
printf("Enter the data of node %d: ", i);
scanf("%d", &data);

newNode->data = data; // Link data field of newNode with data


newNode->next = NULL; // Link address field of newNode with NULL

temp->next = newNode; // Link previous node i.e. temp to the newNode

temp = temp->next;
}
}

printf("SINGLY LINKED LIST CREATED SUCCESSFULLY\n");


}
}

/* Create a new node and inserts at the beginning of the linked list. */
void insertNodeAtBeginning(int data)
{
struct node *newNode;

newNode = (struct node*)malloc(sizeof(struct node));

if(newNode == NULL)
{
printf("Unable to allocate memory.");
}
else
{
newNode->data = data; // Link data part
newNode->next = head; // Link address part

head = newNode; // Make newNode as first node

printf("DATA INSERTED SUCCESSFULLY\n");


}
}

/* Display entire list */


void displayList()
{
struct node *temp;

/* If the list is empty i.e. head = NULL */


if(head == NULL)
{
printf("List is empty.");
}
else
{
temp = head;
while(temp != NULL)
{
printf("Data = %d\n", temp->data); // Print data of current node
temp = temp->next; // Move to next node
}
}
}

2. Insertion of a node after a given node

Algorithm
To insert a node at the nth position in Singly Linked List
Input : n position to insert data in the list
Begin:
createSinglyLinkedList (head)
alloc (newNode)
If (newNode == NULL) then
write ('Unable to allocate memory.')
End if
Else then
read (data)
newNode.data ← data
temp ← head
For i ← 2 to n-1
temp ← temp.next
If (temp == NULL) then
break
End if
End for
If (temp != NULL) then
newNode.next ← temp.next
temp.next ← newNode
End if
End else
End

Time complexity of this algorithm is O(n) as n can be any position.

Implementation in C
#include <stdlib.h>

void createList(int n);


void insertNodeAtMiddle(int data, int position);
void displayList();

int main()
{
int n, data, position;

/* Create a singly linked list of n nodes */


printf("Enter the total number of nodes: ");
scanf("%d", &n);
createList(n);

printf("\nData in the list \n");


displayList();

/* Insert data at middle of the singly linked list */


printf("nEnter data to insert at middle of the list: ");
scanf("%d", &data);
printf("Enter the position to insert new node: " );
scanf("%d", &position);
insertNodeAtMiddle(data, position);

printf("\nData in the list \n");


displayList();

return 0;
}

void insertNodeAtMiddle(int data, int position)


{
int i;
struct node *newNode, *temp;

newNode = (struct node*)malloc(sizeof(struct node));

if(newNode == NULL)
{
printf("Unable to allocate memory.");
}
else
{
newNode->data = data; // Link data part
newNode->next = NULL;

temp = head;

/*
* Traverse to the n-1 position
*/
for(i=2; i<=position-1; i++)
{
temp = temp->next;
if(temp == NULL)
break;
}

if(temp != NULL)
{
/* Link address part of new node */
newNode->next = temp->next;

/* Link address part of n-1 node */


temp->next = newNode;

printf("DATA INSERTED SUCCESSFULLY\n");


}
else
{
printf("UNABLE TO INSERT DATA AT THE GIVEN POSITION\n");
}
}
}

3. Insertion at the end of the linked list

Algorithm
To insert node at the end of a Singly Linked List
Begin:
createSinglyLinkedList (head)
alloc (newNode)
If (newNode == NULL) then
write ('Unable to allocate memory')
End if
Else then
read (data)
newNode.data ← data
newNode.next ← NULL
temp ← head
While (temp.next != NULL) do
temp ← temp.next
End while
temp.next ← newNode
End else
End

Time complexity of this algorithm is O(n) where n is the number of nodes in linked list. Since there is a
loop from head to end, the function does O(n) work.
This method can also be optimized to work in O(1) by keeping an extra pointer to tail (end) of the
linked list.

Implementation in C
#include <stdlib.h>

void createList(int n);


void insertNodeAtEnd(int data);
void displayList();

int main()
{
int n, data;

/* Create a singly linked list of n nodes */


printf("Enter the total number of nodes: ");
scanf("%d", &n);
createList(n);

printf("\nData in the list \n");


displayList();

/* Insert data at the end of the singly linked list */


printf("\nEnter data to insert at end of the list: ");
scanf("%d", &data);
insertNodeAtEnd(data);

printf("\nData in the list \n");


displayList();

return 0;
}

/* Create a new node and inserts at the end of the linked list. */
void insertNodeAtEnd(int data)
{
struct node *newNode, *temp;

newNode = (struct node*)malloc(sizeof(struct node));

if(newNode == NULL)
{
printf("Unable to allocate memory.");
}
else
{
newNode->data = data; // Link the data part
newNode->next = NULL;

temp = head;

// Traverse to the last node


while(temp->next != NULL)
temp = temp->next;

temp->next = newNode; // Link address part


printf("DATA INSERTED SUCCESSFULLY\n");
}
}

Deleting a node from the singly linked list


A node can be deleted in three ways from a singly linked list:
1) From the front (beginning) of the linked list
2) After a given node.
3) From the end of the linked list.

1. Deleting the first node of the linked list

Algorithm
To delete the first node from Singly Linked List

Input: head of the linked list


Begin:
If (head != NULL) then
toDelete ← head
head ← head.next
unalloc (toDelete)
End if
End
Time complexity of this algorithm is O(1) as it does constant amount of work.

Implementation in C
#include <stdlib.h>

void createList(int n);


void deleteFirstNode();
void displayList();

int main()
{
int n, choice;

/* Create a singly linked list of n nodes */


printf("Enter the total number of nodes: ");
scanf("%d", &n);
createList(n);

printf("\nData in the list \n");


displayList();

printf("\nPress 1 to delete first node: ");


scanf("%d", &choice);

/* Delete first node from list */


if(choice == 1)
deleteFirstNode();

printf("\nData in the list \n");


displayList();

return 0;
}

/* Deletes the first node of the linked list */


void deleteFirstNode()
{
struct node *toDelete;

if(head == NULL)
{
printf("List is already empty.");
}
else
{
toDelete = head;
head = head->next;

printf("\nData deleted = %d\n", toDelete->data);

/* Clears the memory occupied by first node*/


free(toDelete);

printf("SUCCESSFULLY DELETED FIRST NODE FROM LIST\n");


}
}

2. Deleting the nth node of the linked list


To delete a node from linked list, we need to do following steps.
1) Find previous node of the node to be deleted.
2) Change the next of previous node.
3) Free memory for the node to be deleted.
Algorithm
To delete nth node of Singly Linked List

Input : head node of the linked list


n node to be deleted
Begin:
If (head == NULL) then
write ('List is already empty')
End if
Else then
toDelete ← head
prevNode ← head
For i←2 to n do
prevNode ← toDelete
toDelete ← toDelete.next
If (toDelete == NULL) then
break
End if
End for
If (toDelete != NULL) then
If (toDelete == head) then
head ← head.next
End if
prevNode.next ← toDelete.next
toDelete.next ← NULL
unalloc (toDelete)
End if
End else
End
Time complexity of this algorithm is O(n) where n is the number of nodes in linked list. Since
there is a loop from head to end, the function does O(n) work.

Implementation in C

/* Functions used in program */


void createList(int n);
void deleteMiddleNode(int position);
void displayList();

int main()
{
int n, position;

/* Create a singly linked list of n nodes */


printf("Enter the total number of nodes: ");
scanf("%d", &n);
createList(n);

printf("\nData in the list \n");


displayList();

printf("\nEnter the node position you want to delete: ");


scanf("%d", &position);

/* Delete middle node from list */


deleteMiddleNode(position);

printf("\nData in the list \n");


displayList();

return 0;
}

/* Delete middle node of the linked list */


void deleteMiddleNode(int position)
{
int i;
struct node *toDelete, *prevNode;

if(head == NULL)
{
printf("List is already empty.");
}
else
{
toDelete = head;
prevNode = head;

for(i=2; i<=position; i++)


{
prevNode = toDelete;
toDelete = toDelete->next;

if(toDelete == NULL)
break;
}

if(toDelete != NULL)
{
if(toDelete == head)
head = head->next;

prevNode->next = toDelete->next;
toDelete->next = NULL;

/* Delete nth node */


free(toDelete);

printf("SUCCESSFULLY DELETED NODE FROM MIDDLE OF LIST\n");


}
else
{
printf("Invalid position unable to delete.");
}
}
}

3. Deleting the last node of the linked list


Algorithm
To delete last node of Singly Linked List

Input : head node of the linked list


Begin:
If (head == NULL) then
write ('List is already empty')
End if
Else then
toDelete ← head
secondLastNode ← head
While (toDelete.next != NULL) do
secondLastNode ← toDelete
toDelete ← toDelete.next
End while
If (toDelete == head) then
head ← NULL
End if
Else then
secondLastNode.next ← NULL
End else
unalloc (toDelete)
End else
End

Time complexity of this algorithm is O(n) where n is the number of nodes in linked list. Since
there is a loop from head to end, the function does O(n) work.

3. Display (Traversing) the elements of singly linked list

Step by step descriptive logic to traverse a linked list.

• Create a temporary variable for traversing. Assign reference of head node to it, say temp =
head.
• Repeat below step till temp!= NULL.
• temp->data contains the current node data. You can print it or can perform some calculation on
it.
• Once done, move to next node using temp = temp->next;.
• Go back to 2nd step.

Time complexity of this algorithm is O(n) where n is the number of nodes in linked list. Since
there is a loop from head to end, the function does O(n) work.

Implementation in C
#include <stdlib.h>

/* Structure of a node */
struct node {
int data; // Data
struct node *next; // Address
}*head;

/* Functions to create and display list */


void createList(int n);
void traverseList();

int main()
{
int n;

printf("Enter the total number of nodes: ");


scanf("%d", &n);

createList(n);

printf("\nData in the list \n");


traverseList();

return 0;
}

/* Create a list of n nodes */


void createList(int n)
{
struct node *newNode, *temp;
int data, i;

head = (struct node *)malloc(sizeof(struct node));

// Terminate if memory not allocated


if(head == NULL)
{
printf("Unable to allocate memory.");
exit(0);
}

// Input data of node from the user


printf("Enter the data of node 1: ");
scanf("%d", &data);

head->data = data; // Link data field with data


head->next = NULL; // Link address field to NULL

// Create n - 1 nodes and add to list


temp = head;
for(i=2; i<=n; i++)
{
newNode = (struct node *)malloc(sizeof(struct node));

/* If memory is not allocated for newNode */


if(newNode == NULL)
{
printf("Unable to allocate memory.");
break;
}

printf("Enter the data of node %d: ", i);


scanf("%d", &data);

newNode->data = data; // Link data field of newNode


newNode->next = NULL; // Make sure new node points to NULL

temp->next = newNode; // Link previous node with newNode


temp = temp->next; // Make current node as previous node
}
}

/* Display entire list */


void traverseList()
{
struct node *temp;

// Return if list is empty


if(head == NULL)
{
printf("List is empty.");
return;
}

temp = head;
while(temp != NULL)
{
printf("Data = %d\n", temp->data); // Print data of current node
temp = temp->next; // Move to next node
}
}
4. Searches an element using the given key
Search is one of the most common operation on performed any data structure. In
this section we will learn how to search an element in linked list (iterative and
recursive) using C program. I will explain both ways to search, how to search an
element in linked list using loop and recursion.

Algorithm
Search an element in linked list is fairly similar to how you search an element in
arrays. Here we will learn to perform linear search on singly linked list.

• Input element to search from user. Store it in some variable say keyToSearch.
• Declare two variable one to store index of found element and other to iterate through list. Say
index = 0; and struct node *curNode = head;
• If curNode is not NULL and its data is not equal to keyToSearch. Then, increment index and
move curNode to its next node.
• Repeat step 3 till curNode != NULL and element is not found, otherwise move to 5th step.
• If curNode is not NULL, then element is found hence return index otherwise -1.

Time complexity of this algorithm is O(n) where n is the number of nodes in linked list. Since
there is a loop from head to end, the function does O(n) work.

Implementation in C
#include <stdlib.h>
/* Function declaration */
void createList(int n);
void displayList();
int search(int key);

int main()
{
int n, keyToSearch, index;
// Input node count to create
printf("Enter number of node to create: ");
scanf("%d", &n);

createList(n);

// Display list
printf("\nData in list: \n");
displayList();

// Input element to search from user.


printf("\nEnter element to search: ");
scanf("%d", &keyToSearch);

// Call function to search first element by key


index = search(keyToSearch);

// Element found in the list


if (index >= 0)
printf("%d found in the list at position %d\n", keyToSearch, index + 1);
else
printf("%d not found in the list.\n", keyToSearch);

return 0;
}

int search(int key)


{
int index;
struct node *curNode;

index = 0;
curNode = head;

// Iterate till last element until key is not found


while (curNode != NULL && curNode->data != key)
{
index++;
curNode = curNode->next;
}
return (curNode != NULL) ? index : -1;
}

Recursive search

• If curNode == NULL, means no more element to search further and should return -1.
• If curNode->data == key, element found hence return current index.
• None of the above two conditions satisfies, then move curNode to next node, increment index
and make recursive call to search further. Say searchRecursive(key, curNode->next, index + 1);.

Implementation in C
int searchRecursive(int key, struct node *curNode, int index)
{
if (curNode == NULL)
return -1;
else if (curNode->data == key)
return index;
else
return (searchRecursive(key, curNode->next, index + 1));
}

Doubly Linked List


Doubly linked list is a type of linked list in which each node apart from storing its data has two links.
The first link points to the previous node in the list and the second link points to the next node in the
list. The first node of the list has its previous link pointing to NULL similarly the last node of the list has
its next node pointing to NULL.
The two links help us to traverse the list in both backward and forward direction. But storing an extra link
requires some extra space.

• Link − Each link of a linked list can store a data called an element.
• Next − Each link of a linked list contains a link to the next link called Next.
• Prev − Each link of a linked list contains a link to the previous link called Prev.
• LinkedList − A Linked List contains the connection link to the first link called First and to the last
link called Last.
As per the above illustration, following are the important points to be considered.
• Doubly Linked List contains a link element called first and last.
• Each link carries a data field(s) and two link fields called next and prev.
• Each link is linked with its next link using its next link.
• Each link is linked with its previous link using its previous link.
• The last link carries a link as null to mark the end of the list.

Basic Operations
Following are the basic operations supported by a list.
• Insertion − Adds an element at the beginning of the list.
• Deletion − Deletes an element at the beginning of the list.
• Insert Last − Adds an element at the end of the list.
• Delete Last − Deletes an element from the end of the list.
• Insert After − Adds an element after an item of the list.
• Delete − Deletes an element from the list using the key.
• Display forward − Displays the complete list in a forward manner.
• Display backward − Displays the complete list in a backward manner.

Advantages over singly linked list


1) A DLL can be traversed in both forward and backward direction.
2) The delete operation in DLL is more efficient if pointer to the node to be deleted is given.
3) We can quickly insert a new node before a given node.
In singly linked list, to delete a node, pointer to the previous node is needed. To get this previous node,
sometimes the list is traversed. In DLL, we can get the previous node using previous pointer.

Disadvantages over singly linked list


1) Every node of DLL Require extra space for an previous pointer. It is possible to implement DLL with single
pointer though.
2) All operations require an extra pointer previous to be maintained. For example, in insertion, we need to
modify previous pointers together with next pointers. For example in following functions for insertions at
different positions, we need 1 or 2 extra steps to set previous pointer.

Insertion Operations in Doubly Linked List


1) Add a node at the front
Algorithm

Step 1 - Create a newNode with given value and newNode → prev as NULL.

Step 2 - Check whether list is Empty (head == NULL)


Step 3 - If it is Empty then, assign NULL to newNode → next and newNode to head.

Step 4 - If it is not Empty then, assign head to newNode → next and newNode to head.

2) Inserting At End of the list


We can use the following steps to insert a new node at end of the double linked list:

Algorithm
Step 1 - Create a newNode with given value and newNode → next as NULL.

Step 2 - Check whether list is Empty (head == NULL)

Step 3 - If it is Empty, then assign NULL to newNode → previous and newNode to head.

Step 4 - If it is not Empty, then, define a node pointer temp and initialize with head.

Step 5 - Keep moving the temp to its next node until it reaches to the last node in the list
(until temp → next is equal to NULL).

Step 6 - Assign newNode to temp → next and temp to newNode → previous.


3) Inserting At Specific location in the list (After a Node)
We can use the following steps to insert a new node after a node in the double linked list:

Algorithm
Step 1 - Create a newNode with given value.

Step 2 - Check whether list is Empty (head == NULL)

Step 3 - If it is Empty then, assign NULL to both newNode → previous & newNode → next and
set newNode to head.

Step 4 - If it is not Empty then, define two node pointers temp1 & temp2 and
initialize temp1 with head.

Step 5 - Keep moving the temp1 to its next node until it reaches to the node after which we want
to insert the newNode (until temp1 → data is equal to location, here location is the node value
after which we want to insert the newNode).

Step 6 - Every time check whether temp1 is reached to the last node. If it is reached to the last
node then display 'Given node is not found in the list!!! Insertion not possible!!!' and terminate
the function. Otherwise move the temp1 to next node.

Step 7 - Assign temp1 → next to temp2, newNode to temp1 → next, temp1 to newNode →
previous, temp2 to newNode → next and newNode to temp2 → previous.
Deletion Operations in Doubly Linked List
In a double linked list, the deletion operation can be performed in three ways as follows...

1. Deleting from Beginning of the list


2. Deleting from End of the list
3. Deleting a Specific Node

1) Deletion from Beginning of the list


We can use the following steps to delete a node from beginning of the double linked list...
Algorithm

Step 1 - Check whether list is Empty (head == NULL)

Step 2 - If it is Empty then, display 'List is Empty!!! Deletion is not possible' and terminate the function.

Step 3 - If it is not Empty then, define a Node pointer 'temp' and initialize with head.

Step 4 - Check whether list is having only one node (temp → previous is equal to temp → next)

Step 5 - If it is TRUE, then set head to NULL and delete temp (Setting Empty list conditions)

Step 6 - If it is FALSE, then assign temp → next to head, NULL to head → previous and delete temp.

2) Deleting a Specific Node from the list


We can use the following steps to delete a specific node from the double linked list...

Algorithm

Step 1 - Check whether list is Empty (head == NULL)

Step 2 - If it is Empty then, display 'List is Empty!!! Deletion is not possible' and terminate the function.

Step 3 - If it is not Empty, then define a Node pointer 'temp' and initialize with head.

Step 4 - Keep moving the temp until it reaches to the exact node to be deleted or to the last node.

Step 5 - If it is reached to the last node, then display 'Given node not found in the list! Deletion not
possible!!!' and terminate the fuction.
Step 6 - If it is reached to the exact node which we want to delete, then check whether list is having only one
node or not

Step 7 - If list has only one node and that is the node which is to be deleted then set head to NULL and
delete temp (free(temp)).

Step 8 - If list contains multiple nodes, then check whether temp is the first node in the list (temp == head).

Step 9 - If temp is the first node, then move the head to the next node (head = head → next),
set head of previous to NULL (head → previous = NULL) and delete temp.

Step 10 - If temp is not the first node, then check whether it is the last node in the list (temp → next == NULL).

Step 11 - If temp is the last node then set temp of previous of next to NULL (temp → previous → next = NULL)
and delete temp (free(temp)).

Step 12 - If temp is not the first node and not the last node, then
set temp of previous of next to temp of next (temp → previous → next = temp →
next), temp of next of previous to temp of previous (temp → next → previous = temp → previous) and
delete temp (free(temp)).

3) Deletion from End of the list


We can use the following steps to delete a node from end of the double linked list...

Algorithm

Step 1 - Check whether list is Empty (head == NULL)

Step 2 - If it is Empty, then display 'List is Empty!!! Deletion is not possible' and terminate the function.

Step 3 - If it is not Empty then, define a Node pointer 'temp' and initialize with head.

Step 4 - Check whether list has only one Node (temp → previous and temp → next both are NULL)
Step 5 - If it is TRUE, then assign NULL to head and delete temp. And terminate from the function.
(Setting Empty list condition)

Step 6 - If it is FALSE, then keep moving temp until it reaches to the last node in the list. (until temp → next is
equal to NULL)

Step 7 - Assign NULL to temp → previous → next and delete temp.

Displaying a Double Linked List


We can use the following steps to display the elements of a double linked list...

Algorithm

Step 1 - Check whether list is Empty (head == NULL)

Step 2 - If it is Empty, then display 'List is Empty!!!' and terminate the function.

Step 3 - If it is not Empty, then define a Node pointer 'temp' and initialize with head.

Step 4 - Display 'NULL <--- '.

Step 5 - Keep displaying temp → data with an arrow (<===>) until temp reaches to the last node

Step 6 - Finally, display temp → data with arrow pointing to NULL (temp → data ---> NULL).
Implementation in C
#include<conio.h>

void insertAtBeginning(int);
void insertAtEnd(int);
void insertAtAfter(int,int);
void deleteBeginning();
void deleteEnd();
void deleteSpecific(int);
void display();

struct Node
{
int data;
struct Node *previous, *next;
}*head = NULL;

void main()
{
int choice1, choice2, value, location;
clrscr();
while(1)
{
printf("\n*********** MENU *************\n");
printf("1. Insert\n2. Delete\n3. Display\n4. Exit\nEnter your choice: ");
scanf("%d",&choice1);
switch()
{
case 1: printf("Enter the value to be inserted: ");
scanf("%d",&value);
while(1)
{
printf("\nSelect from the following Inserting options\n");
printf("1. At Beginning\n2. At End\n3. After a Node\n4. Cancel\nEnter your choice:
");
scanf("%d",&choice2);
switch(choice2)
{
case 1: insertAtBeginning(value);
break;
case 2: insertAtEnd(value);
break;
case 3: printf("Enter the location after which you want to insert: ");
scanf("%d",&location);
insertAfter(value,location);
break;
case 4: goto EndSwitch;
default: printf("\nPlease select correct Inserting option!!!\n");
}
}
case 2: while(1)
{
printf("\nSelect from the following Deleting options\n");
printf("1. At Beginning\n2. At End\n3. Specific Node\n4. Cancel\nEnter your choice:
");
scanf("%d",&choice2);
switch(choice2)
{
case 1: deleteBeginning();
break;
case 2: deleteEnd();
break;
case 3: printf("Enter the Node value to be deleted: ");
scanf("%d",&location);
deleteSpecic(location);
break;
case 4: goto EndSwitch;
default: printf("\nPlease select correct Deleting option!!!\n");
}
}
EndSwitch: break;
case 3: display();
break;
case 4: exit(0);
default: printf("\nPlease select correct option!!!");
}
}
}

void insertAtBeginning(int value)


{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode -> data = value;
newNode -> previous = NULL;
if(head == NULL)
{
newNode -> next = NULL;
head = newNode;
}
else
{
newNode -> next = head;
head = newNode;
}
printf("\nInsertion success!!!");
}
void insertAtEnd(int value)
{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode -> data = value;
newNode -> next = NULL;
if(head == NULL)
{
newNode -> previous = NULL;
head = newNode;
}
else
{
struct Node *temp = head;
while(temp -> next != NULL)
temp = temp -> next;
temp -> next = newNode;
newNode -> previous = temp;
}
printf("\nInsertion success!!!");
}
void insertAfter(int value, int location)
{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode -> data = value;
if(head == NULL)
{
newNode -> previous = newNode -> next = NULL;
head = newNode;
}
else
{
struct Node *temp1 = head, temp2;
while(temp1 -> data != location)
{
if(temp1 -> next == NULL)
{
printf("Given node is not found in the list!!!");
goto EndFunction;
}
else
{
temp1 = temp1 -> next;
}
}
temp2 = temp1 -> next;
temp1 -> next = newNode;
newNode -> previous = temp1;
newNode -> next = temp2;
temp2 -> previous = newNode;
printf("\nInsertion success!!!");
}
EndFunction:
}
void deleteBeginning()
{
if(head == NULL)
printf("List is Empty!!! Deletion not possible!!!");
else
{
struct Node *temp = head;
if(temp -> previous == temp -> next)
{
head = NULL;
free(temp);
}
else{
head = temp -> next;
head -> previous = NULL;
free(temp);
}
printf("\nDeletion success!!!");
}
}
void deleteEnd()
{
if(head == NULL)
printf("List is Empty!!! Deletion not possible!!!");
else
{
struct Node *temp = head;
if(temp -> previous == temp -> next)
{
head = NULL;
free(temp);
}
else{
while(temp -> next != NULL)
temp = temp -> next;
temp -> previous -> next = NULL;
free(temp);
}
printf("\nDeletion success!!!");
}
}
void deleteSpecific(int delValue)
{
if(head == NULL)
printf("List is Empty!!! Deletion not possible!!!");
else
{
struct Node *temp = head;
while(temp -> data != delValue)
{
if(temp -> next == NULL)
{
printf("\nGiven node is not found in the list!!!");
goto FuctionEnd;
}
else
{
temp = temp -> next;
}
}
if(temp == head)
{
head = NULL;
free(temp);
}
else
{
temp -> previous -> next = temp -> next;
free(temp);
}
printf("\nDeletion success!!!");
}
FuctionEnd:
}
void display()
{
if(head == NULL)
printf("\nList is Empty!!!");
else
{
struct Node *temp = head;
printf("\nList elements are: \n");
printf("NULL <--- ");
while(temp -> next != NULL)
{
printf("%d <===> ",temp -> data);
}
printf("%d ---> NULL", temp -> data);
}
}
Circular Linked List
In single linked list, every node points to its next node in the sequence and the last node points NULL.
But in circular linked list, every node points to its next node in the sequence but the last node points to
the first node in the list.
A circular linked list is a sequence of elements in which every element has a link to its next element
in the sequence and the last element has a link to the first element.
That means circular linked list is similar to the single linked list except that the last node points to the
first node in the list.

Advantages of Circular Linked Lists:


1) Any node can be a starting point. We can traverse the whole list by starting from any point. We just
need to stop when the first visited node is visited again.
2) Useful for implementation of queue. Unlike this implementation, we don’t need to maintain two
pointers for front and rear if we use circular linked list. We can maintain a pointer to the last inserted
node and front can always be obtained as next of last.
3) Circular lists are useful in applications to repeatedly go around the list. For example, when multiple
applications are running on a PC, it is common for the operating system to put the running applications
on a list and then to cycle through them, giving each of them a slice of time to execute, and then
making them wait while the CPU is given to another application. It is convenient for the operating
system to use a circular list so that when it reaches the end of the list it can cycle around to the front of
the list.
4) Circular Doubly Linked Lists are used for implementation of advanced data structures like Fibonacci
Heap.

Operations
In a circular linked list, we perform the following operations...

1. Insertion
2. Deletion
3. Display
Insertion
In a circular linked list, the insertion operation can be performed in three ways. They are as follows...

1. Inserting At Beginning of the list


2. Inserting At End of the list
3. Inserting At Specific location in the list

Inserting At Beginning of the list


We can use the following steps to insert a new node at beginning of the circular linked list...

Algorithm
Step 1 - Create a newNode with given value.

Step 2 - Check whether list is Empty (head == NULL)

Step 3 - If it is Empty then, set head = newNode and newNode→next = head .

Step 4 - If it is Not Empty then, define a Node pointer 'temp' and initialize with 'head'.

Step 5 - Keep moving the 'temp' to its next node until it reaches to the last node (until 'temp → next == head').

Step 6 - Set 'newNode → next =head', 'head = newNode' and 'temp → next = head'.
Inserting At End of the list
We can use the following steps to insert a new node at end of the circular linked list...

Algorithm
Step 1 - Create a newNode with given value.

Step 2 - Check whether list is Empty (head == NULL).

Step 3 - If it is Empty then, set head = newNode and newNode → next = head.

Step 4 - If it is Not Empty then, define a node pointer temp and initialize with head.

Step 5 - Keep moving the temp to its next node until it reaches to the last node in the list (until temp →
next == head).

Step 6 - Set temp → next = newNode and newNode → next = head.

Inserting At Specific location in the list (After a Node)


We can use the following steps to insert a new node after a node in the circular linked list...

Algorithm
Step 1 - Create a newNode with given value.

Step 2 - Check whether list is Empty (head == NULL)

Step 3 - If it is Empty then, set head = newNode and newNode → next = head.
Step 4 - If it is Not Empty then, define a node pointer temp and initialize with head.

Step 5 - Keep moving the temp to its next node until it reaches to the node after which we want to insert the
newNode (until temp1 → data is equal to location, here location is the node value after which we want to insert
the newNode).

Step 6 - Every time check whether temp is reached to the last node or not. If it is reached to last node then
display 'Given node is not found in the list!!! Insertion not possible!!!' and terminate the function. Otherwise
move the temp to next node.

Step 7 - If temp is reached to the exact node after which we want to insert the newNode then check whether it is
last node (temp → next == head).

Step 8 - If temp is last node then set temp → next = newNode and newNode → next = head.

Step 8 - If temp is not last node then set newNode → next = temp → next and temp → next = newNode.

Deletion
In a circular linked list, the deletion operation can be performed in three ways those are as follows...

1. Deleting from Beginning of the list


2. Deleting from End of the list
3. Deleting a Specific Node

Deleting from Beginning of the list


We can use the following steps to delete a node from beginning of the circular linked list...

Algorithm
Step 1 - Check whether list is Empty (head == NULL)

Step 2 - If it is Empty then, display 'List is Empty!!! Deletion is not possible' and terminate the function.

Step 3 - If it is Not Empty then, define two Node pointers 'temp1' and 'temp2' and initialize both 'temp1' and
'temp2' with head.

Step 4 - Check whether list is having only one node (temp1 → next == head)

Step 5 - If it is TRUE then set head = NULL and delete temp1 (Setting Empty list conditions)

Step 6 - If it is FALSE move the temp1 until it reaches to the last node. (until temp1 → next == head )

Step 7 - Then set head = temp2 → next, temp1 → next = head and delete temp2.

Deleting from End of the list


We can use the following steps to delete a node from end of the circular linked list...

Algorithm
Step 1 - Check whether list is Empty (head == NULL)
Step 2 - If it is Empty then, display 'List is Empty!!! Deletion is not possible' and terminate the function.
Step 3 - If it is Not Empty then, define two Node pointers 'temp1' and 'temp2' and initialize 'temp1' with head.
Step 4 - Check whether list has only one Node (temp1 → next == head)
Step 5 - If it is TRUE. Then, set head = NULL and delete temp1. And terminate from the function.
(Setting Empty list condition)
Step 6 - If it is FALSE. Then, set 'temp2 = temp1 ' and move temp1 to its next node. Repeat the same
until temp1 reaches to the last node in the list. (until temp1 → next == head)
Step 7 - Set temp2 → next = head and delete temp1.
Deleting a Specific Node from the list
We can use the following steps to delete a specific node from the circular linked list...

Algorithm
Step 1 - Check whether list is Empty (head == NULL)
Step 2 - If it is Empty then, display 'List is Empty!!! Deletion is not possible' and terminate the function.
Step 3 - If it is Not Empty then, define two Node pointers 'temp1' and 'temp2' and initialize 'temp1' with head.
Step 4 - Keep moving the temp1 until it reaches to the exact node to be deleted or to the last node. And every
time set 'temp2 = temp1' before moving the 'temp1' to its next node.
Step 5 - If it is reached to the last node then display 'Given node not found in the list! Deletion not possible!!!'.
And terminate the function.
Step 6 - If it is reached to the exact node which we want to delete, then check whether list is having only one
node (temp1 → next == head)
Step 7 - If list has only one node and that is the node to be deleted then set head = NULL and
delete temp1 (free(temp1)).
Step 8 - If list contains multiple nodes then check whether temp1 is the first node in the list (temp1 == head).
Step 9 - If temp1 is the first node then set temp2 = head and keep moving temp2 to its next node
until temp2 reaches to the last node. Then set head = head → next, temp2 → next = head and delete temp1.
Step 10 - If temp1 is not first node then check whether it is last node in the list (temp1 → next == head).
Step 1 1- If temp1 is last node then set temp2 → next = head and delete temp1 (free(temp1)).
Step 12 - If temp1 is not first node and not last node then set temp2 → next = temp1 → next and
delete temp1 (free(temp1)).
Displaying a circular Linked List
We can use the following steps to display the elements of a circular linked list...

Algorithm
Step 1 - Check whether list is Empty (head == NULL)
Step 2 - If it is Empty, then display 'List is Empty!!!' and terminate the function.
Step 3 - If it is Not Empty then, define a Node pointer 'temp' and initialize with head.
Step 4 - Keep displaying temp → data with an arrow (--->) until temp reaches to the last node
Step 5 - Finally display temp → data with arrow pointing to head → data.

Implementation in C
#include<conio.h>

void insertAtBeginning(int);
void insertAtEnd(int);
void insertAtAfter(int,int);
void deleteBeginning();
void deleteEnd();
void deleteSpecific(int);
void display();

struct Node
{
int data;
struct Node *next;
}*head = NULL;
void main()
{
int choice1, choice2, value, location;
clrscr();
while(1)
{
printf("\n*********** MENU *************\n");
printf("1. Insert\n2. Delete\n3. Display\n4. Exit\nEnter your choice: ");
scanf("%d",&choice1);
switch()
{
case 1: printf("Enter the value to be inserted: ");
scanf("%d",&value);
while(1)
{
printf("\nSelect from the following Inserting options\n");
printf("1. At Beginning\n2. At End\n3. After a Node\n4. Cancel\nEnter your choice:
");
scanf("%d",&choice2);
switch(choice2)
{
case 1: insertAtBeginning(value);
break;
case 2: insertAtEnd(value);
break;
case 3: printf("Enter the location after which you want to insert: ");
scanf("%d",&location);
insertAfter(value,location);
break;
case 4: goto EndSwitch;
default: printf("\nPlease select correct Inserting option!!!\n");
}
}
case 2: while(1)
{
printf("\nSelect from the following Deleting options\n");
printf("1. At Beginning\n2. At End\n3. Specific Node\n4. Cancel\nEnter your choice:
");
scanf("%d",&choice2);
switch(choice2)
{
case 1: deleteBeginning();
break;
case 2: deleteEnd();
break;
case 3: printf("Enter the Node value to be deleted: ");
scanf("%d",&location);
deleteSpecic(location);
break;
case 4: goto EndSwitch;
default: printf("\nPlease select correct Deleting option!!!\n");
}
}
EndSwitch: break;
case 3: display();
break;
case 4: exit(0);
default: printf("\nPlease select correct option!!!");
}
}
}

void insertAtBeginning(int value)


{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode -> data = value;
if(head == NULL)
{
head = newNode;
newNode -> next = head;
}
else
{
struct Node *temp = head;
while(temp -> next != head)
temp = temp -> next;
newNode -> next = head;
head = newNode;
temp -> next = head;
}
printf("\nInsertion success!!!");
}
void insertAtEnd(int value)
{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode -> data = value;
if(head == NULL)
{
head = newNode;
newNode -> next = head;
}
else
{
struct Node *temp = head;
while(temp -> next != head)
temp = temp -> next;
temp -> next = newNode;
newNode -> next = head;
}
printf("\nInsertion success!!!");
}
void insertAfter(int value, int location)
{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode -> data = value;
if(head == NULL)
{
head = newNode;
newNode -> next = head;
}
else
{
struct Node *temp = head;
while(temp -> data != location)
{
if(temp -> next == head)
{
printf("Given node is not found in the list!!!");
goto EndFunction;
}
else
{
temp = temp -> next;
}
}
newNode -> next = temp -> next;
temp -> next = newNode;
printf("\nInsertion success!!!");
}
EndFunction:
}
void deleteBeginning()
{
if(head == NULL)
printf("List is Empty!!! Deletion not possible!!!");
else
{
struct Node *temp = head;
if(temp -> next == head)
{
head = NULL;
free(temp);
}
else{
head = head -> next;
free(temp);
}
printf("\nDeletion success!!!");
}
}
void deleteEnd()
{
if(head == NULL)
printf("List is Empty!!! Deletion not possible!!!");
else
{
struct Node *temp1 = head, temp2;
if(temp1 -> next == head)
{
head = NULL;
free(temp1);
}
else{
while(temp1 -> next != head){
temp2 = temp1;
temp1 = temp1 -> next;
}
temp2 -> next = head;
free(temp1);
}
printf("\nDeletion success!!!");
}
}
void deleteSpecific(int delValue)
{
if(head == NULL)
printf("List is Empty!!! Deletion not possible!!!");
else
{
struct Node *temp1 = head, temp2;
while(temp1 -> data != delValue)
{
if(temp1 -> next == head)
{
printf("\nGiven node is not found in the list!!!");
goto FuctionEnd;
}
else
{
temp2 = temp1;
temp1 = temp1 -> next;
}
}
if(temp1 -> next == head){
head = NULL;
free(temp1);
}
else{
if(temp1 == head)
{
temp2 = head;
while(temp2 -> next != head)
temp2 = temp2 -> next;
head = head -> next;
temp2 -> next = head;
free(temp1);
}
else
{
if(temp1 -> next == head)
{
temp2 -> next = head;
}
else
{
temp2 -> next = temp1 -> next;
}
free(temp1);
}
}
printf("\nDeletion success!!!");
}
FuctionEnd:
}
void display()
{
if(head == NULL)
printf("\nList is Empty!!!");
else
{
struct Node *temp = head;
printf("\nList elements are: \n");
while(temp -> next != head)
{
printf("%d ---> ",temp -> data);
}
printf("%d ---> %d", temp -> data, head -> data);
}
}

Polynomial Representation
A polynomial p(x) is the expression in variable x which is in the form (axn + bxn-1 + …. + jx+ k), where a, b,
c …., k fall in the category of real numbers and 'n' is non negative integer, which is called the degree of
polynomial.
An essential characteristic of the polynomial is that each term in the polynomial expression consists of
two parts:

• one is the coefficient


• other is the exponent

Example:
10x2 + 26x, here 10 and 26 are coefficients and 2, 1 is its exponential value.
Points to keep in Mind while working with Polynomials:

• The sign of each coefficient and exponent is stored within the coefficient and the exponent itself
• Additional terms having equal exponent is possible one
• The storage allocation for each term in the polynomial must be done in ascending and descending
order of their exponent.

Representation of Polynomial
Polynomial can be represented in the various ways. These are:

• By the use of arrays


• By the use of Linked List

Representation of Polynomial Using Arrays


There may arise some situation where you need to evaluate many polynomial expressions and perform
basic arithmetic operations like addition and subtraction with those numbers. For this, you will have to
get a way to represent those polynomials. The simple way is to represent a polynomial with degree 'n'
and store the coefficient of n+1 terms of the polynomial in the array. So every array element will
consist of two values:

• Coefficient and
• Exponent Coefficient
6 10 6 4

0 1 2 3 Exponent
Addition of Two Polynomial
For adding two polynomials using arrays is straightforward method, since both the arrays may be
added up element wise beginning from 0 to n-1, resulting in addition of two polynomials. Addition of
two polynomials using linked list requires comparing the exponents, and wherever the exponents are
found to be same, the coefficients are added up. For terms with different exponents, the complete
term is simply added to the result thereby making it a part of addition result.

Implementation in C
#include<stdio.h>
#include<math.h>
/*
This structure is used to store a polynomial term. An array of such terms represents a
polynomial.
The "coeff" element stores the coefficient of a term in the polynomial,while
the "exp" element stores the exponent.
*/
struct poly
{
float coeff;
int exp;
};
//declaration of polynomials
struct poly a[50],b[50],c[50],d[50];
int main()
{
int i;
int deg1,deg2; //stores degrees of the polynomial
int k=0,l=0,m=0;
printf("Enter the highest degree of polynomial1:");
scanf("%d",&deg1); //taking polynomial terms from the user
for(i=0;i<=deg1;i++)
{
//entering values in coefficient of the polynomial terms
printf("\nEnter the coeff of x^%d :",i);
scanf("%f",&a[i].coeff);
//entering values in exponent of the polynomial terms
a[k++].exp = i;
}
//taking second polynomial from the user
printf("\nEnter the highest degree of polynomial2:");
scanf("%d",&deg2);
for(i=0;i<=deg2;i++)
{
printf("\nEnter the coeff of x^%d :",i);
scanf("%f",&b[i].coeff);
b[l++].exp = i;
}
//printing first polynomial
printf("\nExpression 1 = %.1f",a[0].coeff);
for(i=1;i<=deg1;i++)
{
printf("+ %.1fx^%d",a[i].coeff,a[i].exp);
}
//printing second polynomial
printf("\nExpression 2 = %.1f",b[0].coeff);
for(i=1;i<=deg2;i++)
{
printf("+ %.1fx^%d",b[i].coeff,b[i].exp);
}
//Adding the polynomials
if(deg1>deg2)
{
for(i=0;i<=deg2;i++)
{
c[m].coeff = a[i].coeff + b[i].coeff; c[m].exp = a[i].exp;
m++;
}
for(i=deg2+1;i<=deg1;i++)
{
c[m].coeff = a[i].coeff;
c[m].exp = a[i].exp;
m++;
}
}
else
{
for(i=0;i<=deg1;i++)
{
c[m].coeff = a[i].coeff + b[i].coeff;
c[m].exp = a[i].exp;
m++;
}
for(i=deg1+1;i<=deg2;i++)
{
c[m].coeff = b[i].coeff;
c[m].exp = b[i].exp;
m++;
}
}
//printing the sum of the two polynomials
printf("\nExpression after addition = %.1f",c[0].coeff);
for(i=1;i<m;i++)
{
printf("+ %.1fx^%d",c[i].coeff,c[i].exp);
}
return 0;
}

Multiplication of Two Polynomial


Multiplication of two polynomials however requires manipulation of each node such that the
exponents are added up and the coefficients are multiplied. After each term of first polynomial is
operated upon with each term of the second polynomial, then the result has to be added up by
comparing the exponents and adding the coefficients for similar exponents and including terms as such
with dissimilar exponents in the result.
Consider below program to understand multiplication of two polynomial

Implementation in C
#include<stdio.h>
#include<math.h>
/* This structure is used to store a polynomial term. An array of such terms represents a
polynomial.
The "coeff" element stores the coefficient of a term in the polynomial,while
the "exp" element stores the exponent.
*/
struct poly
{
float coeff;
int exp;
};
//declaration of polynomials
struct poly a[50],b[50],c[50],d[50];
int main()
{
int i;
int deg1,deg2; //stores degrees of the polynomial
int k=0,l=0,m=0;
printf("Enter the highest degree of polynomial1:");
scanf("%d",&deg1); //taking polynomial terms from the user
for(i=0;i<=deg1;i++)
{
//entering values in coefficient of the polynomial terms
printf("\nEnter the coeff of x^%d :",i);
scanf("%f",&a[i].coeff); //entering values in exponent of the polynomial terms
a[k++].exp = i;
}
//taking second polynomial from the user
printf("\nEnter the highest degree of polynomial2:");
scanf("%d",&deg2);
for(i=0;i<=deg2;i++)
{
printf("\nEnter the coeff of x^%d :",i);
scanf("%f",&b[i].coeff);
b[l++].exp = i;
}
//printing first polynomial
printf("\nExpression 1 = %.1f",a[0].coeff);
for(i=1;i<=deg1;i++)
{
printf("+ %.1fx^%d",a[i].coeff,a[i].exp);
}
//printing second polynomial
printf("\nExpression 2 = %.1f",b[0].coeff);
for(i=1;i<=deg2;i++)
{
printf("+ %.1fx^%d",b[i].coeff,b[i].exp);
}
//Multiply the polynomials
if(deg1>deg2)
{
for(i=0;i<=deg2;i++)
{
c[m].coeff = a[i].coeff * b[i].coeff;
c[m].exp = a[i].exp;
m++;
}
for(i=deg2+1;i<=deg1;i++)
{
c[m].coeff = a[i].coeff;
c[m].exp = a[i].exp;
m++;
}
}
else
{
for(i=0;i<=deg1;i++)
{
c[m].coeff = a[i].coeff * b[i].coeff;
c[m].exp = a[i].exp;
m++;
}
for(i=deg1+1;i<=deg2;i++)
{
c[m].coeff = b[i].coeff;
c[m].exp = b[i].exp;
m++;
}
}
//printing the product of the two polynomials
printf("\nExpression after multiplication = %.1f",c[0].coeff);
for(i=1;i<m;i++)
{
printf("+ %.1fx^%d",c[i].coeff,c[i].exp);
}
return 0;
}

Representation of Polynomial Using Linked List


To understand this concept better let's first brush up all the basic contents that are required.
linked list is a data structure that stores each element as an object in a node of the list. every note
contains two parts data han and links to the next node.
Polynomial is a mathematical expression that consists of variables and coefficients. for example x^2 -
4x + 7
In the Polynomial linked list, the coefficients and exponents of the polynomial are defined as the data
node of the list.
For adding two polynomials that are stored as a linked list. We need to add the coefficients of variables
with the same power. In a linked list node contains 3 members, coefficient value link to the next node.
a linked list that is used to store Polynomial looks like −
Polynomial : 4x7 + 12x2 + 45

This is how a linked list represented polynomial looks like.

Addition of two polynomials using Linked list


Adding two polynomials that are represented by a linked list. We check values at the exponent value of
the node. For the same values of exponent, we will add the coefficients.
Example,

Input :
p1= 13x8 + 7x5 + 32x2 + 54
p2= 3x12 + 17x5 + 3x3 + 98

Output : 3x12 + 13x8 + 24x5 + 3x3 + 32x2 + 152


Algorithm
Input − polynomial p1 and p2 represented as a linked list.

Step 1: loop around all values of linked list and follow step 2& 3.
Step 2: if the value of a node’s exponent. is greater copy this node to result node and head towards the
next node.
Step 3: if the values of both node’s exponent is same add the coefficients and then copy the added value
with node to the result.
Step 4: Print the resultant node.

Implementation in C
#include<bits/stdc++.h>
using namespace std;
struct Node{
int coeff;
int pow;
struct Node *next;
};
void create_node(int x, int y, struct Node **temp){
struct Node *r, *z;
z = *temp;
if(z == NULL){
r =(struct Node*)malloc(sizeof(struct Node));
r->coeff = x;
r->pow = y;
*temp = r;
r->next = (struct Node*)malloc(sizeof(struct Node));
r = r->next;
r->next = NULL;
} else {
r->coeff = x;
r->pow = y;
r->next = (struct Node*)malloc(sizeof(struct Node));
r = r->next;
r->next = NULL;
}
}
void polyadd(struct Node *p1, struct Node *p2, struct Node *result){
while(p1->next && p2->next){
if(p1->pow > p2->pow){
result->pow = p1->pow;
result->coeff = p1->coeff;
p1 = p1->next;
}
else if(p1->pow < p2->pow){
result->pow = p2->pow;
result->coeff = p2->coeff;
p2 = p2->next;
} else {
result->pow = p1->pow;
result->coeff = p1->coeff+p2->coeff;
p1 = p1->next;
p2 = p2->next;
}
result->next = (struct Node *)malloc(sizeof(struct Node));
result = result->next;
result->next = NULL;
}
while(p1->next || p2->next){
if(p1->next){
result->pow = p1->pow;
result->coeff = p1->coeff;
p1 = p1->next;
}
if(p2->next){
result->pow = p2->pow;
result->coeff = p2->coeff;
p2 = p2->next;
}
result->next = (struct Node *)malloc(sizeof(struct Node));
result = result->next;
result->next = NULL;
}
}
void printpoly(struct Node *node){
while(node->next != NULL){
printf("%dx^%d", node->coeff, node->pow);
node = node->next;
if(node->next != NULL)
printf(" + ");
}
}
int main(){
struct Node *p1 = NULL, *p2 = NULL, *result = NULL;
create_node(41,7,&p1);
create_node(12,5,&p1);
create_node(65,0,&p1);
create_node(21,5,&p2);
create_node(15,2,&p2);
printf("polynomial 1: ");
printpoly(p1);
printf("\npolynomial 2: ");
printpoly(p2);
result = (struct Node *)malloc(sizeof(struct Node));
polyadd(p1, p2, result);
printf("\npolynomial after adding p1 and p2 : ");
printpoly(result);
return 0;
}

Multiplication of two polynomials using Linked list


Algorithm
The algorithm follows very basic algebraic operations an those are described as follows:
1. Input the multiplicand and multiplier and multiplier
2. Set both the polynomial in descending order of the coefficient
3. Multiply each node of multiplicand with each node of the multiplier (multiplication of the
coefficient part and addition of the exponent part) and add them into a newly formed linked list
in descending order
4. Coefficient having the same exponent value are added up with each other in the list and no two
nodes have the same exponent value.
5. Then the product is to be displayed in a proper way in the form of ax^n+bx^n-1+…
6. Certain points to be noted before displaying a polynomial: Any coefficient with value 0 must not
be displayed, 1x^n+2x^n-1 must not be displayed … node having coefficient value 1 must be
displayed as x^n, node with exponent value 0 must be displayed as x not x^0, 1x^n-2x^n-1
format should be maintained not standard like errors 1x^n+-2x^n-1 should come up.

Implementation in C
#include<stdio.h>
#include<stdlib.h>

struct node {
int coefficient, exponent;
struct node *next;
};

struct node *hPtr1, *hPtr2, *hPtr3;


/*
* creates new node and fill the given data
*/
struct node * buildNode(int coefficient, int exponent) {
struct node *ptr = (struct node *) malloc(sizeof (struct node));
ptr->coefficient = coefficient;
ptr->exponent = exponent;
ptr->next = NULL;
return ptr;
}

/* insert data in decending order - based on exponent value */


void polynomial_insert(struct node ** myNode, int coefficient, int exponent) {
struct node *lPtr, *pPtr, *qPtr = *myNode;
lPtr = buildNode(coefficient, exponent);

/* inserting new node at appropriate position */


if (*myNode == NULL || (*myNode)->exponent < exponent) {
*myNode = lPtr;
(*myNode)->next = qPtr;
return;
}

/* placing new node between two nodes or end of node */


while (qPtr) {
pPtr = qPtr;
qPtr = qPtr->next;
if (!qPtr) {
pPtr->next = lPtr;
break;

}
else if ((exponent < pPtr->exponent) && (exponent > qPtr->exponent){
lPtr->next = qPtr;
pPtr->next = lPtr;
break;
}
}
return;
}

/* inserting new node with resultant data into the output list (n1) */
void polynomial_add(struct node **n1, int coefficient, int exponent) {
struct node *x = NULL, *temp = *n1;
if (*n1 == NULL || (*n1)->exponent < exponent) {
/* adding at the front */
*n1 = x = buildNode(coefficient, exponent);
(*n1)->next = temp;
} else {
while (temp) {
if (temp->exponent == exponent) {
/* updating the co-efficient value alone */
temp->coefficient = temp->coefficient + coefficient;
return;
}
if (temp->exponent > exponent && (!temp->next || temp->next->exponent < exponent)) {
/* inserting in the middle or end */
x = buildNode(coefficient, exponent);
x->next = temp->next;
temp->next = x;
return;
}
temp = temp->next;
}
x->next = NULL;
temp->next = x;
}
}

void polynomial_multiply(struct node **n1, struct node *n2, struct node *n3) {
struct node * temp;
int coefficient, exponent;

temp = n3;

/* if both input list are absent, then output list is NULL */


if (!n2 && !n3)
return;

/* input list 1(n2) is absent, then output list is input list2 (n3) */
if (!n2) {
*n1 = n3;
} else if (!n3) {

/*
* list n3 is absent, then o/p list is n2
*/
*n1 = n2;
} else {
while (n2) {
while (n3) {
/* multiply coefficient & add exponents */
coefficient = n2->coefficient * n3->coefficient;
exponent = n2->exponent + n3->exponent;
n3 = n3->next;
/* insert the above manipulated data to o/p list */
polynomial_add(n1, coefficient, exponent);
}
n3 = temp;
n2 = n2->next;
}
}
return;
}

/* delete the given input list */


struct node * polynomial_deleteList(struct node *ptr) {
struct node *temp;
while (ptr){
temp = ptr->next;
free(ptr);
ptr = temp;
}
return NULL;
}

void polynomial_view(struct node *ptr) {


int i = 0;
int flag=0;
while (ptr) {
if(ptr->exponent != 0 || ptr->exponent != 1 ){
if(ptr->coefficient > 0 && flag==0 ){
printf("%dx^%d", ptr->coefficient,ptr->exponent);
flag++;
}
else if (ptr->coefficient > 0 && flag==1 )
printf("+%dx^%d", ptr->coefficient,ptr->exponent);
else if(ptr->coefficient < 0)
printf("%dx^%d", ptr->coefficient,ptr->exponent);
}
else if (ptr->exponent == 0){
if(ptr->coefficient > 0 && flag==0 ){
printf("%d", ptr->coefficient);
flag++;
}
else if (ptr->coefficient > 0 && flag==1 )
printf("+%d", ptr->coefficient);
else if(ptr->coefficient < 0)
printf("%d", ptr->coefficient);
}
else if( ptr->exponent == 1 ){
if(ptr->coefficient > 0 && flag==0 ){
printf("%dx", ptr->coefficient);
flag++;
}
else if (ptr->coefficient > 0 && flag==1 )
printf("+%dx", ptr->coefficient);
else if(ptr->coefficient < 0)
printf("%dx", ptr->coefficient);
}
ptr = ptr->next;
i++;
}
printf("\n");
return;
}

int main (int argc, char *argv[])


{
int coefficient, exponent, i, n;
int count;
printf("-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=\n");
printf(" Multiplication of Two Polynomials\n");
printf("-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=\n");
printf("Enter the number of coefficients in the multiplicand:");
scanf("%d",&count);
for(i=0;i<count;i++)
{
printf("Enter the coefficient part:");
scanf("%d", &coefficient);
printf("Enter the exponent part:");
scanf("%d",&exponent);
polynomial_insert(&hPtr1, coefficient, exponent);
}
printf("Enter the number of coefficients in the multiplier:");
scanf("%d",&count);
for(i=0;i<count;i++)
{
printf("Enter the coefficient part:");
scanf("%d", &coefficient);
printf("Enter the exponent part:");
scanf("%d",&exponent);
polynomial_insert(&hPtr2, coefficient, exponent);
}
printf("Polynomial Expression 1: ");
polynomial_view(hPtr1);
printf("Polynomial Expression 2: ");
polynomial_view(hPtr2);

polynomial_multiply(&hPtr3, hPtr1, hPtr2);

printf("Output:\n");
polynomial_view(hPtr3);

printf("-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
hPtr1 = polynomial_deleteList(hPtr1);
hPtr2 = polynomial_deleteList(hPtr2);
hPtr3 = polynomial_deleteList(hPtr3);

return 0;
}

Two Variables Polynomial


Polynomials in two variables are algebraic expressions consisting of terms in the form axnym . The
degree of each term in a polynomial in two variables is the sum of the exponents in each term and
the degree of the polynomial is the largest such sum.

Here are some examples of polynomials in two variables and their degrees.

x2y−6x3y12+10x2−7y+1 degree : 15

6x4+8y4−xy2 degree : 4

x4y2−x3y3−xy+x4 degree : 6

6x14−10y3+3x−11y degree : 14

You might also like