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

Assignment No 3

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

Assignment No 3

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

Assignment No: 3 - Linked List

Question 1: Differentiate between Arrays and Linked Lists

Arrays and Linked Lists are fundamental data structures in computer science, and they differ in
their structure, memory management, efficiency, and operations.

1. Structure:

 Array: Arrays are collections of elements stored in contiguous memory locations,


meaning each element is positioned next to the previous one. This structure allows for
quick access to any element using its index, providing an O(1) time complexity for
accessing an element.
 Linked List: Linked Lists are collections of nodes where each node contains data and a
pointer to the next node in the sequence. In a singly linked list, each node only points to
the next node, creating a chain of nodes. This structure makes linked lists dynamic and
more flexible than arrays but requires sequential access, which is slower than array
indexing.

2. Memory Allocation:

 Array: Arrays have a fixed size that must be defined at the time of their creation, and
memory is allocated in contiguous blocks. This can be a disadvantage if the size of the
array needs to change, as resizing is often costly in terms of time and memory.
 Linked List: Linked Lists use dynamic memory allocation. Each node is allocated
separately, and nodes are linked together using pointers. This means the list can grow or
shrink easily by adding or removing nodes without a need to reallocate a contiguous
memory block.

3. Flexibility and Resizing:

 Array: Fixed in size; if you need more space, a new array must be created with
additional space, and the existing elements must be copied over. This can be inefficient in
cases of frequent resizing.
 Linked List: Supports dynamic resizing, allowing easy addition and removal of nodes
without worrying about a fixed size. Thus, linked lists are useful when the number of
elements is unknown at the outset or may vary significantly.

4. Insertion and Deletion:

 Array: Insertion and deletion are costly operations (O(n) in the worst case) because all
elements following the inserted/deleted element must be shifted to maintain contiguous
memory.
 Linked List: Inserting and deleting nodes in a linked list, especially at the beginning or
middle, is more efficient, as only the pointers need to be updated. However, accessing a
specific element by position requires traversing from the head, which is O(n).

5. Example Use Cases:

 Array: Suitable for applications where fast element access is essential, and the size of the
collection is known in advance (e.g., look-up tables, databases).
 Linked List: Used in scenarios requiring frequent insertions and deletions (e.g.,
implementation of stacks, queues, and real-time applications).

 .

Question 2: Write an Algorithm to Implement Ascending Priority Queue


Using Singly Linked List

An ascending priority queue is a data structure where elements are ordered based on priority,
with the lowest priority (smallest value) at the front. In this example, we will use a singly linked
list to implement this priority queue.

Algorithm:

1. Insertion (insert()):
o Step 1: Create a new node with the given priority and data.
o Step 2: If the list is empty or if the priority of the new node is less than the head
node, set the new node as the head.
o Step 3: Otherwise, traverse the list to find the correct position based on priority
order. Insert the new node at the appropriate position to maintain ascending order.
o Complexity: O(n) due to the traversal to find the insertion point.
2. Removal (remove()):
o Step 1: Check if the list is empty. If it is, output an error message.
o Step 2: Remove the head node, as it contains the element with the lowest priority.
o Step 3: Update the head pointer to the next node.
o Complexity: O(1) as it only removes the head.

Code Example in C:

c
Copy code
#include <stdio.h>
#include <stdlib.h>

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

// Insert node in ascending order of priority


void insert(struct Node** head, int data, int priority) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->priority = priority;
newNode->next = NULL;

if (*head == NULL || (*head)->priority > priority) {


newNode->next = *head;
*head = newNode;
} else {
struct Node* current = *head;
while (current->next != NULL && current->next->priority <= priority) {
current = current->next;
}
newNode->next = current->next;
current->next = newNode;
}
}

// Remove node with the highest priority (smallest value)


void remove(struct Node** head) {
if (*head == NULL) {
printf("Queue is empty\n");
return;
}
struct Node* temp = *head;
*head = (*head)->next;
free(temp);
}

Question 3: Write an Algorithm to Reverse a Given Singly Linked List

Reversing a singly linked list involves changing the direction of the next pointers, such that the
last node becomes the head and the head node becomes the last node.

Algorithm:

1. Initialize three pointers: prev (initially NULL), current (points to the head), and next
(will be used to store the next node).
2. Traverse the list. For each node:
o Save the next node.
o Change the next pointer of the current node to prev.
o Move prev and current one step forward.
3. Once current becomes NULL (end of the list), prev will be the new head of the
reversed list.

Code Example in C:
void reverse(struct Node** head) {
struct Node* prev = NULL;
struct Node* current = *head;
struct Node* next = NULL;

while (current != NULL) {


next = current->next;
current->next = prev;
prev = current;
current = next;
}
*head = prev;
}

Question 4: Write a Program to Insert and Delete an Element After a Given


Node in a Singly Linked List

This question requires two functions: one for inserting a node after a specified node and another
for deleting the node after a specified node.

Insertion:

1. Find the specified node after which the new node needs to be inserted.
2. Create a new node, set its next pointer to the next pointer of the specified node.
3. Update the next pointer of the specified node to point to the new node.

Deletion:

1. Find the specified node.


2. Check if the specified node has a next node; if not, there's nothing to delete.
3. Save the node to be deleted in a temporary pointer.
4. Update the next pointer of the specified node to skip the node to be deleted.
5. Free the memory of the deleted node.

Code Example in C:

void insertAfter(struct Node* prevNode, int data) {


if (prevNode == NULL) {
printf("Previous node cannot be NULL\n");
return;
}
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = prevNode->next;
prevNode->next = newNode;
}

void deleteAfter(struct Node* prevNode) {


if (prevNode == NULL || prevNode->next == NULL) {
printf("No node to delete\n");
return;
}
struct Node* temp = prevNode->next;
prevNode->next = temp->next;
free(temp);
}

Each function here provides the essential operations for manipulating linked lists, essential
in various applications such as implementing queues, dynamic memory structures, and
real-time systems where fast insertion and deletion are critical.

Doubly Linked List

1. C/C++ Program to Add Two Polynomials Represented Using Doubly Linked


Lists

Brief Explanation:

A polynomial can be represented using a doubly linked list where each node contains the
coefficient and exponent of a term. To add two polynomials, we traverse both lists
simultaneously, comparing the exponents:

 If the exponents match, add the coefficients.


 If one exponent is greater, move the pointer of the smaller exponent forward.
 Insert the sum result into a new list to represent the result polynomial.

Detailed Explanation:

1. Representation of a Polynomial:
o Each node in the doubly linked list contains the coefficient and exponent of a
polynomial term.
o For example, 3x2+4x+53x^2 + 4x + 53x2+4x+5 would be represented as three
nodes: (3,2),(4,1),(5,0)(3, 2), (4, 1), (5, 0)(3,2),(4,1),(5,0).
o Using a doubly linked list allows us to traverse the list in both forward and
reverse directions, making it versatile.
2. Adding Two Polynomials:
o Initialization: Start with two pointers, each at the head of one of the polynomial
lists.
o Comparison: Compare the exponents of the nodes at both pointers.
 If exponents are the same, add the coefficients and create a new node with
the sum.
 If exponents differ, add the node with the smaller exponent directly to the
result list.
o Traversal: Move the pointer for the processed term to the next node.
o Edge Cases: If one polynomial has more terms than the other, append the
remaining terms to the result.
3. Necessary Functions:
o Insert Node: To add terms to the result list in the correct order.
o Display Polynomial: To print the resulting polynomial in readable form.

C++ Code Example:

#include <iostream>
using namespace std;

struct Node {
int coeff;
int exp;
Node* next;
Node* prev;
};

Node* addTerm(Node* head, int coeff, int exp) {


Node* newNode = new Node();
newNode->coeff = coeff;
newNode->exp = exp;
newNode->next = NULL;
newNode->prev = NULL;
if (!head) return newNode;

Node* temp = head;


while (temp->next) temp = temp->next;
temp->next = newNode;
newNode->prev = temp;
return head;
}

Node* addPolynomials(Node* poly1, Node* poly2) {


Node* result = NULL;
while (poly1 && poly2) {
if (poly1->exp == poly2->exp) {
result = addTerm(result, poly1->coeff + poly2->coeff, poly1->exp);
poly1 = poly1->next;
poly2 = poly2->next;
} else if (poly1->exp > poly2->exp) {
result = addTerm(result, poly1->coeff, poly1->exp);
poly1 = poly1->next;
} else {
result = addTerm(result, poly2->coeff, poly2->exp);
poly2 = poly2->next;
}
}
while (poly1) {
result = addTerm(result, poly1->coeff, poly1->exp);
poly1 = poly1->next;
}
while (poly2) {
result = addTerm(result, poly2->coeff, poly2->exp);
poly2 = poly2->next;
}
return result;
}

2. Program to Concatenate Two Doubly Linked Lists

Brief Explanation:

Concatenating two doubly linked lists involves linking the last node of the first list to the first
node of the second list. This can be done in constant time if we maintain pointers to the last node
in each list.

Detailed Explanation:

1. Steps for Concatenation:


o Check List Head Nodes: If either of the lists is empty, return the other list as the
concatenated result.
o Traverse to the End of the First List: Start from the head of the first list and
move to its last node.
o Link Last and First Nodes: Set the next pointer of the last node in the first list
to point to the head of the second list. Update the prev pointer of the head of the
second list to point back to the last node of the first list.
2. Edge Cases:
o Empty Lists: If one list is empty, return the other list directly.
o Single-Node Lists: If both lists contain only one node, link them normally as
described.

Code Example:

void concatenate(Node*& head1, Node*& head2) {


if (!head1) {
head1 = head2;
return;
}
if (!head2) return;
Node* temp = head1;
while (temp->next) temp = temp->next;

temp->next = head2;
head2->prev = temp;
}

3. Advantages of Doubly Linked Lists over Singly Linked Lists

Brief Explanation:

A doubly linked list allows traversal in both directions, providing more flexibility compared to a
singly linked list. Additionally, it allows easy deletion of nodes from both ends, improving the
efficiency of certain operations like reversing and deletion.

Detailed Explanation:

1. Bidirectional Traversal:
o In a doubly linked list, each node contains a pointer to both its next and previous
nodes, allowing traversal in both directions.
o This is particularly useful in applications where both forward and backward
traversal are needed (e.g., in browser history, where users may go backward and
forward).
2. Efficient Node Deletion:
o With a pointer to the previous node, deleting a node is faster and doesn’t require
traversing from the head.
o For example, if we want to delete the last node, we can reach it directly from the
previous node, while in a singly linked list, we would need to traverse from the
beginning.
3. Improved Insertion and Deletion at Both Ends:
o A doubly linked list allows direct insertion and deletion at both the beginning and
end without needing to traverse the list.
o For instance, inserting at the end in a singly linked list requires traversal unless a
tail pointer is maintained. In doubly linked lists, insertion and deletion from the
end are O(1) operations.
4. Complexity Comparison:
o Doubly linked lists are slightly more complex to implement due to the extra
pointer but provide flexibility in navigation and operations.

Example Code for Deleting a Node in Doubly Linked List: The function delete(p, &x)
deletes the node pointed by p and updates x with the deleted data.

void deleteNode(Node*& head, Node* p, int& x) {


if (!p) return;

x = p->data;
if (p->prev) p->prev->next = p->next;
else head = p->next;

if (p->next) p->next->prev = p->prev;

delete p;
}

 Parameters:
o p: Node to be deleted.
o head: Reference to the head node for updating if the head node is deleted.
o x: Variable to store the data of the deleted node for further use if required.

Circular Linked List:

1. Advantages of Circular and Doubly Linked Lists over Singly Linked Lists

Circular Linked List Advantages:

1. Continuous Traversal:
o In a circular linked list, the last node points back to the first node. This creates a
loop, allowing continuous traversal of the list.
o This property is particularly useful in applications that require circular or
repetitive access, such as round-robin scheduling in operating systems.
2. Efficient Navigation:
o A circular linked list allows for a more efficient setup for applications that cycle
through data continuously. For example, in multiplayer gaming where players
take turns in a loop, a circular list lets us return to the start without additional
checks.
3. No NULL Pointers for End Node:
o Since the last node in a circular linked list points back to the head node instead of
NULL, it avoids issues with null pointers when implementing circular behavior,
reducing the need for extra conditions or checks.

Doubly Linked List Advantages:

1. Bidirectional Traversal:
o In a doubly linked list, each node has both a next and prev pointer, enabling
traversal in both forward and backward directions.
o This feature is useful in applications like browser history navigation, where users
may want to go backward and forward easily.
2. Efficient Deletion of Nodes:
o In a doubly linked list, any node can be deleted without traversing from the head.
Once a node is located, it can be removed directly since it has a prev pointer.
o For instance, deleting the last node in a singly linked list requires traversal from
the head, while in a doubly linked list, it can be done in constant time if we keep a
pointer to the tail node.
3. More Flexible Operations:
o A doubly linked list simplifies certain operations that are challenging in singly
linked lists, like adding or removing elements from both ends. This makes it
useful in data structures that require double-ended functionality, such as deques
(double-ended queues).

2. Algorithms for Operations on Circular Singly Linked List Using Header Node

A circular singly linked list with a header node means the list starts with a dummy or header
node. The header node doesn’t store data but acts as a placeholder, pointing to the start of the
actual data nodes.

Algorithm to Add a Node at the End

1. Initialize New Node:


o Create a new node, newNode, with the desired data and set its next pointer to
NULL.
2. Check if List is Empty:
o If the header node's next pointer is NULL (indicating an empty list), point the
header’s next to the newNode.
o Also, set newNode->next to the header node, completing the circular link.
3. Traverse to the Last Node:
o If the list is not empty, start from the header node and traverse to the last node
(where current->next points back to the header).
4. Insert New Node:
o Set the last node’s next pointer to newNode.
o Set newNode->next to the header node, maintaining the circular structure.
5. End of Algorithm.

Algorithm in Pseudocode:

Function addNodeAtEnd(header, data):


newNode = Create a new Node
newNode.data = data
newNode.next = NULL
If header.next is NULL:
// List is empty
header.next = newNode
newNode.next = header // Link back to header
Else:
current = header.next
While current.next != header:
current = current.next
End While
current.next = newNode
newNode.next = header // Maintain circular link
End If
End Function

Algorithm to Add a Node at the Beginning

1. Initialize New Node:


o Create a new node, newNode, with the desired data.
2. Check if List is Empty:
o If the list is empty (header’s next pointer is NULL), point header->next to
newNode and newNode->next to the header, creating a self-loop.
3. Set New Node’s next:
o If the list is not empty, set newNode->next to point to the node currently
following the header (header->next).
4. Update Header Node’s Next Pointer:
o Point header->next to the newNode, making it the first node in the list.
5. Traverse to Last Node:
o If the list was not empty, traverse to the last node (where current->next points
to the header).
6. Maintain Circular Link:
o Set the last node’s next pointer to newNode to maintain the circular structure.
7. End of Algorithm.

Algorithm in Pseudocode:

Function addNodeAtBeginning(header, data):


newNode = Create a new Node
newNode.data = data

If header.next is NULL:
// List is empty
header.next = newNode
newNode.next = header // Link back to header
Else:
newNode.next = header.next // Point new node to first node
header.next = newNode // Set header's next to new node

// Traverse to last node


current = newNode.next
While current.next != header:
current = current.next
End While
current.next = newNode // Maintain circular link
End If
End Function

Summary of Both Algorithms:

These algorithms are designed to handle circular singly linked lists using a header node, making
insertions flexible and efficient. The header node enables consistent management of the list's
start and helps avoid special cases when the list is empty or only has one node.

You might also like