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

Data Structure Module - 1

The document discusses data structures and provides examples of linear and non-linear data structures. It describes arrays as a linear data structure that stores elements of the same type in contiguous memory locations. Common operations on arrays include insertion, deletion, searching, and sorting. The document also discusses stacks as a linear data structure that follows LIFO operations of push and pop.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
54 views

Data Structure Module - 1

The document discusses data structures and provides examples of linear and non-linear data structures. It describes arrays as a linear data structure that stores elements of the same type in contiguous memory locations. Common operations on arrays include insertion, deletion, searching, and sorting. The document also discusses stacks as a linear data structure that follows LIFO operations of push and pop.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 51

DATA STRUCTURES

Introduction to Data Structures:

A data structure is a way of storing and organizing data in a computer’s memory that it could make the
data quickly available to the processor for required computation.

A data structure should be seen as a logical concept that must address two fundamental concerns.

1. First, how the data will be stored, and

2. Second, what operations will be performed on it.

It can be classified as

• Linear data structure


• Non-linear data structure

Linear Data Structure:

Linear data structures can be constructed as a continuous arrangement of data elements in the memory.

For example : Array, Stack, Queue, Tables, List, and Linked Lists.

Operations applied on linear data structure:

The following list of operations applied on linear data structures

1. Insert an element

2. Delete an element

3. Traverse

4. Sort the list of elements

5. Search for a data element

Non-linear Data Structure:

Non-linear data structure can be constructed as a collection of randomly distributed set of data item
joined together by using a special pointer (tag).

For example: Tree, Decision tree, Graph and Forest

Operations applied on non-linear data structures:

The following list of operations applied on non-linear data structures.

1. Insert elements
2. Delete elements

3. Search for a data element

4. Sort the list of elements

Array:

An array is a linear data structure that collects elements of the same data type and stores them
in contiguous and adjacent memory locations. Arrays work on an index system starting from 0 to
(n-1), where n is the size of the array.

There are majorly two types of arrays, they are:

One-Dimensional Arrays:

You can imagine a 1d array as a row, where elements are stored one after another.

Multi-Dimensional Arrays:
These multi-dimensional arrays are again of two types. They are:

Two-Dimensional Arrays:

You can imagine it like a table where each cell contains elements.

Similarly Three-Dimensional Array can be visualized as multi layered two-dimensional array.


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

o Each element in an array is of the same data type and carries the same size that is
4 bytes.
o Elements in the array are stored at contiguous memory locations from which the
first element is stored at the smallest memory location.
o Elements of the array can be randomly accessed since we can calculate the address
of each element of the array with the given base address and the size of the data
element.

Arrays are typically defined with square brackets with the size of the arrays as its argument and
specifying its type.

1D Arrays: int arr[n];

2D Arrays: char arr[m][n];

3D Arrays: float arr[m][n][p];

Operations on an Array:

• Insertion

• Deletion

• Searching

• Sorting

Insertion operation
This operation is performed to insert one or more elements into the array. As per the
requirements, an element can be added at the beginning, end, or at any index of the array.
Now, let's see the implementation of inserting an element into the array.
1. #include <stdio.h>
2. int main()
3. {
4. int arr[20] = { 18, 30, 15, 70, 12 };
5. int i, x, pos, n = 5;
6. printf("Array elements before insertion\n");
7. for (i = 0; i < n; i++)
8. printf("%d ", arr[i]);
9. printf("\n");
10.
11. x = 50; // element to be inserted
12. pos = 4;
13. n++;
14.
15. for (i = n-1; i >= pos; i--)
16. arr[i] = arr[i - 1];
17. arr[pos - 1] = x;
18. printf("Array elements after insertion\n");
19. for (i = 0; i < n; i++)
20. printf("%d ", arr[i]);
21. printf("\n");
22. return 0;
23. }

Output

Deletion operation
As the name implies, this operation removes an element from the array and then
reorganizes all of the array elements.

1. #include <stdio.h>
2.
3. void main() {
4. int arr[] = {18, 30, 15, 70, 12};
5. int k = 30, n = 5;
6. int i, j;
7.
8. printf("Given array elements are :\n");
9.
10. for(i = 0; i<n; i++) {
11. printf("arr[%d] = %d, ", i, arr[i]);
12. }
13.
14. j = k;
15.
16. while( j < n) {
17. arr[j-1] = arr[j];
18. j = j + 1;
19. }
20.
21. n = n -1;
22.
23. printf("\nElements of array after deletion:\n");
24.
25. for(i = 0; i<n; i++) {
26. printf("arr[%d] = %d, ", i, arr[i]);
27. }
28. }

Output

Search operation
This operation is performed to search an element in the array based on the value or index.

1. #include <stdio.h>
2.
3. void main() {
4. int arr[5] = {18, 30, 15, 70, 12};
5. int item = 70, i, j=0 ;
6.
7. printf("Given array elements are :\n");
8.
9. for(i = 0; i<5; i++) {
10. printf("arr[%d] = %d, ", i, arr[i]);
11. }
12. printf("\nElement to be searched = %d", item);
13. while( j < 5){
14. if( arr[j] == item ) {
15. break;
16. }
17.
18. j = j + 1;
19. }
20.
21. printf("\nElement %d is found at %d position", item, j+1);
22. }

Output

Update operation
This operation is performed to update an existing array element located at the given
index.
1. #include <stdio.h>
2.
3. void main() {
4. int arr[5] = {18, 30, 15, 70, 12};
5. int item = 50, i, pos = 3;
6.
7. printf("Given array elements are :\n");
8.
9. for(i = 0; i<5; i++) {
10. printf("arr[%d] = %d, ", i, arr[i]);
11. }
12.
13. arr[pos-1] = item;
14. printf("\nArray elements after updation :\n");
15.
16. for(i = 0; i<5; i++) {
17. printf("arr[%d] = %d, ", i, arr[i]);
18. }
19. }

Output

Advantages of Array
o Array provides the single name for the group of variables of the same type. Therefore, it is
easy to remember the name of all the elements of an array.
o Traversing an array is a very simple process; we just need to increment the base address
of the array in order to visit each element one by one.
o Any element in the array can be directly accessed by using the index.
Disadvantages of Array
o Array is homogenous. It means that the elements with similar data type can be stored in
it.
o In array, there is static memory allocation that is size of an array cannot be altered.
o There will be wastage of memory if we store less number of elements than the declared
size.

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

Why to use Sparse Matrix instead of simple matrix ?


• Storage: There are lesser non-zero elements than zeros and thus lesser
memory can be used to store only those elements.
• Computing time: Computing time can be saved by logically designing a
data structure traversing only non-zero elements.

Example:
0 0 3 0 4
0 0 5 7 0
0 0 0 0 0
0 2 6 0 0
Representing a sparse matrix by a 2D array leads to wastage of lots of memory
as zeroes in the matrix are of no use in most of the cases. So, instead of storing
zeroes with non-zero elements, we only store non-zero elements. This means
storing non-zero elements with triples- (Row, Column, value).
Sparse Matrix Representations can be done in many ways following are two
common representations:
1. Array representation
2. Linked list representation

Method 1: Using Arrays:


2D array is used to represent a sparse matrix in which there are three rows
named as
• Row: Index of row, where non-zero element is located
• Column: Index of column, where non-zero element is located
• Value: Value of the non zero element located at index – (row,column)

Method 2: Using Linked Lists


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

Stack:
A stack is an Abstract Data Type (ADT), commonly used in most programming languages. It is
named stack as it behaves like a real-world stack, for example – a deck of cards or a pile of
plates, etc. A real-world stack allows operations at one end only.

This feature makes it LIFO data structure. LIFO stands for Last-in-first-out. Here, the element
which is placed (inserted or added) last, is accessed first. In stack terminology, insertion
operation is called PUSH operation and removal operation is called POP operation.

Stack Representation
The following diagram depicts a stack and its operations −
A stack can be implemented by means of Array, Structure, Pointer, and Linked List. Stack
can either be a fixed size one or it may have a sense of dynamic resizing. Here, we are
going to implement stack using arrays, which makes it a fixed size stack implementation.

Basic Operations
Stack operations may involve initializing the stack, using it and then de-initializing it. Apart
from these basic stuffs, a stack is used for the following two primary operations −
• push() − Pushing (storing) an element on the stack.
• pop() − Removing (accessing) an element from the stack.
When data is PUSHed onto stack.
To use a stack efficiently, we need to check the status of stack as well. For the same
purpose, the following functionality is added to stacks −
• peek() − get the top data element of the stack, without removing it.
• isFull() − check if stack is full.
• isEmpty() − check if stack is empty.
At all times, we maintain a pointer to the last PUSHed data on the stack. As this pointer
always represents the top of the stack, hence named top. The top pointer provides top
value of the stack without actually removing it.
First we should learn about procedures to support stack functions −
peek()
Algorithm of peek() function −
begin procedure peek
return stack[top]
end procedure

isfull()
Algorithm of isfull() function −
begin procedure isfull

if top equals to MAXSIZE


return true
else
return false
endif

end procedure

isempty()
Algorithm of isempty() function −
begin procedure isempty

if top less than 1


return true
else
return false
endif

end procedure

Push Operation
The process of putting a new data element onto stack is known as a Push Operation.
Push operation involves a series of steps −
• Step 1 − Checks if the stack is full.
• Step 2 − If the stack is full, produces an error and exit.
• Step 3 − If the stack is not full, increments top to point next empty space.
• Step 4 − Adds data element to the stack location, where top is pointing.
• Step 5 − Returns success.
void push(int data) {
if(!isFull()) {
top = top + 1;
stack[top] = data;
} else {
printf("Could not insert data, Stack is full.\n");
}
}

Pop Operation
Accessing the content while removing it from the stack, is known as a Pop Operation. In
an array implementation of pop() operation, the data element is not actually removed,
instead top is decremented to a lower position in the stack to point to the next value. But
in linked-list implementation, pop() actually removes data element and deallocates
memory space.
A Pop operation may involve the following steps −
• Step 1 − Checks if the stack is empty.
• Step 2 − If the stack is empty, produces an error and exit.
• Step 3 − If the stack is not empty, accesses the data element at which top is
pointing.
• Step 4 − Decreases the value of top by 1.
• Step 5 − Returns success.
int pop(int data) {

if(!isempty()) {
data = stack[top];
top = top - 1;
return data;
} else {
printf("Could not retrieve data, Stack is empty.\n");
}
}

Queue :
The Queue in data structure is an ordered, linear sequence of items. It is a FIFO (First In
First Out) data structure, which means that we can insert an item to the rear end of the
queue and remove from the front of the queue only. A Queue is a sequential data type,
unlike an array, in an array, we can access any of its elements using indexing, but we can
only access the element at the front of the queue at a time.
Queue Representation

A Queue in data structure can be accessed from both of its sides (at the front for
deletion and back for insertion).

The following diagram tries to explain the queue representation as a data structure-

Working of Queue

The Queue in data structure uses the FIFO (First In First Out) approach. Initially, we will
set a front pointer to keep track of the front most element of the queue. Then the queue
is initialized to -1 as its front, as we will add (enqueue) elements to the queue, the front
gets updated to point to its front most element and if we remove (dequeue) elements
from the queue, the front gets reduced.

We can use queue to perform its main two operations: Enqueue and Dequeue, other
operations being Peek, isEmpty and isFull.

Queue operations

Enqueue

The Enqueue operation is used to add an element to the front of the queue.

Steps of the algorithm:

1. Check if the Queue is full.


2. Set the front as 0 for the first element.
3. Increase rear by 1.
4. Add the new element at the rear index.

Dequeue

The Dequeue operation is used to remove an element from the rear of the queue.

Steps of the algorithm:


1. Check if the Queue is empty.
2. Return the value at the front index.
3. Increase front by 1.
4. Set front and rear as -1 for the last element.

Peek

The Peek operation is used to return the front most element of the queue.

Steps of the algorithm:

1. Check if the Queue is empty.


2. Return the value at the front index.

isFull

The isFull operation is used to check if the queue is full or not.

Steps of the algorithm:

1. Check if the number of elements in the queue (size) is equal to the capacity, if
yes, return True.
2. Return False.

isEmpty

The isEmpty operation is used to check if the queue is empty or not.

Steps of the algorithm:

1. Check if the number of elements in the queue (size) is equal to 0, if yes, return
True.
2. Return False.

Program/Source Code
Here is source code of the C Program to implement a queue using array.

1. /*
2. * C Program to Implement a Queue using an Array
3. */
4. #include <stdio.h>
5.
6. #define MAX 50
7.
8. void insert();
9. void delete();
10. void display();
11. int queue_array[MAX];
12. int rear = - 1;
13. int front = - 1;
14. main()
15. {
16. int choice;
17. while (1)
18. {
19. printf("1.Insert element to queue \n");
20. printf("2.Delete element from queue \n");
21. printf("3.Display all elements of queue \n");
22. printf("4.Quit \n");
23. printf("Enter your choice : ");
24. scanf("%d", &choice);
25. switch (choice)
26. {
27. case 1:
28. insert();
29. break;
30. case 2:
31. delete();
32. break;
33. case 3:
34. display();
35. break;
36. case 4:
37. exit(1);
38. default:
39. printf("Wrong choice \n");
40. } /* End of switch */
41. } /* End of while */
42. } /* End of main() */
43.
44. void insert()
45. {
46. int add_item;
47. if (rear == MAX - 1)
48. printf("Queue Overflow \n");
49. else
50. {
51. if (front == - 1)
52. /*If queue is initially empty */
53. front = 0;
54. printf("Inset the element in queue : ");
55. scanf("%d", &add_item);
56. rear = rear + 1;
57. queue_array[rear] = add_item;
58. }
59. } /* End of insert() */
60.
61. void delete()
62. {
63. if (front == - 1 || front > rear)
64. {
65. printf("Queue Underflow \n");
66. return ;
67. }
68. else
69. {
70. printf("Element deleted from queue is : %d\n",
queue_array[front]);
71. front = front + 1;
72. }
73. } /* End of delete() */
74.
75. void display()
76. {
77. int i;
78. if (front == - 1)
79. printf("Queue is empty \n");
80. else
81. {
82. printf("Queue is : \n");
83. for (i = front; i <= rear; i++)
84. printf("%d ", queue_array[i]);
85. printf("\n");
86. }
87. } /* End of display() */

Linked list
A linked list is a linear data structure that includes a series of connected nodes. Here,
each node stores the data and the address of the next node. For example,

You have to start somewhere, so we give the address of the first node a special name
called HEAD. Also, the last node in the linked list can be identified because its next
portion points to NULL.

Linked lists can be of multiple types: singly, doubly, and circular linked list.

Representation of Linked List

Let's see how each node of the linked list is represented. Each node consists:
• A data item

• An address of another node

We wrap both the data item and the next node reference in a struct as:

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

Each struct node has a data item and a pointer to another struct node. A
single linked list of three items can be formed by following code.

// Linked list implementation in C

#include <stdio.h>
#include <stdlib.h>

// Creating a node
struct node {
int value;
struct node *next;
};

// print the linked list value


void printLinkedlist(struct node *p)
{
while (p != NULL) {
printf("%d ", p->value);
p = p->next;
}
}

int main()
{
// Initialize nodes
struct node *head;
struct node *one = NULL;
struct node *two = NULL;
struct node *three = NULL;

// Allocate memory
one = malloc(sizeof(struct node));
two = malloc(sizeof(struct node));
three = malloc(sizeof(struct node));

// Assign value values


one->value = 1;
two->value = 2;
three->value = 3;

// Connect nodes
one->next = two;
two->next = three;
three->next = NULL;

// printing node-value
head = one;
printLinkedlist(head);
}

Basic LinkedList Functions & Operations

Many applications use LinkedList in computer science, let’s discuss basic LinkedList
functions.

• A node can be represented using structures.


• A node takes the form of a user-defined structure, a node contains two parts,i.e. to store
data and to store the reference of the next node
• Basic LinkedList functions
are create(), display(), insert_begin(), insert_end(), insert_pos(), delete_begin(), delete_e
nd(), delete_pos()

create()

• This function is a foundation pillar for the entire linked list.


• Here, we create a temp node to scan the value.
• Then we check if LinkedList is empty or not, if LinkedList is empty then the temp node
would be the head node.
• If LinkedList is not empty, then by using another node, we traverse till the end of
LinkedList and add the temp node at the end of LinkedList.

display()
• This function is used to display the entire LinkedList using a while loop
• We first check, if the head node is pointing to NULL or not, if the head node is
pointing to NULL, then it indicates that LinkedList is empty, so we return
• If LinkedList is not empty, we assign head node to a temp node and we use this
temp node to traverse over the LinkedList using a loop and print them

insert_begin()

• Initially, we create a temp node to scan the value then we check if LinkedList is
empty or not
• If LinkedList is empty, then the newly created node would be treated as a head
node
• If LinkedList is not empty, then we make the temp node point towards the current
head node and the head node to point towards the newly created node

insert_end()

• Firstly, we create a temp node to scan the value then we check if LinkedList is
empty or not
• If LinkedList is empty, then the newly created node would be inserted to
LinkedList
• If LinkedList is not empty, then we create a new node say ptr, by using ptr we
traverse till the end of LinkedList and insert the temp node at the end of
LinkedList

insert_pos()

• Here, we create a temp node to scan the value then we check if LinkedList is
empty or not
• If LinkedList empty, then we return
• If LinkedList is not empty, then we take input of node position from the user, if the
input is greater than the length of LinkedList, then we return
• If the input is in the range of length of LinkedList then, let's assume we have four
nodes A, B, C, D and we need to insert a node next to B, so, we just traverse till
node C and make node B point to node E and node E to point to node C.
delete_begin()

• This function checks if nodes are present in LinkedList or not, if nodes are not
present then we return
• If nodes are present then we make ahead node to point towards the second node
and store the address of the first node in a node say, temp
• By using the address stored in temp, we delete the first node from the memory

delete_end()

• This function checks if nodes are present in LinkedList or not, if nodes are not
present in LinkedList, then we return
• If nodes are present in LinkedList, then we create a temp node and assign a
head node value in it.
• By using this temp node, we traverse till last but one node of the LinkedList, and
then we store the address present in the next field in a node say ptr.
• Now, we delete the ptr from memory, such that the last node is deleted from
LinkedList

delete_pos()

• On invoking this function, we check if nodes are present in LinkedList or not, if


nodes are not present then we return
• If nodes are present in LinkedList, as x,y,z and we need to delete node y
• To delete node y, we traverse till node x and make x to point towards node z,
then we delete node y from memory

Linked List Applications

• Dynamic memory allocation

• Implemented in stack and queue


• In undo functionality of softwares
• Hash tables, Graphs

Representation of a Stack as a Linked List

Stack can also be represented by using a linked list. We know that in the case of arrays we face
a limitation , i.e , array is a data structure of limited size. Hence before using an array to
represent a stack we will have to consider an enough amount of space to suffice the memory
required by the stack.

However, a Linked List is a much more flexible data structure. Both the push and pop
operations can be performed on either ends.. But We prefer performing the Push and
pop operation at the beginning.

The Stack operations occur in constant time. But if we perform the push and pop
operations at the end of the linked list it takes time O(n).

But performing the operations at the beginning occurs in constant time.


Let us understand this with the help of an example.

Let us consider a linked list as shown here.

In the given data we can see that we have-

• Head = 200.
• Top = 33.

Implementation of a Stack using a Linked List


#include <stdio.h>
#include <stdlib.h>

void Push(); /*to insert the element*/

void pop(); /*to delete the element*/

void display(); /*to display the elements in a stack*/

struct n
{
int element;
struct n *nxt;
};
struct n *hd;

void main ()
{
int option=0;
while(option != 4)
{
printf("\nSelect from the following options\n1.Push\n2.Pop\n3.Show\n4.Exit
\nEnter one of your choices:");
scanf("%d",&option);
switch(option)
{
case 1:
{
Push();
break;
}
case 2:
{
pop();
break;
}
case 3:
{
display();
break;
}
case 4:
{
printf("Exit");
break;
}
default:
{
printf("\nKindly enter a valid option\n");
}
};
}
}
void Push ()
{
int element;
struct n *ptr = (struct n*)malloc(sizeof(struct n));
if(ptr == NULL)
{
printf("Stack full. Operation failed");
}
else
{
printf("Enter the element you want to insert: ");
scanf("%d",&element);
if(hd==NULL)
{
ptr->element = element;
ptr -> nxt = NULL;
hd=ptr;
}
else
{
ptr->element = element;
ptr->nxt = hd;
hd=ptr;

}
printf("Element is inserted");
}
}

void pop()
{
int num;
struct n *ptr;
if (hd == NULL)
{
printf("Not enough elements. Underflow!");
}
else
{
num = hd->element;
ptr = hd;
hd = hd->nxt;
free(ptr);
printf("Element deleted: %d",num);

}
}
void display()
{
int i;
struct n *ptr;
ptr=hd;
if(ptr == NULL)
{
printf("The stack is empty\n");
}
else
{
printf("The stack elements are as follows: \n");
while(ptr!=NULL)
{
printf("%d\n",ptr->element);
ptr = ptr->nxt;
}
}
}

Queue using linked list


Queue using an array - drawback
If we implement the queue using an array, we need to specify the array size at the
beginning(at compile time).
We can't change the size of an array at runtime. So, the queue will only work for a fixed
number of elements.
Solution
We can implement the queue data structure using the linked list.
In the linked list, we can change its size at runtime.

Enqueue function
Enqueue function will add the element at the end of the linked list.
Using the rear pointer, we can track the last inserted element.
1. Declare a new node and allocate memory for it.
2. If front == NULL,
make both front and rear points to the new node.
3. Otherwise,
add the new node in rear->next.
make the new node as the rear node. i.e. rear = new node

Visual representation of the above algorithm


Dequeue function
Dequeue function will remove the first element from the queue.
1.Check whether the queue is empty or not
2.If it is the empty queue (front == NULL)
We can't dequeue the element.
3.Otherwise,
Make the front node points to the next node. i.e front = front->next;
if front pointer becomes NULL, set the rear pointer also NULL.
Free the front node's memory.

Implementation of the queue using linked list

/*
* Program : Queue using linked list
* Language : C
*/

#include<stdio.h>
#include<stdlib.h>

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

struct node *front = NULL, *rear = NULL;

void enqueue(int val)


{
struct node *newNode = malloc(sizeof(struct node));
newNode->data = val;
newNode->next = NULL;

//if it is the first node


if(front == NULL && rear == NULL)
//make both front and rear points to the new node
front = rear = newNode;
else
{
//add newnode in rear->next
rear->next = newNode;

//make the new node as the rear node


rear = newNode;
}
}

void dequeue()
{
//used to free the first node after dequeue
struct node *temp;

if(front == NULL)
printf("Queue is Empty. Unable to perform dequeue\n");
else
{
//take backup
temp = front;

//make the front node points to the next node


//logically removing the front element
front = front->next;

//if front == NULL, set rear = NULL


if(front == NULL)
rear = NULL;

//free the first node


free(temp);
}

}
void printList()
{
struct node *temp = front;

while(temp)
{
printf("%d->",temp->data);
temp = temp->next;
}
printf("NULL\n");
}

int main()
{
enqueue(10);
enqueue(20);
enqueue(30);
printf("Queue :");
printList();
dequeue();
printf("After dequeue the new Queue :");
printList();
dequeue();
printf("After dequeue the new Queue :");
printList();

return 0;
}
Doubly Linked List
A doubly linked list is a type of linked list in which each node consists of 3
components:
• *prev - address of the previous node
• data - data item
• *next - address of next node

Representation of Doubly Linked List

Let's see how we can represent a doubly linked list on an algorithm/code.


Suppose we have a doubly linked list:

Here, the single node is represented as

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

Insertion on a Doubly Linked List

We can insert elements at 3 different positions of a doubly-linked list:


1. Insertion at the beginning
2. Insertion in-between nodes
3. Insertion at the End
1. Insertion at the Beginning

Let's add a node with value 6 at the beginning of the doubly linked list we
made above.
1. Create a new node
• allocate memory for newNode
• assign the data to newNode.
2. Set prev and next pointers of new node
• point next of newNode to the first node of the doubly linked list
• point prev to null

3. Make new node as head node


• Point prev of the first node to newNode (now the previous head is the second
node)
• Point head to newNode
2. Insertion in between two nodes

Let's add a node with value 6 after node with value 1 in the doubly linked list.

1. Create a new node


• allocate memory for newNode
• assign the data to newNode.

2. Set the next pointer of new node and previous node


• assign the value of next from previous node to the next of newNode
• assign the address of newNode to the next of previous node

3. Set the prev pointer of new node and the next node
• assign the value of prev of next node to the prev of newNode
• assign the address of newNode to the prev of next node
The final doubly linked list is after this insertion is:

3. Insertion at the End

Let's add a node with value 6 at the end of the doubly linked list.

1. Create a new node


2. Set prev and next pointers of new node and the previous node
If the linked list is empty, make the newNode as the head node. Otherwise,
traverse to the end of the doubly linked list and

The final doubly linked list looks like this.


Deletion from a Doubly Linked List

Similar to insertion, we can also delete a node from 3 different positions of a


doubly linked list.
Suppose we have a double-linked list with elements 1, 2, and 3.

1. Delete the First Node of Doubly Linked List

If the node to be deleted (i.e. del_node) is at the beginning


Reset value node after the del_node (i.e. node two)

Finally, free the memory of del_node . And, the linked will look like this

2. Deletion of the Inner Node


If del_node is an inner node (second node), we must have to reset the value
of next and prev of the nodes before and after the del_node.
For the node before the del_node (i.e. first node)
Assign the value of next of del_node to the next of the first node.
For the node after the del_node (i.e. third node)
Assign the value of prev of del_node to the prev of the third node.

Finally, we will free the memory of del_node . And, the final doubly linked list
looks like this.

3. Delete the Last Node of Doubly Linked List

In this case, we are deleting the last node with value 3 of the doubly linked list.
Here, we can simply delete the del_node and make the next of node
before del_node point to NULL.

he final doubly linked list looks like this.


#include <stdio.h>
#include <stdlib.h>

// node creation
struct Node {
int data;
struct Node* next;
struct Node* prev;
};

// insert node at the front


void insertFront(struct Node** head, int data) {
// allocate memory for newNode
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));

// assign data to newNode


newNode->data = data;

// make newNode as a head


newNode->next = (*head);

// assign null to prev


newNode->prev = NULL;

// previous of head (now head is the second node) is newNode


if ((*head) != NULL)
(*head)->prev = newNode;

// head points to newNode


(*head) = newNode;
}

// insert a node after a specific node


void insertAfter(struct Node* prev_node, int data) {
// check if previous node is null
if (prev_node == NULL) {
printf("previous node cannot be null");
return;
}

// allocate memory for newNode


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

// assign data to newNode


newNode->data = data;

// set next of newNode to next of prev node


newNode->next = prev_node->next;

// set next of prev node to newNode


prev_node->next = newNode;

// set prev of newNode to the previous node


newNode->prev = prev_node;

// set prev of newNode's next to newNode


if (newNode->next != NULL)
newNode->next->prev = newNode;
}

// insert a newNode at the end of the list


void insertEnd(struct Node** head, int data) {
// allocate memory for node
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));

// assign data to newNode


newNode->data = data;

// assign null to next of newNode


newNode->next = NULL;

// store the head node temporarily (for later use)


struct Node* temp = *head;

// if the linked list is empty, make the newNode as head node


if (*head == NULL) {
newNode->prev = NULL;
*head = newNode;
return;
}
// if the linked list is not empty, traverse to the end of the linked list
while (temp->next != NULL)
temp = temp->next;

// now, the last node of the linked list is temp

// assign next of the last node (temp) to newNode


temp->next = newNode;

// assign prev of newNode to temp


newNode->prev = temp;
}

// delete a node from the doubly linked list


void deleteNode(struct Node** head, struct Node* del_node) {
// if head or del is null, deletion is not possible
if (*head == NULL || del_node == NULL)
return;

// if del_node is the head node, point the head pointer to the next of del_node
if (*head == del_node)
*head = del_node->next;

// if del_node is not at the last node, point the prev of node next to del_node
to the previous of del_node
if (del_node->next != NULL)
del_node->next->prev = del_node->prev;

// if del_node is not the first node, point the next of the previous node to
the next node of del_node
if (del_node->prev != NULL)
del_node->prev->next = del_node->next;

// free the memory of del_node


free(del_node);
}

// print the doubly linked list


void displayList(struct Node* node) {
struct Node* last;

while (node != NULL) {


printf("%d->", node->data);
last = node;
node = node->next;
}
if (node == NULL)
printf("NULL\n");
}

int main() {
// initialize an empty node
struct Node* head = NULL;

insertEnd(&head, 5);
insertFront(&head, 1);
insertFront(&head, 6);
insertEnd(&head, 9);

// insert 11 after head


insertAfter(head, 11);

// insert 15 after the seond node


insertAfter(head->next, 15);

displayList(head);

// delete the last node


deleteNode(&head, head->next->next->next->next->next);

displayList(head);
}

Doubly Linked List Applications

1. Redo and undo functionality in software.

2. Forward and backward navigation in browsers.

3. For navigation systems where forward and backward navigation is required.


Singly Linked List Vs Doubly Linked List

Singly Linked List Doubly Linked List

Each node consists of a data value and a Each node consists of a data value, a pointer to the next node,
pointer to the next node. and a pointer to the previous node.

Traversal can occur in one way only (forward


Traversal can occur in both ways.
direction).

It requires less space. It requires more space because of an extra pointer.

It has multiple usages. It can be implemented on the stack, hea


It can be implemented on the stack.
and binary tree.

Circular Linked List


A circular linked list is a type of linked list in which the first and the last nodes
are also connected to each other to form a circle.
There are basically two types of circular linked list:

1. Circular Singly Linked List


Here, the address of the last node consists of the address of the first node.

2. Circular Doubly Linked List


Here, in addition to the last node storing the address of the first node, the first
node will also store the address of the last node.

Representation of Circular Linked List

Insertion on a Circular Linked List

We can insert elements at 3 different positions of a circular linked list:

1. Insertion at the beginning


2. Insertion in-between nodes
3. Insertion at the end
1. Insertion at the Beginning

• store the address of the current first node in the newNode (i.e. pointing
the newNode to the current first node)
• point the last node to newNode (i.e making newNode as head)

2. Insertion in between two nodes


Let's insert newNode after the first node.

• travel to the node given (let this node be p)


• point the next of newNode to the node next to p

• store the address of newNode at next of p

3. Insertion at the end

• store the address of the head node to next of newNode (making newNode the
last node)
• point the current last node to newNode
• make newNode as the last node

Deletion on a Circular Linked List

Suppose we have a double-linked list with elements 1, 2, and 3.


1. If the node to be deleted is the only node

• free the memory occupied by the node

• store NULL in last


2. If last node is to be deleted

• find the node before the last node (let it be temp)

• store the address of the node next to the last node in temp
• free the memory of last

• make temp as the last node

3. If any other nodes are to be deleted

• travel to the node to be deleted (here we are deleting node 2)

• let the node before node 2 be temp


• store the address of the node next to 2 in temp
• free the memory of 2
Circular Linked List Code
// C code to perform circular linked list operations

#include <stdio.h>
#include <stdlib.h>

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

struct Node* addToEmpty(struct Node* last, int data) {


if (last != NULL) return last;

// allocate memory to the new node


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

// assign data to the new node


newNode->data = data;

// assign last to newNode


last = newNode;

// create link to iteself


last->next = last;

return last;
}

// add node to the front


struct Node* addFront(struct Node* last, int data) {
// check if the list is empty
if (last == NULL) return addToEmpty(last, data);

// allocate memory to the new node


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

// add data to the node


newNode->data = data;

// store the address of the current first node in the newNode


newNode->next = last->next;

// make newNode as head


last->next = newNode;

return last;
}

// add node to the end


struct Node* addEnd(struct Node* last, int data) {
// check if the node is empty
if (last == NULL) return addToEmpty(last, data);

// allocate memory to the new node


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

// add data to the node


newNode->data = data;

// store the address of the head node to next of newNode


newNode->next = last->next;

// point the current last node to the newNode


last->next = newNode;

// make newNode as the last node


last = newNode;

return last;
}

// insert node after a specific node


struct Node* addAfter(struct Node* last, int data, int item) {
// check if the list is empty
if (last == NULL) return NULL;

struct Node *newNode, *p;

p = last->next;
do {
// if the item is found, place newNode after it
if (p->data == item) {
// allocate memory to the new node
newNode = (struct Node*)malloc(sizeof(struct Node));

// add data to the node


newNode->data = data;

// make the next of the current node as the next of newNode


newNode->next = p->next;

// put newNode to the next of p


p->next = newNode;

// if p is the last node, make newNode as the last node


if (p == last) last = newNode;
return last;
}

p = p->next;
} while (p != last->next);

printf("\nThe given node is not present in the list");


return last;
}

// delete a node
void deleteNode(struct Node** last, int key) {
// if linked list is empty
if (*last == NULL) return;

// if the list contains only a single node


if ((*last)->data == key && (*last)->next == *last) {
free(*last);
*last = NULL;
return;
}

struct Node *temp = *last, *d;

// if last is to be deleted
if ((*last)->data == key) {
// find the node before the last node
while (temp->next != *last) temp = temp->next;

// point temp node to the next of last i.e. first node


temp->next = (*last)->next;
free(*last);
*last = temp->next;
}

// travel to the node to be deleted


while (temp->next != *last && temp->next->data != key) {
temp = temp->next;
}

// if node to be deleted was found


if (temp->next->data == key) {
d = temp->next;
temp->next = d->next;
free(d);
}
}

void traverse(struct Node* last) {


struct Node* p;

if (last == NULL) {
printf("The list is empty");
return;
}

p = last->next;

do {
printf("%d ", p->data);
p = p->next;

} while (p != last->next);
}

int main() {
struct Node* last = NULL;

last = addToEmpty(last, 6);


last = addEnd(last, 8);
last = addFront(last, 2);

last = addAfter(last, 10, 2);


traverse(last);

deleteNode(&last, 8);

printf("\n");

traverse(last);

return 0;
}

Polynomial
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
Polynomial can be represented in the various ways. These are:

• By the use of arrays


• By the use of Linked List

Representation of Polynomials 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

A polynomial can be thought of as an ordered list of non zero terms. Each non zero
term is a two-tuple which holds two pieces of information:

• The exponent part


• The coefficient part

Adding two polynomials using Linked List

#include<stdio.h>
#include<malloc.h>
#include<conio.h>
struct link{
int coeff;
int pow;
struct link *next;
};
struct link *poly1=NULL,*poly2=NULL,*poly=NULL;
void create(struct link *node)
{
char ch;
do
{
printf("\n enter coeff:");
scanf("%d",&node->coeff);
printf("\n enter power:");
scanf("%d",&node->pow);
node->next=(struct link*)malloc(sizeof(struct link));
node=node->next;
node->next=NULL;
printf("\n continue(y/n):");
ch=getch();
}
while(ch=='y' || ch=='Y');
}
void show(struct link *node)
{
while(node->next!=NULL)
{
printf("%dx^%d",node->coeff,node->pow);
node=node->next;
if(node->next!=NULL)
printf("+");
}
}
void polyadd(struct link *poly1,struct link *poly2,struct link *poly)
{
while(poly1->next && poly2->next)
{
if(poly1->pow>poly2->pow)
{
poly->pow=poly1->pow;
poly->coeff=poly1->coeff;
poly1=poly1->next;
}
else if(poly1->pow<poly2->pow)
{
poly->pow=poly2->pow;
poly->coeff=poly2->coeff;
poly2=poly2->next;
}
else
{
poly->pow=poly1->pow;
poly->coeff=poly1->coeff+poly2->coeff;
poly1=poly1->next;
poly2=poly2->next;
}
poly->next=(struct link *)malloc(sizeof(struct link));
poly=poly->next;
poly->next=NULL;
}
while(poly1->next || poly2->next)
{
if(poly1->next)
{
poly->pow=poly1->pow;
poly->coeff=poly1->coeff;
poly1=poly1->next;
}
if(poly2->next)
{
poly->pow=poly2->pow;
poly->coeff=poly2->coeff;
poly2=poly2->next;
}
poly->next=(struct link *)malloc(sizeof(struct link));
poly=poly->next;
poly->next=NULL;
}
}
main()
{
char ch;
do{
poly1=(struct link *)malloc(sizeof(struct link));
poly2=(struct link *)malloc(sizeof(struct link));
poly=(struct link *)malloc(sizeof(struct link));
printf("\nenter 1st number:");
create(poly1);
printf("\nenter 2nd number:");
create(poly2);
printf("\n1st Number:");
show(poly1);
printf("\n2nd Number:");
show(poly2);
polyadd(poly1,poly2,poly);
printf("\nAdded polynomial:");
show(poly);
printf("\n add two more numbers:");
ch=getch();
}
while(ch=='y' || ch=='Y');
}

You might also like