Python Linked List
## Implementing a Linked List in Python with Sorting and Search Capabilities
A **Linked List** is a fundamental linear data structure used in computer science. Unlike arrays or Python's built-in lists, elements in a linked list are not stored in contiguous memory locations. Instead, they are represented as individual **Nodes**. Each node contains two parts:
1. **Data**: The actual value stored in the node.
2. **Next**: A reference (or pointer) to the next node in the sequence.
In this tutorial, we will build a Singly Linked List from scratch in Python, implementing essential operations such as appending elements, traversing, searching, and sorting.
---
## Architecture of a Linked List
A singly linked list is managed via a `head` pointer, which points to the first node. If the list is empty, the `head` points to `None`. The last node of the list always points to `None` to signify the end of the sequence.
```
β
βΌ
βββββββββββββ βββββββββββββ βββββββββββββ
β Data | Nextβ βββ> β Data | Nextβ βββ> β Data | Nextβ βββ> None
βββββββββββββ βββββββββββββ βββββββββββββ
```
---
## Code Implementation
Below is the complete Python implementation of a `Node` class and a `LinkedList` class containing `append`, `display`, `sort`, and `search` methods.
```python
class Node:
"""Represents a single node in the linked list."""
def __init__(self, data):
self.data = data # Stores the data payload
self.next = None # Reference to the next node, initialized to None
class LinkedList:
"""Represents the linked list structure."""
def __init__(self):
self.head = None # Points to the first node of the list
def append(self, data):
"""Appends a new node with the given data to the end of the list."""
new_node = Node(data)
# If the list is empty, make the new node the head
if not self.head:
self.head = new_node
return
# Traverse to the last node
last_node = self.head
while last_node.next:
last_node = last_node.next
# Link the last node to the new node
last_node.next = new_node
def display(self):
"""Traverses and prints the entire linked list."""
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
def sort(self):
"""
Sorts the elements of the linked list.
Extracts data to a Python list, sorts it, and reconstructs the linked list.
"""
if not self.head:
return
# Step 1: Extract data from the nodes
sorted_list = []
current = self.head
while current:
sorted_list.append(current.data)
current = current.next
# Step 2: Sort the extracted data
sorted_list.sort()
# Step 3: Rebuild the linked list with sorted values
self.head = None
for item in sorted_list:
self.append(item)
def search(self, key):
"""Searches for a specific value in the linked list. Returns True if found."""
current = self.head
while current:
if current.data == key:
return True
current = current.next
return False
# --- Example Usage ---
if __name__ == "__main__":
# Initialize the linked list
ll = LinkedList()
# Append elements
ll.append(3)
ll.append(1)
ll.append(4)
ll.append(2)
print("Original Linked List:")
ll.display()
# Sort the linked list
ll.sort()
print("\nSorted Linked List:")
ll.display()
# Search for elements
print("\nSearch Results:")
print(f"Is 4 in the list? {ll.search(4)}")
print(f"Is 5 in the list? {ll.search(5)}")
```
---
## Detailed Code Walkthrough
### 1. The `Node` Class
The `Node` class is the building block of our linked list. It contains:
* `self.data`: Holds the value.
* `self.next`: Holds the reference to the next node. It defaults to `None` when a node is first created.
### 2. The `LinkedList` Class
The `LinkedList` class manages the nodes and exposes the API for interacting with them:
* **`append(data)`**: Adds a node to the end of the list. If the list is empty (`self.head is None`), the new node becomes the head. Otherwise, it traverses the list using a `while` loop until it finds the node where `next` is `None`, then attaches the new node there.
* **`display()`**: Iterates through each node starting from `self.head` and prints its data, visually representing the links with `->` arrows.
* **`sort()`**: This implementation uses a hybrid approach. It extracts the node values into a standard Python list, utilizes Python's highly optimized Timsort algorithm (`list.sort()`), and then clears and rebuilds the linked list structure in sorted order.
* **`search(key)`**: Performs a linear search starting from the `head`. It compares each node's data with the target `key`. If a match is found, it returns `True`; if the end of the list is reached without a match, it returns `False`.
---
## Execution Output
When you run the script, you will see the following output:
```text
Original Linked List:
3 -> 1 -> 4 -> 2 -> None
Sorted Linked List:
1 -> 2 -> 3 -> 4 -> None
Search Results:
Is 4 in the list? True
Is 5 in the list? False
```
---
## Key Considerations & Complexity Analysis
| Operation | Time Complexity | Space Complexity | Description |
| :--- | :--- | :--- | :--- |
| **Append** | $O(N)$ | $O(1)$ | Requires traversing the entire list to find the end. Can be optimized to $O(1)$ by maintaining a `tail` pointer. |
| **Search** | $O(N)$ | $O(1)$ | Requires linear traversal in the worst case. |
| **Sort** | $O(N \log N)$ | $O(N)$ | The current approach uses an auxiliary Python list, which requires $O(N)$ extra space but benefits from Python's fast C-implemented sorting. |
### Optimization Tips
* **Tail Pointer**: By keeping track of a `self.tail` pointer in the `LinkedList` class, you can optimize the `append` operation from $O(N)$ to $O(1)$.
* **In-Place Sorting**: For memory-constrained environments, you can implement in-place sorting algorithms like **Merge Sort** directly on the linked list nodes to achieve $O(N \log N)$ time complexity with $O(1)$ auxiliary space.
YouTip