Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
| 14 | `heaps` | Кучи | Продвинутые структуры данных | Реализация кучи (очереди с приоритетом) на массиве. Алгоритмы вставки, удаления и извлечения максимума. Сортировка кучей. Алгоритм Хаффмана. |
| 15 | `dsu` | Непересекающиеся множества | Продвинутые структуры данных | Реализация структуры данных "Непересекающиеся множества" (DSU/Union-Find). Алгоритмы объединения и поиска. Применение в задачах на компоненты связности. |
| 16 | `hash_tables` | Хеш-таблицы | Продвинутые структуры данных | Реализация хеш-таблицы на массиве. Алгоритмы вставки, удаления и поиска. Разрешение коллизий с помощью цепочек и открытой адресации. |
| 17 | `graphs` | Графы | Продвинутые структуры данных | Реализация графов с помощью списков смежности. Алгоритмы обхода DFS и BFS. Нахождение кратчайших путей (Dijkstra, Bellman-Ford). |
| 18 | `balanced_ds` | Сбалансированные структуры данных | Продвинутые структуры данных | Реализация АВЛ-дерева. Базовые операции. Балансировка, разбиение и склейка. Динамическое дерево отрезков. Структура Rope. |
| 17 | `graphs` | Графы | Продвинутые структуры данных | Реализация графов на списке смежности. ООП-представление графа. Алгоритмы обхода DFS и BFS. Нахождение кратчайших путей (Dijkstra, Bellman-Ford). |
| 18 | `balanced_ds` | Сбалансированные структуры данных | Продвинутые структуры данных | Реализация АВЛ-дерева. Базовые операции. Балансировка, разбиение и склеивание. Динамическое дерево отрезков. Структура Rope. |

---

Expand Down
1,007 changes: 925 additions & 82 deletions abstract/graphs.md

Large diffs are not rendered by default.

Empty file added src/graphs/oop/__init__.py
Empty file.
131 changes: 131 additions & 0 deletions src/graphs/oop/graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Generic, Self

from src.graphs.oop.models import Edge, EdgeData, K, Node, Weight
from src.graphs.oop.operations import (
bfs,
dfs,
dfs_iterative,
dijkstra,
dijkstra_heap,
find_path,
find_path_all,
find_shortest_path,
)

if TYPE_CHECKING:
from collections.abc import Iterable


class Graph(Generic[K]):
"""
ООП-представление графа.
"""

nodes: dict[K, Node[K]]
"""
Словарь узлов графа.
"""

def __init__(self, nodes: dict[K, Node[K]] | None = None) -> None:
self.nodes = nodes if nodes is not None else {}

@staticmethod
def edge_exists(node_from: Node[K], node_to: Node[K]) -> bool:
"""
Проверяет, есть ли ребро между двумя узлами.
"""
return node_to in node_from.parents

@staticmethod
def find_path(start: Node[K], end: Node[K]) -> list[K]:
"""
Находит путь между двумя узлами.
"""
return find_path(start, end)

@staticmethod
def find_path_all(start: Node[K], end: Node[K]) -> list[list[K]]:
"""
Находит все пути между двумя узлами.
"""
return find_path_all(start, end)

@staticmethod
def find_shortest_path(start: Node[K], end: Node[K]) -> list[K]:
"""
Находит кратчайший путь между двумя узлами.
"""
return find_shortest_path(start, end)

@classmethod
def from_edge_list(cls, edges: Iterable[EdgeData[K]]) -> Self:
"""
Создает граф из списка ребер.
"""
graph = cls()
for key_from, key_to, weight in edges:
node_from = graph.add(key_from) if not graph.contains(key_from) else graph.get(key_from)
node_to = graph.add(key_to) if not graph.contains(key_to) else graph.get(key_to)
graph.connect(node_from, node_to, weight)
return graph

def get(self, key: K) -> Node[K]:
"""
Возвращает узел по его ключу.
"""
return self.nodes[key]

def contains(self, key: K) -> bool:
"""
Проверяет, есть ли узел с таким ключом в графе.
"""
return key in self.nodes

def add(self, key: K) -> Node[K]:
"""
Добавляет узел в граф.
"""
if self.contains(key):
raise ValueError(f"Node with value {key} already exists")

node = Node(key)
self.nodes[key] = node

return node

def connect(self, node_from: Node[K], node_to: Node[K], weight: Weight) -> Edge:
"""
Добавляет ребро в граф.
"""
if self.edge_exists(node_from, node_to):
raise ValueError(f"Edge from {node_from.key} to {node_to.key} already exists")

edge = Edge(node_to, weight)
node_from.edges.append(edge)
node_to.parents[node_from] = edge

return edge

def dfs(self, node: Node[K] | None = None, *, iterative: bool = True) -> list[K]:
"""
Обход графа в глубину.
"""
nodes = self.nodes.values() if node is None else {node}
func = dfs_iterative if iterative else dfs
return func(nodes)

def bfs(self, node: Node[K] | None = None) -> list[K]:
"""
Обход графа в ширину.
"""
nodes = self.nodes.values() if node is None else {node}
return bfs(nodes)

def dijkstra(self, start: Node[K], end: Node[K], *, use_heap: bool = True) -> list[K]:
"""
Алгоритм Дейкстры.
"""
func = dijkstra_heap if use_heap else dijkstra
return func(self.nodes.values(), start, end)
61 changes: 61 additions & 0 deletions src/graphs/oop/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Generic, Self, TypeVar, cast

K = TypeVar("K")
NodeT = TypeVar("NodeT", bound="Node[Any]")
Weight = float
EdgeData = tuple[K, K, Weight]


@dataclass
class Node(Generic[K]):
"""
Узел графа.
"""

key: K
"""Значение узла."""
edges: list[Edge] = field(default_factory=list)
"""Список рёбер, ведущих из узла."""
parents: dict[Self, Edge] = field(default_factory=dict)
"""Список родителей."""

def __hash__(self) -> int:
return hash(self.key)

def __eq__(self, other: object) -> bool:
if not isinstance(other, Node):
return NotImplemented
return cast(bool, self.key == other.key)

def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.key})"


@dataclass
class Edge:
"""
Ребро графа.
"""

adjacent: Node[Any]
"""Узел, на который ведёт ребро."""
weight: Weight
"""Вес ребра."""


@dataclass
class PathNode:
"""
Узел пути.
"""

node: Node[Any]
"""Узел."""
parent: Self | None
"""Родительский узел."""

def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.node})"
Loading
Loading