Time complexity is a measure of the amount of time an algorithm takes to run as a function of the size of the input. For example, an algorithm with a time complexity of $O(n)$ means that the running time grows linearly with the size of the input n
. By choosing data structures with lower time complexity for common operations, we can speed up our Python code.
Space complexity refers to the amount of memory an algorithm or data structure requires as a function of the input size. Sometimes, we may need to make a trade - off between time and space complexity.
A hash table is a data structure that stores key - value pairs. It uses a hash function to compute an index into an array of buckets, where the value associated with the key is stored.
In Python, dictionaries are implemented as hash tables.
# Create a dictionary
student_grades = {'Alice': 85, 'Bob': 90, 'Charlie': 78}
# Access a value
print(student_grades['Bob'])
# Add a new key - value pair
student_grades['David'] = 88
# Time complexity: O(1) for access, insertion, and deletion on average
A heap is a complete binary tree that satisfies the heap property. In a min - heap, the value of each node is less than or equal to the values of its children.
Python’s heapq
module provides an implementation of the heap queue algorithm.
import heapq
# Create a list
numbers = [5, 3, 8, 1, 2]
# Convert the list into a heap
heapq.heapify(numbers)
# Add an element to the heap
heapq.heappush(numbers, 4)
# Remove and return the smallest element
smallest = heapq.heappop(numbers)
print(smallest)
# Time complexity: O(log n) for insertion and deletion
A trie, also known as a prefix tree, is a tree - like data structure used to store a dynamic set of strings. Each node in the trie represents a single character, and the path from the root to a node represents a prefix.
class TrieNode:
def __init__(self):
self.children = {}
self.is_end_of_word = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_end_of_word = True
def search(self, word):
node = self.root
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.is_end_of_word
trie = Trie()
trie.insert("apple")
print(trie.search("apple"))
print(trie.search("app"))
# Time complexity: O(m) for insertion and search, where m is the length of the word
cProfile
to identify performance bottlenecks in your code. This will help you determine which parts of your code need optimization.import cProfile
def slow_function():
result = []
for i in range(10000):
result.append(i * i)
return result
cProfile.run('slow_function()')
Advanced data structures are powerful tools for accelerating Python code. By understanding the fundamental concepts, choosing the right data structures for the task at hand, and following common and best practices, you can significantly improve the performance of your Python programs. Whether it’s using hash tables for fast lookups, heaps for priority queue operations, or tries for efficient string processing, each data structure has its own strengths and use cases.