Data Structure Typed Notes-Unit-1
Data Structure Typed Notes-Unit-1
DATA STRUCTURE
JULY 2020
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.
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
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
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.
Definiteness- Every step of the algorithm should be clear and well defined.
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.
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.
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.
If we have two algorithms with the following expressions representing the time required by
them for execution, then:
Expression 1: (20n2 + 3n - 4)
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.
Big Oh(O)
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.
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.
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.
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.
Instruction Space: It's the amount of memory used to save the compiled version of instructions.
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().
But while calculating the Space Complexity of any algorithm, we usually consider only Data Space and
we neglect the Instruction Space and Environmental Stack.
Type Size
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[]
x = x + a[i];
return(x);
In the above code, 4*n bytes of space is required for the array a[] elements.
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.
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
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.
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.
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++)
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.
high = mid - 1;
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)
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.
Big Omega denotes "more than or the same as" <expression> iterations.
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,
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).
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.
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.
Stack −
isFull(), This is used to check whether stack is full or not
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
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
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.
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,
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.
= 1000+24
= 1024
Basic Operations
Following are the basic operations supported by an array.
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.
Insertion in an Array
Following can be a situation with array insertion:
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
4. End
Implementation in C
#include <stdio.h>
#define MAX 5
void main()
array[i+1] = array[i];
array[0] = value;
N++;
// print to confirm
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()
{ array[i+1] = array[i];
}
// add new element at first position
array[index] = value;
N++;
// print to confirm
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
A[index + 1] = New_Element
4. End
Implementation in C
#include <stdio.h>
#define MAX 5
void main()
array[i + 1] = array[i];
array[index + 1] = value;
N++;
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
5. Set J = J+1
6. Set N = N-1
7. Stop
void main()
int k = 3, n = 5, i, j;
j = k;
while( j < n)
LA[j-1] = LA[j];
j = j + 1;
n = n -1;
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.
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;
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.
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:
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);
}
}
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;
LA[k-1] = item;
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.
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.
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:
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.
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:
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 .
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:
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.
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++;
int compactMatrix[3][size];
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>
}
else
{
while (temp->next != NULL)
temp = temp->next;
printf("row_position: ");
while(temp != NULL)
{
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;
PrintList(start);
return 0;
}
(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:
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.
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.
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.
s11 s12 0 0
s21 s22 s23 0
0 s32 s33 ss34
0 0 s43 s44
= 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.
Basic Operations
Following are the basic operations supported by a list.
Let's know more about them and how they are different from each other.
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.
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.
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
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.
Implementation in C
#include <stdlib.h>
/* Structure of a node */
struct node {
int data; // Data
struct node *next; // Address
}*head;
int main()
{
int n, data;
return 0;
}
temp = head;
temp = temp->next;
}
}
/* Create a new node and inserts at the beginning of the linked list. */
void insertNodeAtBeginning(int data)
{
struct node *newNode;
if(newNode == NULL)
{
printf("Unable to allocate memory.");
}
else
{
newNode->data = data; // Link data part
newNode->next = head; // Link address part
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
Implementation in C
#include <stdlib.h>
int main()
{
int n, data, position;
return 0;
}
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;
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>
int main()
{
int n, data;
return 0;
}
/* Create a new node and inserts at the end of the linked list. */
void insertNodeAtEnd(int data)
{
struct node *newNode, *temp;
if(newNode == NULL)
{
printf("Unable to allocate memory.");
}
else
{
newNode->data = data; // Link the data part
newNode->next = NULL;
temp = head;
Algorithm
To delete the first node from Singly Linked List
Implementation in C
#include <stdlib.h>
int main()
{
int n, choice;
return 0;
}
if(head == NULL)
{
printf("List is already empty.");
}
else
{
toDelete = head;
head = head->next;
Implementation in C
int main()
{
int n, position;
return 0;
}
if(head == NULL)
{
printf("List is already empty.");
}
else
{
toDelete = head;
prevNode = head;
if(toDelete == NULL)
break;
}
if(toDelete != NULL)
{
if(toDelete == head)
head = head->next;
prevNode->next = toDelete->next;
toDelete->next = NULL;
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.
• 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;
int main()
{
int n;
createList(n);
return 0;
}
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();
return 0;
}
index = 0;
curNode = head;
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));
}
• 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.
Step 1 - Create a newNode with given value and newNode → prev as NULL.
Step 4 - If it is not Empty then, assign head to newNode → next and newNode to head.
Algorithm
Step 1 - Create a newNode with given value and newNode → next as 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).
Algorithm
Step 1 - Create a newNode with given value.
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...
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.
Algorithm
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)).
Algorithm
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)
Algorithm
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 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!!!");
}
}
}
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...
Algorithm
Step 1 - Create a newNode with given value.
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 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).
Algorithm
Step 1 - Create a newNode with given value.
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...
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.
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!!!");
}
}
}
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:
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:
• 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",°1); //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",°2);
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;
}
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",°1); //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",°2);
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;
}
Input :
p1= 13x8 + 7x5 + 32x2 + 54
p2= 3x12 + 17x5 + 3x3 + 98
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;
}
Implementation in C
#include<stdio.h>
#include<stdlib.h>
struct node {
int coefficient, exponent;
struct node *next;
};
}
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;
/* 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;
}
printf("Output:\n");
polynomial_view(hPtr3);
printf("-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
hPtr1 = polynomial_deleteList(hPtr1);
hPtr2 = polynomial_deleteList(hPtr2);
hPtr3 = polynomial_deleteList(hPtr3);
return 0;
}
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