In this chapter of my journey through Data Structures and Algorithms, we steer our expedition towards an intimate exploration of Linked Lists. Our objective is to uncover their structure and operations, illustrating their implementations in Python and Java. This post functions as a chronicle of my latest progress, but more importantly, it serves as an intricate dissection of the recently conquered topic - Linked Lists. I have cultivated this understanding through my hands-on coding experience and will now elucidate it in a detailed manner, enhanced with a garnishing of insightful comments and a focused pruning of unnecessary methods.
A Linked List is a linear data structure, where the elements are not stored at contiguous memory locations, as in arrays, but are linked using pointers. Each element, called a node, consists of a value and a reference (in Python) or a pointer (in Java) to the next node in the sequence.
Let's delve into this topic, examining the implementation of a singly linked list in Python and Java, leveraging the code that I have crafted during my learning journey. I've trimmed a lot of the methods out to illustrate the foundations of this data structure but as you can imagine, there are all sorts of methods that can be added to this class, such as prepend, insert, get, etc...
Python
# We begin with defining a Node class
class Node:
def __init__(self, value):
self.value = value # The data the Node will hold
self.next = None # The next Node it points to, initially set as None
# We now define a LinkedList class
class LinkedList:
def __init__(self, value):
new_node = Node(value) # Create a new instance of Node
self.head = new_node # Our LinkedList head points to this new node
self.tail = new_node # The tail also points to the new node as it's the only node in the list
self.length = 1 # The list length is initialized to 1 as it holds one Node
# Method to append a Node to the end of our list
def append(self, value):
new_node = Node(value) # Create a new instance of Node with the given value
if self.length == 0: # If list is empty
self.head = new_node # Our head and tail point to this new node
self.tail = new_node
else:
self.tail.next = new_node # Our current tail's next points to the new node
self.tail = new_node # Our tail now points to the new node
self.length += 1 # We increment the list length by 1
Java
public class LinkedList {
private Node head; // Reference to the head of the list
private Node tail; // Reference to the tail of the list
private int length; // Variable to hold the length of the list
// We begin with defining an inner Node class
class Node {
int value; // The data the Node will hold
Node next; // Reference to the next Node
Node(int value) { // Constructor to create a new Node with the given value
this.value = value;
}
}
public LinkedList(int value) {
Node newNode = new Node(value); // Create a new instance of Node
head = newNode; // Our LinkedList head points to this new node
tail = newNode; // The tail also points to the new node as it's the only node in the list
length = 1; // The list length is initialized to 1 as it holds one Node
}
public void append(int value) {
Node newNode = new Node(value); // Create a new instance of Node with the given value
if (length == 0) { // If list is empty
head = newNode; // Our head and tail point to this new node
tail = newNode;
} else {
tail.next = newNode; // Our current tail's next points to the new node
tail = newNode; // Our tail now points to the new node
}
length++; // We increment the list length by 1
}
}
In this exploration, we observed how a Linked List is constructed, detailing how to create a Node and how to append a Node to our List. This granular approach is the cornerstone of understanding how to manipulate the data stored in this structure. Once you understand how a linked list works in code, it becomes easier to solve problems with it. Using this foundation you can start to build more complex data structures like Doubly Linked Lists and Circular Linked Lists.
As my journey into Data Structures and Algorithms progresses, expect further intricate examinations of new concepts, all of which are not only intriguing theoretically but critical for practical applications as well. As we forge ahead on this learning journey, we will continue to unravel such fascinating aspects of Data Structures and Algorithms. Let's continue to learn, code, and evolve.