SP24-DS&A-Week05-Data-Structures
SP24-DS&A-Week05-Data-Structures
Week05 (13/14/14-Mar-2024)
M Ateeq,
Department of Data Science, The Islamia University of Bahawalpur.
Linked List
Key Characteristics:
Dynamic Size: Unlike arrays, linked lists can grow or shrink in size dynamically, allowing for
efficient use of memory.
Sequential Access: Elements in a linked list can be accessed sequentially starting from the
first node. There is no direct access to the elements as in arrays.
Memory Utilization: Each node in a linked list requires extra memory for a pointer, in
addition to the data it holds.
Singly Linked List: Each node has a pointer to the next node.
Doubly Linked List: Each node has pointers to both the next and the previous nodes,
allowing for backward traversal.
Circular Linked List: The last node points back to the first node, creating a circular loop.
Memory Allocation:
Arrays: Allocate memory in a contiguous block. This requires knowing the size of the array
in advance or may involve costly operations to resize.
Linked Lists: Allocate memory for each element separately, wherever memory is available.
This makes linked lists more flexible in terms of memory usage.
Access Time:
Arrays: Provide constant time access (O(1)) to elements using their index, making them
efficient for scenarios requiring frequent access to elements by their position.
Linked Lists: Require O(n) time to access the n-th element as it involves traversing the list
from the beginning, making them less efficient for direct element access.
Arrays: Inserting or deleting elements, especially in the middle of the array, can be costly
(O(n)) as it requires shifting elements to maintain continuity.
Linked Lists: Can insert or delete nodes in O(1) time if the pointer to the previous node is
known, as it only involves changing pointers.
Memory Efficiency:
Arrays: May lead to wasted memory if the array is not fully utilized or require overhead for
resizing operations.
Linked Lists: Use memory more efficiently for dynamic data where the size changes over
time, but each node requires extra memory for storing the pointer.
Use Cases:
Arrays: Preferred for applications requiring fast access to elements by index, efficient
memory usage when the size of the data is known and does not change frequently.
Linked Lists: Ideal for applications requiring frequent insertion and deletion operations,
dynamic memory allocation, or when the size of the data is unknown or changes frequently.
In short, the choice between using an array or a linked list depends on the specific requirements
of the application, including memory constraints, operations to be performed, and the importance
of access time versus insertion/deletion efficiency.
Applications:
1. Dynamic Memory Allocation: Linked lists are used in managing dynamic memory
allocation, where the size of the data structure can grow or shrink at runtime without
reallocating the entire structure.
2. Implementing Other Data Structures: They serve as the underlying data structure for
implementing stacks, queues, graphs, and other more complex data structures.
3. Undo Functionality in Applications: Linked lists are used to implement undo functionality
in applications like word processors, where operations are stored in a list that can be
traversed backwards.
4. Hash Tables: Linked lists handle collisions in hash tables, where each bucket is a linked list,
allowing multiple entries to exist at the same hash value.
5. Memory Management: In operating systems, linked lists manage available memory blocks
and are used in the implementation of file systems.
6. Polynomial Arithmetic: They are used to represent and perform arithmetic on polynomials,
where each node represents a term in the polynomial.
Advantages:
1. Dynamic Size: Unlike arrays, the size of a linked list can grow or shrink during runtime,
which is beneficial for applications with fluctuating data size requirements.
2. Efficient Operations: Insertions and deletions are more efficient in linked lists, especially
when performed at the beginning or middle, as they generally require only pointer changes.
3. No Memory Wastage: Since linked lists allocate memory as needed for each element, they
don't reserve more memory than required, unlike static data structures.
4. Flexibility: The non-contiguous memory allocation in linked lists offers flexibility that arrays
cannot, supporting efficient memory use under fragmentation.
5. Simplicity in Reversing: Reversing a linked list is more straightforward and requires less
memory than reversing an array.
Structure: Each node contains data and a pointer to the next node in the sequence. The
last node points to null, indicating the end of the list.
Usage: Singly linked lists are simple and use less memory. They are suitable for simple
applications where only forward traversal is required.
Structure: Nodes in a doubly linked list contain data and two pointers: one pointing to the
next node and another pointing to the previous node. This allows for bidirectional traversal.
Usage: Doubly linked lists are used in applications that require frequent traversal in both
directions, such as navigation through a browser's history or a music playlist.
Singly Circular Linked List: Similar to a singly linked list, but the last node points back to
the first node, creating a circular structure.
Doubly Circular Linked List: A doubly linked list where the last node points to the first node
and the first node points to the last, allowing bidirectional circular traversal.
Usage: Circular linked lists are used in applications requiring cyclic traversals, such as a
round-robin scheduler in operating systems or implementing a multiplayer board game that
loops over players.
Each type of linked list offers distinct advantages and is chosen based on the requirements of the
application such as the need for bidirectional traversal memory usage considerations and the
Store a
Store fixed- Store elements
Store variable- collection of
size where each element Store elements in a
size sequential elements
Purpose sequential points to both the circular manner for
collections of where each
collections of next and the continuous looping.
elements. element points
elements. previous.
to the next.
O(1) at the
beginning, O(1) if inserting at
Amortized O(1),
O(n) at the O(1) if the node to known points, but
Complexity: Not applicable O(n) when
end or middle insert before/after is requires handling
Insertion (fixed size) resizing is
(if the previous known. to maintain
needed.
node is not circularity.
known).
O(1) with
direct access
O(1) with direct
O(n) to shift to the node O(1) if the node to
Complexity: Not applicable access, requires
elements after before the one be deleted is
Deletion (fixed size) handling to
deletion. to be deleted, known.
maintain circularity.
otherwise
O(n).
Each data structure offers unique benefits and comes with its own set of trade-offs, making it
better suited for certain applications over others. Understanding these differences is crucial for
choosing the most appropriate data structure for a given problem.
Yes (Circular
Queue Yes Yes Yes Yes
Queue)
Yes
Graphs Yes Yes (Adjacency Yes (Adjacency List) Not Common
List)
Hash Tables Yes Yes Yes (Chaining) Yes (Chaining) Not Common
Notes:
Stacks and Queues are commonly implemented using arrays and linked lists due to their
nature of element addition and removal. Dynamic arrays and doubly linked lists provide
more flexibility and efficiency in certain scenarios.
Deques (Double-Ended Queues) benefit significantly from doubly linked lists, which allow
efficient insertions and deletions at both ends.
Graphs can be represented using arrays (as adjacency matrices) or linked lists (as
adjacency lists), with the choice depending on the graph's density and the operations
performed.
Hash Tables often use linked lists to handle collisions through chaining, where doubly linked
lists can offer advantages in deletion complexity.
Trees are commonly represented with dynamic arrays or linked lists, where linked lists can
provide a natural representation for the hierarchical structure.
Choosing the appropriate underlying data structure depends on the specific requirements of the
advanced data structure being implemented, such as memory usage, operation complexity, and
the need for dynamic resizing.
#include <iostream>
return 0;
}
In this example:
The Node struct contains two members: an integer data member to store the data and a
Node* next member, which is a pointer to the next node in the linked list.
In the main() function, memory is allocated for a new node using the new keyword.
The data member of the node is assigned a value.
The next pointer is initialized to nullptr , indicating that this node is currently the last
node in the list.
After using the node, memory is deallocated using the delete keyword to avoid memory
leaks.
This is the basic building block for creating a linked list in C++. You can create more nodes and
link them together by setting the next pointer of one node to point to another node.
#include <iostream>
int main() {
// Example of creating a linked list
Node* head = new Node; // Head node
head->data = 10; // Assign a value to the data componen
t
head->next = nullptr; // Initialize the next pointer to null
(end of list)
return 0;
}
In this example:
The traverseLinkedList() function takes the head of the linked list as its argument.
Inside the function, a pointer current is initialized to the head node.
It then iterates over the list using a while loop. At each iteration, it prints the data stored in
the current node and moves current to the next node using current = current-
>next .
The loop continues until current becomes nullptr , indicating the end of the list.
In the main() function, a linked list is created with three nodes, and then the
traverseLinkedList() function is called to print its elements.
These functions provide the basic operations for inserting nodes into a singly linked list. You can
choose the appropriate function based on where you want to insert the new node. Remember to
handle edge cases such as an empty list or invalid positions accordingly.
Reflection MCQs
A) O(1)
B) O(log n)
C) O(n)
D) O(n^2)
Correct Option: C
4. Which of the following operations is more efficient in a linked list compared to a
dynamic array?
A) Static Array
B) Circular Linked List
C) Singly Linked List
D) Doubly Linked List
Correct Option: D
9. What is the time complexity of inserting a new node at the beginning of a singly
linked list?
A) O(1)
B) O(log n)
C) O(n)
D) O(n^2)
Correct Option: A
10. Which of the following is NOT a correct way to represent a node in a singly linked list
using struct in C++?
A) A linked list where the next pointer of the last node points to NULL.
B) A linked list where each node has two pointers: one to the next node and one to the
previous node.
C) A linked list where the next pointer of the last node points to the first node.
D) A linked list with nodes in a random order.
Correct Option: C
15. What advantage does a doubly linked list have over a singly linked list?
17. Which operation is typically more efficient in a linked list compared to an array?
A) O(1)
B) O(log n)
C) O(n)
D) O(n^2)
Correct Option: C
struct Node {
int data;
Node* next;
};
Instructions:
1. Implement insertAtBeginning : Create a new node with the given data, point it to the
current head, and update the head of the list.
2. Implement insertAtEnd : If the list is empty, the new node becomes the head. Otherwise,
traverse to the end of the list and link the new node.
3. Complete displayList : Traverse the list from the head while printing the data of each
node until the end is reached.
4. Fill in search : Traverse the list. If a node with the given data is found, return true. If the
end of the list is reached, return false.
// Insert elements
insertAtBeginning(&head, 10);
insertAtEnd(&head, 20);
insertAtBeginning(&head, 5);
displayList(head);
return 0;
}
Please feel free to share your problems to ensure that your weekly tasks are completed
and you are not staying behind