Assignment 1 Dsa
Assignment 1 Dsa
Assignment 1 Dsa
ANSWER 1:
A data structure is a specific way of organizing and storing data in a computer so it
can be accessed and modified efficiently. It provides a means to manage and process data, facilitating
operations like data retrieval, insertion, deletion, and manipulation.
Linear Data Structures: These store elements sequentially and are easy to traverse since elements are
arranged in a specific order. They are generally simple to implement but may have limitations in terms of
efficiency when scaling.
1. Array: A collection of elements, identified by index or key, stored sequentially in memory. All elements
are of the same data type. Arrays allow constant-time access to elements but have fixed size and may require
significant memory if large.
2. Linked List: A collection of nodes where each node contains a data part and a reference (or link) to the
next node in the sequence. Linked lists allow for efficient insertions and deletions but have a slower access
time than arrays due to the need to traverse nodes.
Singly Linked List: Each node points to the next node.
Doubly Linked List: Each node points to both the next and the previous node.
Circular Linked List: The last node points back to the first, forming a loop.
Stack: A collection of elements that follows the Last In, First Out (LIFO) principle. Operations are
performed at one end, called the "top" of the stack. Common operations include push (insert) and pop
(remove). Stacks are used for functions like backtracking, reversing, and recursion management.
Queue: A collection of elements that follows the First In, First Out (FIFO) principle. Elements are added at
the back (enqueue) and removed from the front (dequeue). Queues are used in scheduling processes and
managing tasks in order.
1. Simple Queue: Basic FIFO structure.
2. Circular Queue: The end of the queue wraps around to connect with the start, optimizing
space.
3. Priority Queue: Elements are dequeued based on priority rather than order of arrival.
4. Dequeue: A double-ended queue where insertions and deletions can be made from both ends.
A data structure is a specific way of organizing and storing data in a computer so it can be accessed and
modified efficiently. It provides a means to manage and process data, facilitating operations like data
retrieval, insertion, deletion, and manipulation.
1. Linear Data Structures: These store elements sequentially and are easy to traverse since elements are
arranged in a specific order. They are generally simple to implement but may have limitations in terms
of efficiency when scaling.
2. Non-Linear Data Structures: These store elements in a hierarchical or interconnected manner,
allowing for more complex relationships among data. Non-linear structures are efficient for certain
tasks, such as representing hierarchical data or graphs, but can be more complex to implement.
Linear Data Structures
1. Array: A collection of elements, identified by index or key, stored sequentially in memory. All
elements are of the same data type. Arrays allow constant-time access to elements but have fixed
size and may require significant memory if large.
2. Linked List: A collection of nodes where each node contains a data part and a reference (or link) to
the next node in the sequence. Linked lists allow for efficient insertions and deletions but have a
slower access time than arrays due to the need to traverse nodes.
o Singly Linked List: Each node points to the next node.
o Doubly Linked List: Each node points to both the next and the previous node.
o Circular Linked List: The last node points back to the first, forming a loop.
3. Stack: A collection of elements that follows the Last In, First Out (LIFO) principle. Operations are
performed at one end, called the "top" of the stack. Common operations include push (insert) and pop
(remove). Stacks are used for functions like backtracking, reversing, and recursion management.
4. Queue: A collection of elements that follows the First In, First Out (FIFO) principle. Elements are
added at the back (enqueue) and removed from the front (dequeue). Queues are used in scheduling
processes and managing tasks in order.
o Simple Queue: Basic FIFO structure.
o Circular Queue: The end of the queue wraps around to connect with the start, optimizing
space.
o Priority Queue: Elements are dequeued based on priority rather than order of arrival.
o Deque: A double-ended queue where insertions and deletions can be made from both ends.
1. Tree: A hierarchical structure consisting of nodes. Each node contains a data value and links to child
nodes. The topmost node is the "root," and nodes with no children are "leaves." Trees are efficient for
hierarchical data storage, such as file systems and databases.
o Binary Tree: Each node has at most two children (left and right).
o Binary Search Tree (BST): A binary tree with sorted nodes; left child values are smaller, and
right child values are larger.
o AVL Tree: A self-balancing binary search tree that maintains balance through rotations to
ensure efficient search and update operations.
o B-Tree: A balanced tree structure optimized for systems that read and write large blocks of
data.
2. Graph: A structure consisting of nodes (vertices) connected by edges. Graphs represent complex
relationships among data points and can be directed or undirected.
o Undirected Graph: Edges have no direction (mutual connection).
o Directed Graph (Digraph): Edges have direction (one-way connection).
o Weighted Graph: Edges have weights (values) representing cost, distance, or other
factors.
o Unweighted Graph: Edges have no weight.
3. Heap: A specialized tree-based structure that follows the heap property. It is commonly used to
implement priority queues.
o Max-Heap: The value of each parent node is greater than or equal to the values of its children.
o Min-Heap: The value of each parent node is less than or equal to the values of its children.
ANSWER 2:
An Abstract Data Type (ADT) is a theoretical model for data structures that defines a set of data
and the operations that can be performed on it without specifying the actual implementation details. An ADT
focuses on what operations can be done (the interface) rather than how they are done (the implementation). This
abstraction allows programmers to use data structures in a more general way, focusing on functionality without
worrying about underlying complexities.
CHARACTERISTICS:
o Elements are arranged in a sequential order.
o Each element has a single predecessor and successor, except the first and last.
ANSWER 3:
Linear data structures support a variety of basic operations that are essential for manipulating and managing data.
Here are the primary operations commonly performed with linear structures like arrays, linked lists, stacks, and
queues:
1. Traversal
Traversal is the process of accessing each element in the linear structure sequentially.
This operation is essential for displaying, processing, or analyzing each element.
For example, in an array or linked list, traversal involves moving from the first to the last element to
process each item.
2. Insertion
3. Deletion
4. Searching
Searching involves finding the location of a particular element within the structure.
Searching methods vary based on the data structure:
o Array and Linked List: A linear search (O(n)) is used to locate an element by checking
each item.
o Stack and Queue: Typically, only the element at the top (stack) or front (queue) is accessible,
so searching isn’t commonly used for these structures.
5. Updating
Accessing retrieves the value of an element at a particular index or position in the structure.
Array: Direct access is possible with indexing (O(1) time complexity).
Linked List: Accessing a specific node requires traversal from the start to the desired position,
making it an O(n) operation.
ANSWER 4:
EXAMPLE:
2. CALLOC(Contiguous Allocation)
EXAMPLE :
int* ptr = (int*)calloc(10, sizeof(int)); // Allocates and initializes memory for an array of 10 integers to 0.
3. REALLOC (Reallocation)
EXAMPLE:
4. FREE (Deallocation)
#include <stdio.h>
#include <stdlib.h> int
main() {
// Allocate memory for an array of 5 integers using malloc int* arr
= (int*)malloc(5 * sizeof(int));
for(int i = 0; i < 5; i++) {
arr[i] = i + 1; printf("%d ",
arr[i]);
}
// Resize the array to hold 10 integers using realloc arr =
(int*)realloc(arr, 10 * sizeof(int));
ANSWER 5:
Linear Data Structures organize data in a sequential, ordered manner, where elements are arranged one after
another. This sequential setup means each element typically has a unique predecessor and successor, except for
the first and last elements in the structure. Common examples of linear data structures include arrays, linked lists,
stacks, and queues.
These structures are generally straightforward to implement and use, making them suitable for simpler tasks
like managing lists of items or processing tasks in a sequence. For instance, stacks and queues manage data in
an orderly way, following Last In, First Out (LIFO) and First In, First Out (FIFO) principles, respectively.
Linear data structures often allow direct access to elements, such as accessing elements by index in arrays.
However, they may require shifting elements for insertions and deletions, which can impact performance,
especially for large datasets.
Non-linear structures are ideal for modeling complex data like hierarchical relationships (such as file
directories) or interconnected data (like social networks). They tend to be more complex to implement due to
the varied relationships among elements, and traversal methods can be intricate, requiring specific techniques
(e.g., preorder, inorder, and postorder for trees).
ANSWER 6:
The time complexity and space complexity of an algorithm are metrics used to evaluate its efficiency and
resource usage. They help in understanding the algorithm's performance, especially as the input size
grows.
Time Complexity
Definition: Time complexity is a measure of the amount of time an algorithm takes to run as a
function of the input size, typically denoted as nnn.
Purpose: It gives an idea of how the execution time increases with the input size, helping to
predict the algorithm’s behavior on large datasets.
Notations: Big O notation (e.g., O(n)O(n)O(n), O(n2)O(n^2)O(n2), O(logn)O(\log n)O(logn)) is
used to express the upper bounds of time complexity, representing the worst-case scenario of time
growth.
Example: A linear search algorithm has a time complexity of O(n)O(n)O(n) because it may need to
check each element in a list of nnn elements in the worst case.
Space Complexity
ANSWER 7:
Best case, average case, and worst case time analyses are used to describe an algorithm's performance
under different conditions of input data. Each provides insights into how efficiently an algorithm will run
depending on the scenario. Let’s go through each case with examples:
1. Best Case
Definition: The best case time complexity of an algorithm is the minimum time it takes to complete,
given the most favorable input.
Purpose: This scenario is typically used to understand the fastest performance that an algorithm can
achieve.
Example: Consider linear search in an unsorted array.
o Best Case: The element to be found is the very first element in the array.
o Time Complexity: O(1)O(1)O(1), as the algorithm only needs one comparison to find the
target element.
2. Average Case
Definition: The average case time complexity represents the expected time it takes for an algorithm to
complete, assuming random input distribution.
Purpose: This case gives a realistic measure of performance across a range of inputs, assuming no
particular bias.
Example: Again, consider linear search in an unsorted array.
o Average Case: The element is expected to be somewhere in the middle of the array on
average.
o Time Complexity: O(n/2)O(n/2)O(n/2), which simplifies to O(n)O(n)O(n), as it might
take approximately half of the array's length to find the element.
3. Worst Case
Definition: The worst case time complexity is the maximum time an algorithm takes to complete,
given the most challenging input.
Purpose: This case helps to identify performance limits and ensure the algorithm won’t exceed certain
time constraints, even in the most difficult scenarios.
Example: Consider linear search again.
o Worst Case: The element is located at the last position or is not in the array at all.
o Time Complexity: O(n)O(n)O(n), as the algorithm must examine each element in the array to
confirm the element's absence or to find it at the end.
Best Case: If the array is already sorted, Bubble Sort will only make one pass without needing to
swap elements.
o Time Complexity: O(n)O(n)O(n).
Average Case: When the elements are in random order, Bubble Sort has to go through multiple
passes and perform several swaps.
o Time Complexity: O(n2)O(n^2)O(n2).
Worst Case: If the array is in reverse order, Bubble Sort must swap every pair on each pass, making
it go through the maximum number of comparisons and swaps.
o Time Complexity: O(n2)O(n^2)O(n2).
ANSWER 8:
Performance analysis is the process of evaluating an algorithm to determine its efficiency and effectiveness in
solving a specific problem. This analysis involves studying various factors such as time complexity, space
complexity, and resource utilization under different conditions of input data. The goal is to understand how
well the algorithm performs, especially as the size of the input data grows.
1. Time Complexity: This measures the amount of time an algorithm takes to complete as a function of
the input size. Different cases (best, average, worst) are often analyzed to provide a comprehensive view
of expected performance.
2. Space Complexity: This assesses the amount of memory space required by the algorithm in relation to
the input size. Like time complexity, it can also be evaluated under different conditions.
3. Scalability: Analyzing how the algorithm's performance changes with increasing input sizes is
crucial for determining its applicability in real-world scenarios.
4. Resource Usage: Besides time and space, performance analysis can include evaluating the
algorithm's use of other resources, such as CPU cycles and network bandwidth, depending on the
context.
1. Benchmarking: Implementing the algorithm and comparing its performance against established
benchmarks or other algorithms to assess its relative efficiency.
2. Execution Time: Measuring the actual time taken by the algorithm to complete tasks, often using
built-in timing functions or profiling tools.
3. Memory Usage: Monitoring how much memory the algorithm consumes during execution,
which can be measured through profiling tools or manual tracking.
4. Test Cases: Running the algorithm with various input sizes and types to gather data on its
performance across different scenarios. This helps in identifying edge cases and understanding
average behavior.
5. Statistical Analysis: Collecting data from multiple runs to perform statistical analysis, which helps
to account for variations in performance due to external factors like hardware differences and system
load.
ANSWER 9:
#include <iostream>
#include <vector>
#include <chrono> using
namespace std;
using namespace std::chrono;
return sum;
int main() {
// Example vector
cout << "Sum of vector values: " << result << endl;
cout << "Execution time: " << duration.count() << " microseconds" << endl;
return 0;
}
Answer:
Sparse Matrix:
A sparse matrix is a matrix in which most of the elements are zero (or a predefined "empty" value). This is
in contrast to a dense matrix, where most of the elements are non-zero. Sparse matrices arise frequently in
scientific computing, image processing, and other areas where the data is large but sparse.
Using Array:
In an array-based representation of a sparse matrix, only the non-zero elements are stored along with their
row and column indices. This reduces memory usage significantly. There are a few common ways to store
sparse matrices:
Triplet Format (COO - Coordinate List): The sparse matrix is stored as a list of non-zero values,
along with their row and column indices.
A = [0, 0, 0, 0]
[5, 0, 0, 0]
[0, 8, 0, 0]
[0, 0, 0, 4]
In this format, the matrix is represented as an array of tuples or a 3-column array (row, column, value).
Compressed Sparse Row (CSR) and Compressed Sparse Column (CSC) are other formats that
store row/column indices separately, further reducing memory.
In the linked list representation of a sparse matrix, each non-zero element is stored in a node that contains:
The row index.
The column index.
The value of the non-zero element.
A pointer to the next element in the list.
A linked list can be implemented in a row-wise or column-wise manner. It helps in saving memory,
especially when the number of non-zero elements is significantly less than the total number of elements.
Ques 2: Find the Address of Element A1(4, 12) in a Two-Dimensional Array Stored in Row-Major
Order
Answer:
Given:
For a two-dimensional array A[i][j]A[i][j]A[i][j] with base address BBB, the address of element A[i,j]A[i,
j]A[i,j] is given by:
Here:
Number of rows = 8 - 1 + 1 = 8
Number of columns = 14 - 7 + 1 = 8
i=4,j=12i = 4, j = 12i=4,j=12
Address(4,12)=100+4×((4−1)×8+(12−7))=100+4×(24+5)=100+4×29=100+116=216
Ques 3: Find the Address of Element Z1(4, 12) in a Two-Dimensional Array Stored in Column-Major
Order
Answer:
Given:
For a two-dimensional array Z[i][j]Z[i][j]Z[i][j] with base address BBB, the address of element Z[i,j]Z[i,
j]Z[i,j] is given by:
Here:
Number of rows = 9 - 2 + 1 = 8
Number of columns = 18 - 9 + 1 = 10
i=4,j=12i = 4, j = 12i=4,j=12
Address(4,12)=100+4×((12−1)×8+(4−1))=100+4×(11×8+3)=100+4×(88+3)=100+4×91=100+364=464
Stack
Ques 1: What is a Stack? Explain Basic Primitive Operations of Stack with Examples
Answer:
A stack is a linear data structure that follows the Last In, First Out (LIFO) principle. This means that the
last element inserted into the stack is the first to be removed.
Example:
Ques 2: Write a C Program to Implement a Stack with Overflow and Underflow Checks Using Array
Answer:
#include <stdio.h>
#define MAX 5
int stack[MAX];
int top = -1;
int isFull() {
return top == MAX - 1;
}
int isEmpty() {
return top == -1;
}
int pop() {
if (isEmpty()) {
printf("Stack Underflow\n");
return -1;
} else {
return stack[top--];
}
}
int peek() {
if (isEmpty()) {
printf("Stack is empty\n");
return -1;
} else {
return stack[top];
}
}
int main() {
push(10);
push(20);
push(30);
printf("Top element is %d\n", peek());
printf("%d popped from stack\n", pop());
printf("Top element is %d\n", peek());
return 0;
}
Answer:
Algorithm:
Algorithm Steps:
Ques 4: What is the Advantage of Polish Expression Over Infix Notation? Write an Algorithm to
Convert Infix to Reverse Polish Notation (Postfix)
Answer:
No Parentheses Needed: Polish notation (both prefix and postfix) eliminates the need for
parentheses to enforce operator precedence.
Faster Evaluation: It allows for faster computation because the order of operations is explicitly
defined.
1. Initialize an empty stack for operators and an empty list for the result.
2. For each character in the infix expression:
o If it's an operand, add it to the result list.
o If it's an operator, pop operators from the stack to the result list until the operator at the top of
the stack has lower precedence, then push the current operator onto the stack.
o If it's a parenthesis, handle it by pushing it onto the stack or popping operators until a left
parenthesis is encountered.
3. Pop any remaining operators from the stack to the result list.
Answer:
To convert an infix expression to postfix, we use a stack-based algorithm that respects operator precedence
and parentheses.
Operator Precedence:
1. Initialize an empty stack for operators and an empty list for the result.
2. For each token (operand or operator) in the infix expression:
o If the token is an operand (variable or number), append it to the result list.
o If the token is an operator, pop operators from the stack to the result list while they have
greater or equal precedence than the current operator. Then, push the current operator onto
the stack.
o If the token is a left parenthesis (, push it onto the stack.
o If the token is a right parenthesis ), pop operators from the stack to the result list until a left
parenthesis ( is encountered.
3. After reading the entire expression, pop any remaining operators from the stack to the result list.
1. Infix Expression: A + ( (B – C) * (D – E) + F) / G ) $ (H – J)
Postfix Expression: A B C – D E – * F + G / H J – $ +
Steps:
2. Infix Expression: (A + B) * (C – D) $ E * F
Postfix Expression: A B + C D – * E F * $
Steps:
1. Encounter ( → Push to stack
2. Encounter A → Append to result: A
3. Encounter + → Push + to stack
4. Encounter B → Append to result: A B
5. Pop + from stack → Append to result: A B +
6. Pop ( from stack → Discard
7. Encounter * → Push * to stack
8. Encounter ( → Push to stack
9. Encounter C → Append to result: A B + C
10. Encounter – → Push – to stack
11. Encounter D → Append to result: A B + C D
12. Pop – from stack → Append to result: A B + C D –
13. Pop * from stack → Append to result: A B + C D – *
14. Encounter $ → Push $ to stack
15. Encounter E → Append to result: A B + C D – * E
16. Encounter * → Push * to stack
17. Encounter F → Append to result: A B + C D – * E F
18. Pop * from stack → Append to result: A B + C D – * E F *
19. Pop $ from stack → Append to result: A B + C D – * E F * $
3. Infix Expression: (A + B) * (C ^ (D – E) + F) – G
Postfix Expression: A B + C D E – ^ F + * G –
Steps:
4. Infix Expression: A + B * C
Postfix Expression: A B C * +
Steps:
5. Infix Expression: A + B * C ^ D – E
Postfix Expression: A B C D ^ * + E –
Steps:
Postfix Expression: A B C + + D E F * + G / +
Steps:
7. Infix Expression: (A + B) * C / D + E ^ F / G
Postfix Expression: A B + C * D / E F ^ G / +
Steps:
8. Infix Expression: (A + B) * C / D
Postfix Expression: A B + C * D /
Steps:
Postfix Expression: A B + C D / – E /
Steps:
Postfix Expression: A B C D E ^ / – / F +
Steps:
Postfix Expression: A B C D E ^ * / –
Steps:
Let's evaluate the two postfix expressions, step by step, assuming that:
A=1
B=2
C=3
1. Postfix Expression: A B + C – B A + C - +
Final Result: 0
2. Postfix Expression: A B C + * C B A - + *
Push A (1): [1]
Push B (2): [1, 2]
Push C (3): [1, 2, 3]
Encounter +: Pop 3 and 2 → 2 + 3 = 5; Stack: [1, 5]
Encounter *: Pop 5 and 1 → 1 * 5 = 5; Stack: [5]
Push C (3): [5, 3]
Push B (2): [5, 3, 2]
Push A (1): [5, 3, 2, 1]
Encounter –: Pop 1 and 2 → 2 - 1 = 1; Stack: [5, 3, 1]
Encounter +: Pop 1 and 3 → 3 + 1 = 4; Stack: [5, 4]
Encounter *: Pop 4 and 5 → 5 * 4 = 20; Stack: [20]
Final Result: 20
Summary:
1. A B + C – B A + C - + evaluates to 0.
2. A B C + * C B A - + * evaluates to 20.
Answer:
Merge Sort: A sorting algorithm that divides the data into two halves, recursively sorts each half,
and then merges the sorted halves back together.
Quick Sort: A sorting algorithm that selects a pivot element, partitions the data around the pivot, and
recursively sorts the subarrays.
Answer:
1. Choose Pivot: Choose a pivot element from the array. For simplicity, let's choose the last element as
the pivot.
o Initial Array: [42, 29, 74, 11, 65, 58]
o Pivot: 58
2. Partitioning: Rearrange the elements such that elements smaller than the pivot go to the left, and
elements greater than the pivot go to the right. After partitioning:
o [42, 29, 11, 58, 65, 74]
3. Recursively Apply Quick Sort on the subarrays to the left and right of the pivot:
o Left Subarray: [42, 29, 11]
Pivot: 11
After partitioning: [11, 29, 42]
o Right Subarray: [65, 74]
Pivot: 74
After partitioning: [65, 74]
4. Final Sorted Array: [11, 29, 42, 58, 65, 74]
Ques 3: Write a C Program for Insertion Sort and Discuss Its Efficiency
Answer:
#include <stdio.h>
// Move elements of arr[0..i-1], that are greater than key, to one position ahead
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
int main() {
int arr[] = {42, 29, 74, 11, 65, 58};
int n = sizeof(arr) / sizeof(arr[0]);
insertionSort(arr, n);
return 0;
}
#include <stdio.h>
int main() {
int arr[] = {42, 29, 74, 11, 65, 58};
int n = sizeof(arr) / sizeof(arr[0]);
selectionSort(arr, n);
return 0;
}
#include <stdio.h>
int main() {
int arr[] = {42, 29, 74, 11, 65, 58};
int n = sizeof(arr) / sizeof(arr[0]);
bubbleSort(arr, n);
return 0;
}
int main() {
int arr[] = {42, 29, 74, 11, 65, 58};
int n = sizeof(arr) / sizeof(arr[0]);
quickSort(arr, 0, n-1);
return 0;
}
#include <stdio.h>
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (leftArr[i] <= rightArr[j]) {
arr[k] = leftArr[i];
i++;
} else {
arr[k] = rightArr[j];
j++;
}
k++;
}
int main() {
int arr[] = {42, 29, 74, 11, 65, 58};
int n = sizeof(arr) / sizeof(arr[0]);
mergeSort(arr, 0, n-1);
return 0;
}
Ques 8: Using Quick Sort, Give Tracing for the Following Set of Numbers
Answer:
Tracing Steps:
1. First Pass:
Pivot: 99, Partitioning: [42, 11, 23, 58, 65, 72, 36, 99]
2. Second Pass (Left subarray, Pivot = 72):
Partitioning: [42, 11, 23, 58, 65, 36, 72, 99]
3. Third Pass (Left subarray, Pivot = 36):
Partitioning: [11, 23, 36, 58, 65, 42]
4. Continue recursively to partition and sort.
The final sorted array: [11, 23, 36, 42, 58, 65, 72, 99]
Searching
Ques 1: Precondition and Algorithm of Binary Search Method
Answer:
Precondition:
1. Initialize two variables, low and high, to the start and end indices of the array.
2. While low <= high:
o Calculate mid = (low + high) / 2.
o If the element at mid is equal to the target, return mid.
o If the element at mid is less than the target, set low = mid + 1.
o If the element at mid is greater than the target, set high = mid - 1.
3. If the target is not found, return -1.
Answer:
Ques 2: Write an algorithm to implement ascending priority queue using a singly linked list.
Answer:
An ascending priority queue means elements are inserted in such a way that the list is always ordered from
lowest to highest priority (or value).
insert():
remove():
newNode->next = current->next;
current->next = newNode;
}
Answer:
Ques 4: Write a program to insert and delete an element after a given node in a singly linked list.
Answer:
1. Find the node after which the new node will be inserted.
2. Create a new node with the given data.
3. Adjust the pointers to insert the new node after the given node.
Answer:
To represent a polynomial, each node in the doubly linked list will store the coefficient and the exponent of
a term.
Steps:
Create nodes for each term in the polynomial.
Add polynomials by comparing the exponents and adding the coefficients of terms with the same
exponent.
struct PolyNode {
int coef;
int exp;
struct PolyNode *next;
struct PolyNode *prev;
};
temp->next = NULL;
temp->prev = tail;
if (tail != NULL) tail->next = temp;
tail = temp;
return result;
}
Answer:
temp->next = list2;
if (list2 != NULL) list2->prev = temp;
}
Ques 3: Explain the advantages of doubly linked list over singly linked list.
Answer:
Advantages:
1. Bidirectional Traversal: Doubly linked lists can be traversed in both forward and backward
directions, unlike singly linked lists, which can only be traversed in one direction.
2. Efficient Deletion: Insertion and deletion at both ends are more efficient. In a singly linked list,
deleting a node requires knowledge of the previous node, while in a doubly linked list, the previous
pointer is already available.
3. More Flexible: Doubly linked lists allow more complex operations such as reversing a list or
inserting/deleting nodes at any position with fewer pointer manipulations.
Ques 4: Write function delete(p, &x) which deletes the node pointed by p in a doubly linked list.
Answer:
if (p->prev != NULL) {
p->prev->next = p->next;
}
if (p->next != NULL) {
p->next->prev = p->prev;
}
Answer:
Efficient Traversal: Can be traversed in a circular manner, allowing easy access to the first node
from the last node without needing a special reference.
Simplified Operations: Insertion and deletion at both ends become more straightforward as the list
is circular and does not require special handling for the first and last elements.
Bidirectional Traversal: Can traverse in both directions, unlike singly linked lists.
Efficient Deletion: Insertion and deletion operations are more efficient as there is direct access to the
previous node.
Ques 2: Write an algorithm to perform operations on Circular Singly Linked List using a header
node:
Answer:
1. Traverse to the last node (where the next pointer points to the header).
2. Insert the new node after the last node and make it point to the header node.
1. Create a new node and make it point to the node that the header node points to.
2. Update the header node to point to the new node.
newNode->next = header;
temp->next = newNode;
}
newNode->next = header->next;
header->next = newNode;
}