← Назад к курсу
Работа в Python с различными структурами данных
В Python типовые структуры данных можно реализовать как встроенными средствами, так и с помощью сторонних библиотек. Вот подробный разбор:
1. Связные списки (Linked Lists)
Односвязный список
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def append(self, data):
"""Добавление в конец"""
new_node = Node(data)
if not self.head:
self.head = new_node
return
current = self.head
while current.next:
current = current.next
current.next = new_node
def prepend(self, data):
"""Добавление в начало"""
new_node = Node(data)
new_node.next = self.head
self.head = new_node
def delete(self, key):
"""Удаление узла по значению"""
current = self.head
# Если удаляем голову
if current and current.data == key:
self.head = current.next
current = None
return
prev = None
while current and current.data != key:
prev = current
current = current.next
if current is None:
return
prev.next = current.next
current = None
def display(self):
"""Вывод списка"""
elements = []
current = self.head
while current:
elements.append(current.data)
current = current.next
return " -> ".join(map(str, elements))
# Использование
ll = LinkedList()
ll.append(1)
ll.append(2)
ll.prepend(0)
print(ll.display()) # 0 -> 1 -> 2
ll.delete(1)
print(ll.display()) # 0 -> 2
Двусвязный список
class DNode:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class DoublyLinkedList:
def __init__(self):
self.head = None
self.tail = None
def append(self, data):
new_node = DNode(data)
if not self.head:
self.head = self.tail = new_node
else:
self.tail.next = new_node
new_node.prev = self.tail
self.tail = new_node
2. Стеки (Stacks)
Реализация на списке
class Stack:
def __init__(self):
self.items = []
def push(self, item):
"""Добавление элемента"""
self.items.append(item)
def pop(self):
"""Удаление и возврат верхнего элемента"""
if not self.is_empty():
return self.items.pop()
return None
def peek(self):
"""Просмотр верхнего элемента без удаления"""
if not self.is_empty():
return self.items[-1]
return None
def is_empty(self):
"""Проверка на пустоту"""
return len(self.items) == 0
def size(self):
"""Размер стека"""
return len(self.items)
# Использование
stack = Stack()
stack.push(1)
stack.push(2)
print(stack.peek()) # 2
print(stack.pop()) # 2
print(stack.size()) # 1
3. Очереди (Queues)
Обычная очередь
from collections import deque
class Queue:
def __init__(self):
self.items = deque()
def enqueue(self, item):
"""Добавление в очередь"""
self.items.append(item)
def dequeue(self):
"""Удаление из очереди"""
if not self.is_empty():
return self.items.popleft()
return None
def is_empty(self):
return len(self.items) == 0
def size(self):
return len(self.items)
Круговая очередь (Circular Queue)
class CircularQueue:
def __init__(self, capacity):
self.capacity = capacity
self.queue = [None] * capacity
self.front = self.rear = -1
def enqueue(self, item):
if self.is_full():
raise Exception("Queue is full")
if self.is_empty():
self.front = self.rear = 0
else:
self.rear = (self.rear + 1) % self.capacity
self.queue[self.rear] = item
def dequeue(self):
if self.is_empty():
raise Exception("Queue is empty")
item = self.queue[self.front]
if self.front == self.rear:
self.front = self.rear = -1
else:
self.front = (self.front + 1) % self.capacity
return item
def is_empty(self):
return self.front == -1
def is_full(self):
return (self.rear + 1) % self.capacity == self.front
4. Деревья (Trees)
Бинарное дерево
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
class BinaryTree:
def __init__(self, root_value):
self.root = TreeNode(root_value)
def insert(self, value):
"""Вставка значения в дерево"""
self._insert_recursive(self.root, value)
def _insert_recursive(self, node, value):
if value < node.value:
if node.left is None:
node.left = TreeNode(value)
else:
self._insert_recursive(node.left, value)
else:
if node.right is None:
node.right = TreeNode(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)
# Обходы дерева
def inorder_traversal(self, node):
"""Центрированный обход (left-root-right)"""
if node:
self.inorder_traversal(node.left)
print(node.value, end=" ")
self.inorder_traversal(node.right)
def preorder_traversal(self, node):
"""Прямой обход (root-left-right)"""
if node:
print(node.value, end=" ")
self.preorder_traversal(node.left)
self.preorder_traversal(node.right)
5. Графы (Graphs)
Список смежности
from collections import defaultdict, deque
class Graph:
def __init__(self):
self.graph = defaultdict(list)
def add_edge(self, u, v, directed=False):
"""Добавление ребра между вершинами u и v"""
self.graph[u].append(v)
if not directed:
self.graph[v].append(u)
def bfs(self, start):
"""Поиск в ширину"""
visited = set()
queue = deque([start])
visited.add(start)
while queue:
vertex = queue.popleft()
print(vertex, end=" ")
for neighbor in self.graph[vertex]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
def dfs(self, start):
"""Поиск в глубину (итеративный)"""
visited = set()
stack = [start]
while stack:
vertex = stack.pop()
if vertex not in visited:
print(vertex, end=" ")
visited.add(vertex)
stack.extend(reversed(self.graph[vertex]))
def dfs_recursive(self, vertex, visited=None):
"""Поиск в глубину (рекурсивный)"""
if visited is None:
visited = set()
visited.add(vertex)
print(vertex, end=" ")
for neighbor in self.graph[vertex]:
if neighbor not in visited:
self.dfs_recursive(neighbor, visited)
# Использование
g = Graph()
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(2, 3)
print("BFS traversal:")
g.bfs(0) # 0 1 2 3
print("\nDFS traversal:")
g.dfs(0) # 0 1 2 3
Матрица смежности
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, u, v, weight=1, directed=False):
self.matrix[u][v] = weight
if not directed:
self.matrix[v][u] = weight
def display(self):
for row in self.matrix:
print(row)
6. Куча (Heap)
import heapq
# Встроенная реализация (min-heap)
heap = []
heapq.heappush(heap, 5)
heapq.heappush(heap, 3)
heapq.heappush(heap, 7)
print(heapq.heappop(heap)) # 3 (минимальный элемент)
# Кастомная реализация
class MinHeap:
def __init__(self):
self.heap = []
def parent(self, i):
return (i - 1) // 2
def left_child(self, i):
return 2 * i + 1
def right_child(self, i):
return 2 * i + 2
def insert(self, key):
self.heap.append(key)
self._heapify_up(len(self.heap) - 1)
def _heapify_up(self, i):
while i > 0 and self.heap[self.parent(i)] > self.heap[i]:
self.heap[i], self.heap[self.parent(i)] = \
self.heap[self.parent(i)], self.heap[i]
i = self.parent(i)
def extract_min(self):
if len(self.heap) == 0:
return None
root = self.heap[0]
self.heap[0] = self.heap[-1]
self.heap.pop()
self._heapify_down(0)
return root
def _heapify_down(self, i):
smallest = i
left = self.left_child(i)
right = self.right_child(i)
if left < len(self.heap) and self.heap[left] < self.heap[smallest]:
smallest = left
if right < len(self.heap) and self.heap[right] < self.heap[smallest]:
smallest = right
if smallest != i:
self.heap[i], self.heap[smallest] = self.heap[smallest], self.heap[i]
self._heapify_down(smallest)
7. Хеш-таблицы (Hash Tables)
class HashTable:
def __init__(self, size=10):
self.size = size
self.table = [[] for _ in range(size)]
def _hash_function(self, key):
return hash(key) % self.size
def insert(self, key, value):
hash_key = self._hash_function(key)
bucket = self.table[hash_key]
for i, (k, v) in enumerate(bucket):
if k == key:
bucket[i] = (key, value)
return
bucket.append((key, value))
def get(self, key):
hash_key = self._hash_function(key)
bucket = self.table[hash_key]
for k, v in bucket:
if k == key:
return v
raise KeyError(f"Key {key} not found")
def delete(self, key):
hash_key = self._hash_function(key)
bucket = self.table[hash_key]
for i, (k, v) in enumerate(bucket):
if k == key:
del bucket[i]
return
raise KeyError(f"Key {key} not found")
# Использование
ht = HashTable()
ht.insert("apple", 5)
ht.insert("banana", 7)
print(ht.get("apple")) # 5
ht.insert("apple", 10) # Обновление
print(ht.get("apple")) # 10
Основные принципы работы:
Для связанных списков:
- Динамическое выделение памяти - каждый узел создается отдельно
- Указатели/ссылки - узлы связаны через указатели на следующий/предыдущий элемент
- Вставка/удаление O(1) в начало/конец (если есть указатель на конец)
Для графов:
- Список смежности - эффективен для разреженных графов
- Матрица смежности - эффективна для плотных графов
- Обходы BFS/DFS - базовые алгоритмы для работы с графами
Для деревьев:
- Рекурсивная структура - каждое поддерево само является деревом
- Балансировка - важна для поддержания эффективности операций
- Обходы - разные порядки (in-order, pre-order, post-order) для разных задач
Общие рекомендации:
- Используйте встроенные коллекции (list, deque, heapq) когда возможно
- Реализуйте кастомные структуры только при необходимости специфического поведения
- Учитывайте временную и пространственную сложность операций
- Для продакшен-кода предпочитайте библиотеки типа networkx для графов