Complex data structures are data structures that are built on top of basic data types and provide more advanced functionality. They are designed to handle large amounts of data efficiently and solve complex problems. Examples of complex data structures include trees, graphs, heaps, and hash tables.
A binary tree is a tree data structure in which each node has at most two children, referred to as the left child and the right child.
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
# Create a simple binary tree
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
A binary search tree (BST) is a binary tree where for each node, all nodes in its left subtree have values less than the node’s value, and all nodes in its right subtree have values greater than the node’s value.
class BSTNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
class BinarySearchTree:
def __init__(self):
self.root = None
def insert(self, value):
if self.root is None:
self.root = BSTNode(value)
else:
self._insert_recursive(self.root, value)
def _insert_recursive(self, node, value):
if value < node.value:
if node.left is None:
node.left = BSTNode(value)
else:
self._insert_recursive(node.left, value)
else:
if node.right is None:
node.right = BSTNode(value)
else:
self._insert_recursive(node.right, value)
def search(self, value):
return self._search_recursive(self.root, value)
def _search_recursive(self, node, value):
if node is None or node.value == value:
return node
if value < node.value:
return self._search_recursive(node.left, value)
return self._search_recursive(node.right, value)
bst = BinarySearchTree()
bst.insert(5)
bst.insert(3)
bst.insert(7)
result = bst.search(3)
print(result.value if result else None)
An adjacency list is a collection of unordered lists used to represent a finite graph. Each list within the adjacency list stores the neighbors of a particular vertex.
class Graph:
def __init__(self):
self.adj_list = {}
def add_vertex(self, vertex):
if vertex not in self.adj_list:
self.adj_list[vertex] = []
def add_edge(self, vertex1, vertex2):
if vertex1 in self.adj_list and vertex2 in self.adj_list:
self.adj_list[vertex1].append(vertex2)
self.adj_list[vertex2].append(vertex1)
def get_neighbors(self, vertex):
return self.adj_list.get(vertex, [])
g = Graph()
g.add_vertex(1)
g.add_vertex(2)
g.add_edge(1, 2)
print(g.get_neighbors(1))
An adjacency matrix is a square matrix used to represent a finite graph. The elements of the matrix indicate whether pairs of vertices are adjacent or not in the graph.
class GraphMatrix:
def __init__(self, num_vertices):
self.num_vertices = num_vertices
self.matrix = [[0] * num_vertices for _ in range(num_vertices)]
def add_edge(self, vertex1, vertex2):
if 0 <= vertex1 < self.num_vertices and 0 <= vertex2 < self.num_vertices:
self.matrix[vertex1][vertex2] = 1
self.matrix[vertex2][vertex1] = 1
def get_neighbors(self, vertex):
neighbors = []
for i in range(self.num_vertices):
if self.matrix[vertex][i] == 1:
neighbors.append(i)
return neighbors
gm = GraphMatrix(3)
gm.add_edge(0, 1)
print(gm.get_neighbors(0))
A min - heap is a complete binary tree where the value of each node is less than or equal to the values of its children.
import heapq
min_heap = []
heapq.heappush(min_heap, 3)
heapq.heappush(min_heap, 1)
heapq.heappush(min_heap, 2)
print(heapq.heappop(min_heap))
To implement a max - heap in Python, we can use the built - in heapq
module by negating the values.
import heapq
max_heap = []
heapq.heappush(max_heap, -3)
heapq.heappush(max_heap, -1)
heapq.heappush(max_heap, -2)
print(-heapq.heappop(max_heap))
heapq
for heaps and networkx
for graphs. Using these libraries can save development time and ensure the correctness of the implementation.In this blog, we have explored how to implement complex data structures such as trees, graphs, and heaps in Python. We started with the fundamental concepts of complex data structures and then dived into the implementation details of each data structure. We also discussed common practices and best practices to follow when working with these data structures. By understanding and implementing these complex data structures, you can solve a wide range of complex problems more efficiently in Python.