# Chapter 8: Data Structures Used in Algorithms

## 8.2 Linked Lists

Linked Lists are a fascinating and powerful type of data structure that can be used to solve a variety of problems. They organize items in a sequential manner, with each item having a pointer to the next one in the list. This simple yet elegant approach allows for efficient insertion and deletion of items, making them ideal for situations where dynamic data structures are required.

In addition to their efficiency, Linked Lists are also incredibly flexible. They can be singly linked, where each item points to the next one, or doubly linked, where each item points both to the next and the previous item in the list. This flexibility allows for a wide range of applications, from implementing stacks and queues to creating complex data structures like trees and graphs.

Overall, Linked Lists are a fundamental concept in computer science and should be part of every programmer's toolkit. Whether you are a beginner or an experienced developer, understanding how Linked Lists work and how to use them can help you solve problems more efficiently and effectively. So next time you are faced with a data structure challenge, remember to consider Linked Lists as a potential solution!

In a Linked List, each item is contained in a node, which has two elements:

**Data**: This contains the value to be stored in the node.**Next**: This contains the reference (or link) to the next node in the chain.

The starting point of the linked list is referred to as the **head**. The final element, or **tail**, of the linked list points to a null reference, indicating the end of the chain.

Here's a simple diagram to illustrate the concept:

`[Head | ] --> [Data | ] --> [Data | ] --> [Data | Null]`

In Python, we can define a simple Node class and a LinkedList class as follows:

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

class LinkedList:

def __init__(self):

self.head = Node()

The LinkedList class maintains a reference to the head node. Initially, this node does not contain any data.

Now, let's add functionality to add elements at the end (append) and display the list:

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

class LinkedList:

def __init__(self):

self.head = Node()

def append(self, data):

new_node = Node(data)

cur = self.head

while cur.next:

cur = cur.next

cur.next = new_node

def display(self):

elems = []

cur_node = self.head

while cur_node.next:

cur_node = cur_node.next

elems.append(cur_node.data)

print(elems)

Here's how we can create a linked list and add elements to it:

`my_list = LinkedList()`

my_list.append(1)

my_list.append(2)

my_list.append(3)

my_list.display() # Output: [1, 2, 3]

And that's the basic concept of a Linked List! In comparison to arrays, linked lists are dynamic and efficient in some operations such as insertion and deletion. However, they use more memory to store data due to the added storage needed for pointers and have slower access times for individual elements.

### 8.2.1 Other Types of Linked Lists

**Doubly Linked Lists**

In computer science, a linked list is a data structure that consists of a sequence of nodes. Each node contains an element of data and a link to the next node in the sequence. However, traversing a linked list from the end to the beginning is impossible without additional information.

That's where the doubly linked list comes in! In a doubly linked list, each node has a link to both the next node and the previous node. This allows for traversal in both directions, which can be useful in various applications. For instance, it can make it easier to manipulate sequences of data in both forward and backward directions.

However, a doubly linked list requires more memory than a singly linked list since it needs to store the extra links. Despite this drawback, doubly linked lists are widely used in various computer science applications and are an important concept to learn for aspiring programmers.

Example:

Each node in a doubly linked list contains an element and two links pointing to the previous and next node respectively.

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

self.prev = None

class DoublyLinkedList:

def __init__(self):

self.head = None

To add elements to the list, you'll need a method that handles the

and **prev**

pointers appropriately.**next**

2. **Circular Linked Lists**

A circular linked list is a type of linked list where the last node points back to the head node rather than to null. This forms a circular chain, which can be useful in various applications. For example, in an operating system's task scheduler, the data is typically cyclic, and a circular linked list can be used to manage the tasks in a round-robin fashion.

Additionally, circular linked lists can be used in computer graphics to create shapes with a circular boundary, such as arcs or circles. They can also be used in various data structures, such as stacks and queues, to implement algorithms more efficiently.

Despite their usefulness, circular linked lists can be more complex than regular linked lists, and they require careful management to avoid infinite loops and memory leaks. However, with proper implementation and use, circular linked lists can be a powerful tool in a programmer's arsenal.

Example:

In a circular linked list, the

of the last node points to the first node.**next**

`class Node:`

def __init__(self, data):

self.data = data

self.next = None

class CircularLinkedList:

def __init__(self):

self.head = None

def append(self, data):

if not self.head:

self.head = Node(data)

self.head.next = self.head

else:

new_node = Node(data)

cur = self.head

while cur.next != self.head:

cur = cur.next

cur.next = new_node

new_node.next = self.head

In this implementation, we create a new node. If the list is empty (

is **self.head**

), we point **None**

to the new node and make the **self.head**

of the new node point to itself. If the list is not empty, we traverse to the end of the list (where **next**

is **cur.next**

) and adjust the **self.head**

pointers of the last node and the new node to maintain the circular structure.**next**

## 8.2 Linked Lists

Linked Lists are a fascinating and powerful type of data structure that can be used to solve a variety of problems. They organize items in a sequential manner, with each item having a pointer to the next one in the list. This simple yet elegant approach allows for efficient insertion and deletion of items, making them ideal for situations where dynamic data structures are required.

In addition to their efficiency, Linked Lists are also incredibly flexible. They can be singly linked, where each item points to the next one, or doubly linked, where each item points both to the next and the previous item in the list. This flexibility allows for a wide range of applications, from implementing stacks and queues to creating complex data structures like trees and graphs.

Overall, Linked Lists are a fundamental concept in computer science and should be part of every programmer's toolkit. Whether you are a beginner or an experienced developer, understanding how Linked Lists work and how to use them can help you solve problems more efficiently and effectively. So next time you are faced with a data structure challenge, remember to consider Linked Lists as a potential solution!

In a Linked List, each item is contained in a node, which has two elements:

**Data**: This contains the value to be stored in the node.**Next**: This contains the reference (or link) to the next node in the chain.

The starting point of the linked list is referred to as the **head**. The final element, or **tail**, of the linked list points to a null reference, indicating the end of the chain.

Here's a simple diagram to illustrate the concept:

`[Head | ] --> [Data | ] --> [Data | ] --> [Data | Null]`

In Python, we can define a simple Node class and a LinkedList class as follows:

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

class LinkedList:

def __init__(self):

self.head = Node()

The LinkedList class maintains a reference to the head node. Initially, this node does not contain any data.

Now, let's add functionality to add elements at the end (append) and display the list:

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

class LinkedList:

def __init__(self):

self.head = Node()

def append(self, data):

new_node = Node(data)

cur = self.head

while cur.next:

cur = cur.next

cur.next = new_node

def display(self):

elems = []

cur_node = self.head

while cur_node.next:

cur_node = cur_node.next

elems.append(cur_node.data)

print(elems)

Here's how we can create a linked list and add elements to it:

`my_list = LinkedList()`

my_list.append(1)

my_list.append(2)

my_list.append(3)

my_list.display() # Output: [1, 2, 3]

And that's the basic concept of a Linked List! In comparison to arrays, linked lists are dynamic and efficient in some operations such as insertion and deletion. However, they use more memory to store data due to the added storage needed for pointers and have slower access times for individual elements.

### 8.2.1 Other Types of Linked Lists

**Doubly Linked Lists**

In computer science, a linked list is a data structure that consists of a sequence of nodes. Each node contains an element of data and a link to the next node in the sequence. However, traversing a linked list from the end to the beginning is impossible without additional information.

That's where the doubly linked list comes in! In a doubly linked list, each node has a link to both the next node and the previous node. This allows for traversal in both directions, which can be useful in various applications. For instance, it can make it easier to manipulate sequences of data in both forward and backward directions.

However, a doubly linked list requires more memory than a singly linked list since it needs to store the extra links. Despite this drawback, doubly linked lists are widely used in various computer science applications and are an important concept to learn for aspiring programmers.

Example:

Each node in a doubly linked list contains an element and two links pointing to the previous and next node respectively.

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

self.prev = None

class DoublyLinkedList:

def __init__(self):

self.head = None

To add elements to the list, you'll need a method that handles the

and **prev**

pointers appropriately.**next**

2. **Circular Linked Lists**

A circular linked list is a type of linked list where the last node points back to the head node rather than to null. This forms a circular chain, which can be useful in various applications. For example, in an operating system's task scheduler, the data is typically cyclic, and a circular linked list can be used to manage the tasks in a round-robin fashion.

Additionally, circular linked lists can be used in computer graphics to create shapes with a circular boundary, such as arcs or circles. They can also be used in various data structures, such as stacks and queues, to implement algorithms more efficiently.

Despite their usefulness, circular linked lists can be more complex than regular linked lists, and they require careful management to avoid infinite loops and memory leaks. However, with proper implementation and use, circular linked lists can be a powerful tool in a programmer's arsenal.

Example:

In a circular linked list, the

of the last node points to the first node.**next**

`class Node:`

def __init__(self, data):

self.data = data

self.next = None

class CircularLinkedList:

def __init__(self):

self.head = None

def append(self, data):

if not self.head:

self.head = Node(data)

self.head.next = self.head

else:

new_node = Node(data)

cur = self.head

while cur.next != self.head:

cur = cur.next

cur.next = new_node

new_node.next = self.head

In this implementation, we create a new node. If the list is empty (

is **self.head**

), we point **None**

to the new node and make the **self.head**

of the new node point to itself. If the list is not empty, we traverse to the end of the list (where **next**

is **cur.next**

) and adjust the **self.head**

pointers of the last node and the new node to maintain the circular structure.**next**

## 8.2 Linked Lists

Linked Lists are a fascinating and powerful type of data structure that can be used to solve a variety of problems. They organize items in a sequential manner, with each item having a pointer to the next one in the list. This simple yet elegant approach allows for efficient insertion and deletion of items, making them ideal for situations where dynamic data structures are required.

In addition to their efficiency, Linked Lists are also incredibly flexible. They can be singly linked, where each item points to the next one, or doubly linked, where each item points both to the next and the previous item in the list. This flexibility allows for a wide range of applications, from implementing stacks and queues to creating complex data structures like trees and graphs.

Overall, Linked Lists are a fundamental concept in computer science and should be part of every programmer's toolkit. Whether you are a beginner or an experienced developer, understanding how Linked Lists work and how to use them can help you solve problems more efficiently and effectively. So next time you are faced with a data structure challenge, remember to consider Linked Lists as a potential solution!

In a Linked List, each item is contained in a node, which has two elements:

**Data**: This contains the value to be stored in the node.**Next**: This contains the reference (or link) to the next node in the chain.

The starting point of the linked list is referred to as the **head**. The final element, or **tail**, of the linked list points to a null reference, indicating the end of the chain.

Here's a simple diagram to illustrate the concept:

`[Head | ] --> [Data | ] --> [Data | ] --> [Data | Null]`

In Python, we can define a simple Node class and a LinkedList class as follows:

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

class LinkedList:

def __init__(self):

self.head = Node()

The LinkedList class maintains a reference to the head node. Initially, this node does not contain any data.

Now, let's add functionality to add elements at the end (append) and display the list:

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

class LinkedList:

def __init__(self):

self.head = Node()

def append(self, data):

new_node = Node(data)

cur = self.head

while cur.next:

cur = cur.next

cur.next = new_node

def display(self):

elems = []

cur_node = self.head

while cur_node.next:

cur_node = cur_node.next

elems.append(cur_node.data)

print(elems)

Here's how we can create a linked list and add elements to it:

`my_list = LinkedList()`

my_list.append(1)

my_list.append(2)

my_list.append(3)

my_list.display() # Output: [1, 2, 3]

And that's the basic concept of a Linked List! In comparison to arrays, linked lists are dynamic and efficient in some operations such as insertion and deletion. However, they use more memory to store data due to the added storage needed for pointers and have slower access times for individual elements.

### 8.2.1 Other Types of Linked Lists

**Doubly Linked Lists**

In computer science, a linked list is a data structure that consists of a sequence of nodes. Each node contains an element of data and a link to the next node in the sequence. However, traversing a linked list from the end to the beginning is impossible without additional information.

That's where the doubly linked list comes in! In a doubly linked list, each node has a link to both the next node and the previous node. This allows for traversal in both directions, which can be useful in various applications. For instance, it can make it easier to manipulate sequences of data in both forward and backward directions.

However, a doubly linked list requires more memory than a singly linked list since it needs to store the extra links. Despite this drawback, doubly linked lists are widely used in various computer science applications and are an important concept to learn for aspiring programmers.

Example:

Each node in a doubly linked list contains an element and two links pointing to the previous and next node respectively.

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

self.prev = None

class DoublyLinkedList:

def __init__(self):

self.head = None

To add elements to the list, you'll need a method that handles the

and **prev**

pointers appropriately.**next**

2. **Circular Linked Lists**

A circular linked list is a type of linked list where the last node points back to the head node rather than to null. This forms a circular chain, which can be useful in various applications. For example, in an operating system's task scheduler, the data is typically cyclic, and a circular linked list can be used to manage the tasks in a round-robin fashion.

Additionally, circular linked lists can be used in computer graphics to create shapes with a circular boundary, such as arcs or circles. They can also be used in various data structures, such as stacks and queues, to implement algorithms more efficiently.

Despite their usefulness, circular linked lists can be more complex than regular linked lists, and they require careful management to avoid infinite loops and memory leaks. However, with proper implementation and use, circular linked lists can be a powerful tool in a programmer's arsenal.

Example:

In a circular linked list, the

of the last node points to the first node.**next**

`class Node:`

def __init__(self, data):

self.data = data

self.next = None

class CircularLinkedList:

def __init__(self):

self.head = None

def append(self, data):

if not self.head:

self.head = Node(data)

self.head.next = self.head

else:

new_node = Node(data)

cur = self.head

while cur.next != self.head:

cur = cur.next

cur.next = new_node

new_node.next = self.head

In this implementation, we create a new node. If the list is empty (

is **self.head**

), we point **None**

to the new node and make the **self.head**

of the new node point to itself. If the list is not empty, we traverse to the end of the list (where **next**

is **cur.next**

) and adjust the **self.head**

pointers of the last node and the new node to maintain the circular structure.**next**

## 8.2 Linked Lists

In a Linked List, each item is contained in a node, which has two elements:

**Data**: This contains the value to be stored in the node.**Next**: This contains the reference (or link) to the next node in the chain.

**head**. The final element, or **tail**, of the linked list points to a null reference, indicating the end of the chain.

Here's a simple diagram to illustrate the concept:

`[Head | ] --> [Data | ] --> [Data | ] --> [Data | Null]`

In Python, we can define a simple Node class and a LinkedList class as follows:

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

class LinkedList:

def __init__(self):

self.head = Node()

Now, let's add functionality to add elements at the end (append) and display the list:

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

class LinkedList:

def __init__(self):

self.head = Node()

def append(self, data):

new_node = Node(data)

cur = self.head

while cur.next:

cur = cur.next

cur.next = new_node

def display(self):

elems = []

cur_node = self.head

while cur_node.next:

cur_node = cur_node.next

elems.append(cur_node.data)

print(elems)

Here's how we can create a linked list and add elements to it:

`my_list = LinkedList()`

my_list.append(1)

my_list.append(2)

my_list.append(3)

my_list.display() # Output: [1, 2, 3]

### 8.2.1 Other Types of Linked Lists

**Doubly Linked Lists**

Example:

`class Node:`

def __init__(self, data=None):

self.data = data

self.next = None

self.prev = None

class DoublyLinkedList:

def __init__(self):

self.head = None

and **prev**

pointers appropriately.**next**

2. **Circular Linked Lists**

Example:

In a circular linked list, the

of the last node points to the first node.**next**

`class Node:`

def __init__(self, data):

self.data = data

self.next = None

class CircularLinkedList:

def __init__(self):

self.head = None

def append(self, data):

if not self.head:

self.head = Node(data)

self.head.next = self.head

else:

new_node = Node(data)

cur = self.head

while cur.next != self.head:

cur = cur.next

cur.next = new_node

new_node.next = self.head

is **self.head**

), we point **None**

to the new node and make the **self.head**

of the new node point to itself. If the list is not empty, we traverse to the end of the list (where **next**

is **cur.next**

) and adjust the **self.head**

pointers of the last node and the new node to maintain the circular structure.**next**