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

Linked List

Uploaded by

omkarkhandare30
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views

Linked List

Uploaded by

omkarkhandare30
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

2.

1 Introduction

A linked list is a fundamental data structure in computer science. It is used to store a


sequence of elements where each element points to the next one in the sequence. The key
idea behind a linked list is that it doesn’t store data in contiguous memory locations like an
array. Instead, each element (called a node) contains two parts:

1. Data: The actual value or information being stored.


2. Pointer (or Link): A reference to the next node in the list.

This characteristic allows linked lists to be dynamic, meaning they can grow or shrink in size
without needing to be resized, unlike arrays. Linked lists are efficient when performing
operations like insertion or deletion since these can be done without shifting other elements.

2.2 Definition of a Linked List

A linked list is a sequence of nodes, where each node contains:

 Data: The actual content, value, or information stored in that node.


 Next (Link): A reference (or pointer) to the next node in the list. The last node’s next
pointer is typically set to NULL or None to indicate the end of the list.

There are different types of linked lists:

1. Singly Linked List: Each node points to the next node only. It has a one-way
connection.
2. Doubly Linked List: Each node has two pointers: one pointing to the next node and
one pointing to the previous node. This allows traversal in both directions.
3. Circular Linked List: The last node points back to the first node, making the list
circular.

Example of a Singly Linked List:

Imagine you have a list of three numbers: [10, 20, 30]. The corresponding linked list would
look like this:

[10 | Next] -> [20 | Next] -> [30 | NULL]

Here:

 The first node contains the number 10 and points to the next node (20).
 The second node contains 20 and points to the next node (30).
 The third node contains 30 and points to NULL, indicating the end of the list.
Diagram of a Singly Linked List:
Head

|
v
[10 | Next] -> [20 | Next] -> [30 | NULL]

In this example:

 The head is the starting point of the linked list.


 Each node has a data part (10, 20, 30) and a pointer to the next node.

2.3 Memory Allocation in a Linked List

Memory allocation in a linked list is dynamic, meaning that each node is allocated memory as
needed. Unlike arrays, where the size is fixed at the time of creation, a linked list can grow or
shrink without needing to know the size in advance.

In a linked list:

1. Each node is allocated memory separately. When a new node is created, memory is
reserved for the data and the pointer to the next node.
2. No contiguous memory is required. Since each node contains a reference to the next
node, the nodes can be scattered in memory.

This allows for efficient insertion and deletion of nodes because only the pointers need to be
updated, rather than moving large blocks of memory.

Example:

Let’s assume we are creating a linked list of three nodes:

1. Node 1 (Data: 10): The system allocates memory for this node, stores 10 in it, and the
next pointer will point to the second node.
2. Node 2 (Data: 20): The system allocates a separate block of memory for this node,
stores 20, and its next pointer will point to the third node.
3. Node 3 (Data: 30): The system allocates memory for this node and stores 30, with its
next pointer set to NULL.

The nodes are stored in different parts of memory, and their links point to each other, creating
the linked list.

Diagram of Memory Allocation:


[10 | Next] [20 | Next] [30 | NULL]
| | |
Memory Memory Memory
Here, each block (node) is in a separate memory location, but the next pointer of one node
links to the memory address of the next node.

Key Points about Memory in Linked Lists:

 Dynamic allocation: Each node is allocated memory only when needed.


 Non-contiguous memory: Nodes can be scattered across memory.
 Pointer update: Insertion and deletion of nodes only involve changing the pointers,
not the entire list.

Summary of Key Concepts:

1. Linked List: A sequence of nodes, where each node points to the next.
2. Node: A unit of the linked list containing data and a pointer.
3. Memory Allocation: Each node is allocated memory separately, and no contiguous
block of memory is required.

Visualizing the Concepts:

 Linked list: A chain of nodes linked together through pointers.


 Memory: Each node lives in its own memory space, and links (pointers) connect the
nodes.

This dynamic nature of linked lists makes them efficient in situations where frequent
insertion and deletion of elements are required.

2.4 Types of Linked Lists

A linked list is a type of data structure that organizes data in a sequence where each element
(node) points to the next one. There are several types of linked lists:

1. Singly Linked List


2. Circular Linked List
3. Doubly Linked List

We'll discuss each type in detail, along with the operations that can be performed on them.

2.4.1 Singly Linked List

A Singly Linked List (SLL) is a type of linked list where each node contains two parts:

1. Data: The value being stored in the node.


2. Next (Pointer): A reference (or pointer) to the next node in the list. The last node's
pointer is set to NULL (or None in Python), indicating the end of the list.

In a singly linked list, traversal is only possible in one direction — from the first node (head)
to the last node.

Example:

Consider a singly linked list with three nodes containing the values 10, 20, and 30:

Head
|
v
[10 | Next] -> [20 | Next] -> [30 | NULL]

Here:

 Head points to the first node.


 The first node stores the data 10 and points to the second node.
 The second node stores the data 20 and points to the third node.
 The third node stores the data 30 and points to NULL, indicating the end of the list.

2.4.2 Operations on a Singly Linked List

The most common operations that can be performed on a singly linked list are:

1. Insertion: Adding a node to the list.


o At the beginning (Head): Insert a new node at the start of the list.
o At the end: Insert a new node at the end of the list.
o After a given node: Insert a new node after a specific node.
2. Deletion: Removing a node from the list.
o At the beginning (Head): Remove the node at the start.
o At the end: Remove the node at the end.
o Specific node: Remove a node by value or position.
3. Traversal: Moving through the list from the head to the last node and printing the
data of each node.

Example Operations:

 Insert at the Beginning: Adding a new node with value 5 at the start.

Before:

Head -> [10 | Next] -> [20 | Next] -> [30 | NULL]

After inserting 5 at the beginning:

Head -> [5 | Next] -> [10 | Next] -> [20 | Next] -> [30 | NULL]
class Node:
"""Represents a node in a singly linked list."""
def __init__(self, data=None):
self.data = data
self.next = None

class SinglyLinkedList:
"""Implements a singly linked list."""
def __init__(self):
self.start = None

def is_empty(self):
"""Checks if the linked list is empty."""
return self.start is None

def insert_start(self, data):


"""Inserts a new node at the beginning of the list."""
new_node = Node(data)
new_node.next = self.start
self.start = new_node

def insert_end(self, data):


"""Inserts a new node at the end of the list."""
new_node = Node(data)
if self.is_empty():
self.start = new_node
else:
current = self.start
while current.next:
current = current.next
current.next = new_node

def insert_after(self, target, data):


"""Inserts a new node after the node with the given target data."""
if self.is_empty():
print("List is empty, cannot insert after.")
return
current = self.start
while current and current.data != target:
current = current.next
if current is None:
print(f"Node with data {target} not found.")
else:
new_node = Node(data)
new_node.next = current.next
current.next = new_node

def delete_start(self):
"""Deletes the first node in the linked list."""
if self.is_empty():
print("List is empty, nothing to delete.")
return
self.start = self.start.next

def delete_end(self):
"""Deletes the last node in the linked list."""
if self.is_empty():
print("List is empty, nothing to delete.")
return
if self.start.next is None:
self.start = None
return
current = self.start
while current.next.next:
current = current.next
current.next = None

def delete_node(self, key):


"""Deletes the first node with the specified key."""
if self.is_empty():
print("List is empty, nothing to delete.")
return

# If the node to delete is the first node


if self.start.data == key:
self.start = self.start.next
return

# Find the node to delete


current = self.start
while current.next and current.next.data != key:
current = current.next

if current.next is None:
print(f"Node with data {key} not found.")
else:
current.next = current.next.next

def display(self):
"""Displays all nodes in the linked list."""
if self.is_empty():
print("List is empty.")
return
current = self.start
while current:
print(current.data, end=" -> ")
current = current.next
print("None")

def search(self, key):


"""Searches for a node with the given key in the linked list."""
current = self.start
while current:
if current.data == key:
return True
current = current.next
return False

# Example Usage
if __name__ == "__main__":
sll = SinglyLinkedList()
sll.insert_end(10)
sll.insert_end(20)
sll.insert_start(5)
sll.display() # Output: 5 -> 10 -> 20 -> None

sll.insert_after(10, 15)
sll.display() # Output: 5 -> 10 -> 15 -> 20 -> None

sll.delete_node(15)
sll.display() # Output: 5 -> 10 -> 20 -> None

print("Search for 10:", sll.search(10)) # Output: True


print("Search for 30:", sll.search(30)) # Output: False

2.4.3 Circular Linked Lists

A Circular Linked List is a type of linked list where the last node’s next pointer points back
to the first node instead of NULL. This creates a circle-like structure. Circular linked lists can
be of two types:

1. Singly Circular Linked List: Each node points to the next, and the last node points
back to the first.
2. Doubly Circular Linked List: Each node has two pointers (next and previous), and
the last node’s next pointer points to the first node, while the first node’s previous
pointer points to the last node.

Problem:
In a traditional singly linked list, traversing the list becomes slow and inefficient when
dealing with operations that require continuous looping through the list. Additionally,
managing both the head and tail pointers consumes extra memory and adds complexity.

Solution: Circular Linked List

A Circular Linked List solves these problems by:

1. Continuous Traversal:
o In a circular linked list, the last node points to the first node, forming a loop.
This allows for continuous traversal without needing to check for the end
(None), making it perfect for applications like round-robin scheduling or
cyclic buffers.
2. Memory Optimization:
o Instead of having both head and tail pointers, only a single pointer to the
last node is needed to manage the entire list. This reduces memory usage and
simplifies management.
3. Simplified Operations:
o Insertion and deletion operations become easier because you don’t need to
handle special cases for the end of the list. For example, the last.next
always points to the first node, so adding or removing nodes is
straightforward.

Example

Imagine a circular list with nodes 10 -> 20 -> 30 -> (back to 10).

 Traversal: You can start at any node and loop through the entire list without
worrying about reaching the end.
 Insertion: Adding a node at the end simply requires linking it to the first node,
making the operation simple and quick.
 Memory: Only the last node needs to be tracked, making it more efficient than a
traditional list where both head and tail are needed.

Key Benefits:

 Faster and Simpler Traversal: No need to check for the end of the list.
 Reduced Memory Usage: Only one pointer (last) is needed to manage the list.
 Efficient Operations: Easier to add/remove nodes, especially at both ends.

Thus, a circular linked list offers a more efficient solution for continuous and cyclic data
processing.

Example of a Singly Circular Linked List:

Consider a circular linked list with three nodes containing values 10, 20, and 30:
Head
|
v
[10 | Next] -> [20 | Next] -> [30 | Next] -> (Back to Head)

 In this list, after the last node (30), the next pointer of node 30 points back to the first
node (10), completing the circle.

2.4.4 Operations on a Circular Linked List

Circular linked lists support operations similar to singly linked lists, but there are some
differences due to the circular nature. The common operations are:

1. Insertion:
o At the beginning: Add a new node before the head.
o At the end: Add a new node and link it to the head to complete the circle.
o After a specific node: Insert after a given node.
2. Deletion:
o At the beginning: Remove the head node and update the next pointer to the
next node.
o At the end: Remove the last node and adjust the pointer of the second-to-last
node to the head.
3. Traversal: Traversal can be a little tricky because you need to ensure you don’t loop
infinitely. The traversal will stop once you encounter the head node again.
4. class Node:
5. """Represents a node in a circular singly linked list."""
6. def __init__(self, data=None):
7. self.data = data
8. self.next = None
9.
10. class CircularLinkedList:
11. """Implements a singly circular linked list using only the 'last'
pointer."""
12.
13. def __init__(self):
14. self.last = None # Only 'last' pointer is needed
15.
16. def is_empty(self):
17. """Checks if the circular linked list is empty."""
18. return self.last is None
19.
20. def insert_start(self, data):
21. """Inserts a new node at the beginning of the list."""
22. new_node = Node(data)
23. if self.is_empty():
24. self.last = new_node
25. new_node.next = self.last # Point to itself
26. else:
27. new_node.next = self.last.next # New node points to
current start
28. self.last.next = new_node # Last node points to new node
29.
30. def insert_end(self, data):
31. """Inserts a new node at the end of the list."""
32. new_node = Node(data)
33. if self.is_empty():
34. self.last = new_node
35. new_node.next = self.last # Point to itself
36. else:
37. new_node.next = self.last.next # New node points to the
first node
38. self.last.next = new_node # Last node points to the new
node
39. self.last = new_node # Update the last pointer to new node
40.
41. def insert_after(self, target, data):
42. """Inserts a new node after the node with the given target
data."""
43. if self.is_empty():
44. print("List is empty, cannot insert after.")
45. return
46. current = self.last.next # Start from the first node
47. while current != self.last:
48. if current.data == target:
49. new_node = Node(data)
50. new_node.next = current.next
51. current.next = new_node
52. if current == self.last: # If target is the last node,
update the last pointer
53. self.last = new_node
54. return
55. current = current.next
56. # Check if the target is the last node
57. if current.data == target:
58. new_node = Node(data)
59. new_node.next = self.last.next
60. current.next = new_node
61. self.last = new_node # Update the last pointer
62.
63. def delete_start(self):
64. """Deletes the first node in the circular linked list."""
65. if self.is_empty():
66. print("List is empty, nothing to delete.")
67. return
68. if self.last.next == self.last: # Only one node in the list
69. self.last = None # List becomes empty
70. else:
71. self.last.next = self.last.next.next # Point last to
second node
72.
73. def delete_end(self):
74. """Deletes the last node in the circular linked list."""
75. if self.is_empty():
76. print("List is empty, nothing to delete.")
77. return
78. if self.last.next == self.last: # Only one node in the list
79. self.last = None # List becomes empty
80. else:
81. current = self.last.next
82. while current.next != self.last: # Find the second-to-last
node
83. current = current.next
84. current.next = self.last.next # Update the second-to-last
node to point to first node
85. self.last = current # Update last pointer to second-to-
last node
86.
87. def delete_node(self, target):
88. """Deletes the first node with the specified target data."""
89. if self.is_empty():
90. print("List is empty, nothing to delete.")
91. return
92. current = self.last.next
93. if current.data == target: # Target is the first node
94. self.delete_start()
95. return
96. while current.next != self.last and current.next.data !=
target:
97. current = current.next
98. if current.next.data == target: # Node to delete found
99. current.next = current.next.next # Remove the node
100. if current.next == self.last: # If last node is
deleted, update last pointer
101. self.last = current
102. else:
103. print(f"Node with data {target} not found.")
104.
105. def display(self):
106. """Displays all nodes in the circular linked list."""
107. if self.is_empty():
108. print("List is empty.")
109. return
110. current = self.last.next # Start from the first node
111. while current != self.last:
112. print(current.data, end=" -> ")
113. current = current.next
114. print(current.data, end=" -> ") # Print the last node
115. print("Back to start") # Indicate circular nature
116.
117. # Example Usage
118. if __name__ == "__main__":
119. # Create a circular linked list and perform operations
120. cll = CircularLinkedList()
121.
122. # Insert nodes
123. cll.insert_end(10)
124. cll.insert_end(20)
125. cll.insert_start(5)
126. cll.insert_end(30)
127. cll.display() # Output: 5 -> 10 -> 20 -> 30 -> Back to start
128.
129. # Insert a node after a specific node
130. cll.insert_after(10, 15)
131. cll.display() # Output: 5 -> 10 -> 15 -> 20 -> 30 -> Back to
start
132.
133. # Delete the first node
134. cll.delete_start()
135. cll.display() # Output: 10 -> 15 -> 20 -> 30 -> Back to
start
136.
137. # Delete the last node
138. cll.delete_end()
139. cll.display() # Output: 10 -> 15 -> 20 -> Back to start
140.
141. # Delete a specific node (node with data 20)
142. cll.delete_node(20)
143. cll.display() # Output: 10 -> 15 -> Back to start
144.
145. # Attempt to delete a non-existing node
146. cll.delete_node(50) # Output: Node with data 50 not found.
147.

2.4.5 Doubly Linked List

A Doubly Linked List (DLL) is a type of linked list where each node contains three parts:

1. Data: The value being stored.


2. Next: A reference (pointer) to the next node.
3. Previous: A reference (pointer) to the previous node.

In a doubly linked list, you can traverse both forward and backward, as each node points to
both the next and the previous node.

Example:

Consider a doubly linked list with three nodes containing values 10, 20, and 30:

NULL <- [10 | Prev | Next] <-> [20 | Prev | Next] <-> [30 | Prev | Next] ->
NULL

 The first node’s prev pointer is NULL because it has no previous node.
 The last node’s next pointer is NULL because it has no next node.
 Each node is connected to both its previous and next node, allowing for two-way
traversal.

2.4.6 Operations on a Doubly Linked List

In a Doubly Linked List, the operations are similar to those in a singly linked list but with
the added flexibility of two pointers per node. The common operations are:

1. Insertion:
o At the beginning: Insert before the first node. Update the head pointer.
o At the end: Insert after the last node.
o After a specific node: Insert after a given node.
o Before a specific node: Insert before a given node.
2. Deletion:
o At the beginning: Remove the first node and update the head.
o At the end: Remove the last node and update the last pointer.
o Specific node: Remove a node by value or position.
3. Traversal:
o Forward Traversal: Start from the head and go to the next node using the
next pointer.
o Backward Traversal: Start from the tail (last node) and go backward using
the previous pointer.

Example Operations:

 Insert at the Beginning: Adding a new node with value 5 at the start.

Before:

NULL <- [10 | Prev | Next] <-> [20 | Prev | Next] <-> [30 | Prev | Next] ->
NULL

After inserting 5 at the beginning:


NULL <- [5 | Prev | Next] <-> [10 | Prev | Next] <-> [20 | Prev | Next] <->
[30 | Prev | Next] -> NULL

Summary of Linked List Types and Operations:

1. Singly Linked List: One pointer per node (next). Efficient for simple operations but
only one-way traversal.
2. Circular Linked List: The last node points to the first, forming a circle. It can be
singly or doubly circular.
3. Doubly Linked List: Two pointers per node (next and previous). Allows traversal in
both directions.

Operations across all linked lists include:

 Insertion: Add a node at various positions.


 Deletion: Remove a node from the list.
 Traversal: Visit each node in the list.
 class Node:
 """Represents a node in a doubly linked list."""
 def __init__(self, data=None):
 self.data = data
 self.next = None
 self.prev = None

 class DoublyLinkedList:
 """Implements a doubly linked list."""
 def __init__(self):
 self.start = None

 def is_empty(self):
 """Checks if the doubly linked list is empty."""
 return self.start is None

 def insert_start(self, data):
 """Inserts a new node at the beginning of the list."""
 new_node = Node(data)
 if self.is_empty():
 self.start = new_node
 else:
 new_node.next = self.start
 self.start.prev = new_node
 self.start = new_node

 def insert_end(self, data):
 """Inserts a new node at the end of the list."""
 new_node = Node(data)
 if self.is_empty():
 self.start = new_node
 else:
 current = self.start
 while current.next:
 current = current.next
 current.next = new_node
 new_node.prev = current

 def insert_after(self, target, data):
 """Inserts a new node after the node with the given target
data."""
 if self.is_empty():
 print("List is empty, cannot insert after.")
 return
 current = self.start
 while current and current.data != target:
 current = current.next
 if current is None:
 print(f"Node with data {target} not found.")
 else:
 new_node = Node(data)
 new_node.next = current.next
 if current.next:
 current.next.prev = new_node
 current.next = new_node
 new_node.prev = current

 def delete_start(self):
 """Deletes the first node in the doubly linked list."""
 if self.is_empty():
 print("List is empty, nothing to delete.")
 return
 if self.start.next is None:
 self.start = None
 else:
 self.start = self.start.next
 self.start.prev = None

 def delete_end(self):
 """Deletes the last node in the doubly linked list."""
 if self.is_empty():
 print("List is empty, nothing to delete.")
 return
 current = self.start
 while current.next:
 current = current.next
 if current.prev:
 current.prev.next = None
 else:
 self.start = None

 def delete_node(self, key):
 """Deletes the node with the specified key."""
 if self.is_empty():
 print("List is empty, nothing to delete.")
 return
 current = self.start
 if current.data == key: # Special case: first node
 self.delete_start()
 return
 while current and current.data != key:
 current = current.next
 if current is None:
 print(f"Node with data {key} not found.")
 else:
 if current.next:
 current.next.prev = current.prev
 if current.prev:
 current.prev.next = current.next

 def display(self):
 """Displays all nodes in the doubly linked list."""
 if self.is_empty():
 print("List is empty.")
 return
 current = self.start
 while current:
 print(current.data, end=" <-> ")
 current = current.next
 print("None")

 def search(self, key):
 """Searches for a node with the given key in the doubly linked
list."""
 current = self.start
 while current:
 if current.data == key:
 return True
 current = current.next
 return False

 # Example Usage of Doubly Linked List
 if __name__ == "__main__":
 dll = DoublyLinkedList()
 dll.insert_end(10)
 dll.insert_end(20)
 dll.insert_start(5)
 dll.display() # Output: 5 <-> 10 <-> 20 <-> None

 dll.insert_after(10, 15)
 dll.display() # Output: 5 <-> 10 <-> 15 <-> 20 <-> None

 dll.delete_node(15)
 dll.display() # Output: 5 <-> 10 <-> 20 <-> None

 print("Search for 10:", dll.search(10)) # Output: True
 print("Search for 30:", dll.search(30)) # Output: False

Summary of Linked List Types:

Type Description Advantages Disadvantages

Singly Linked Linear list where each node Simple, space- Can only traverse in one
List points to the next node. efficient. direction.

Circular Last node points to the first Efficient for circular Complexity increases for
Linked List node, forming a circle. traversal. deletion.

Doubly Nodes have both prev and Can traverse in both More memory usage due to
Linked List next pointers. directions. extra pointer.

Diagrams for Quick Reference:

Singly Linked List:

Head -> [10 | Next] -> [20 | Next] -> [30 | NULL]

Circular Linked List (Singly):

Head -> [10 | Next] -> [20 | Next] -> [30 | Next] -> (Back to Head)

Doubly Linked List:

NULL <- [10 | Prev | Next] <-> [20 | Prev | Next] <-> [30 | Prev | Next] ->
NULL

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy