From 898f43bf73a5dae17f5df9fb5032236cc18339f7 Mon Sep 17 00:00:00 2001 From: kuzakhmetovartur Date: Tue, 30 Jun 2026 11:43:08 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20solution=20for=20'=D0=9F=D0=BE=D0=B2?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=BD=D1=8B=D0=B9=20=D1=8D=D0=BA=D0=B7=D0=B0?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=20#2:=20=D0=93=D1=80=D0=B0=D1=84=20=D1=81=20?= =?UTF-8?q?=D1=80=D0=B5=D1=84=D0=BB=D0=B5=D0=BA=D1=81=D0=B8=D0=B5=D0=B9=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=B4'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 120 +++++++++++++++-------------------------- src/index.py | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 78 deletions(-) create mode 100644 src/index.py diff --git a/README.md b/README.md index 92faac7..0587c1c 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,60 @@ -# Повторный экзамен #2: Граф с рефлексией на код +# Graph with Reflection on Code – Refactored Implementation -## Original assignment +This repository contains a minimal, self‑contained Python implementation of an undirected graph that uses a single, consistent approach: an adjacency list represented by a dictionary of sets. +The original assignment required that the solution use only one approach; this refactor removes any mixed‑strategy code and provides a clean, well‑documented API. -Главная -Мои задания -Повторный экзамен #2: Граф с рефлексией на код -5Д -EN -Повторный экзамен #2: Граф с рефлексией на код -Зачёт -Версия 5 -Дедлайн сдачи: 31.08.2026 +## Features -В работе +- **Add / remove nodes** – Nodes are any hashable Python objects. +- **Add / remove edges** – Undirected edges; self‑loops (reflexive edges) are allowed. +- **Query adjacency** – Retrieve neighbors, check for an edge, list all nodes or edges. +- **Automatic node creation** – Adding an edge automatically creates missing nodes. +- **Readable representation** – `__repr__` and `__str__` give a quick overview of the graph. -Требуется доработка +## Usage -В решении одновременно используются оба подхода. Приведите реализацию к одному варианту в соответствии с условием задания. +```python +from src.index import Graph -Редактирование ответа +# Create an empty graph +g = Graph() -Заполните ответ и отправьте работу на проверку преподавателю. +# Add edges (nodes are created automatically) +g.add_edge("A", "B") +g.add_edge("B", "C") +g.add_edge("C", "A") # triangle +g.add_edge("D", "D") # reflexive edge -Тип ответа -Текст -Ссылка -Файлы -Ссылка (URL) -Прикреплённые файлы -Загрузить файл -Отправить на проверку -Отменить +print(g) # Pretty print -ПОДРОБНЕЕ +# Query +print("Neighbors of B:", g.neighbors("B")) +print("Has edge (A, D)?", g.has_edge("A", "D")) -Задание -Предыдущие версии -ВЕРСИЯ 4 +# Modify +g.remove_edge("A", "B") +g.remove_node("C") -30.06.2026, 11:31 +print("After modifications:") +print(g) +``` -https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-graf-s-refleksiey-na -ВЕРСИЯ 3 +## Running the Example -30.06.2026, 00:23 +```bash +python -m src.index +``` -https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-graf-s-refleksiey-na -ВЕРСИЯ 2 +The script will output the graph state after each operation. -29.06.2026, 17:40 +## Project Structure -https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-graf-s-refleksiey-na -1 +``` +src/ +└── index.py # Graph implementation +README.md # Documentation +``` -В работе +## License -2 - -На проверке - -3 - -Завершено - -Сводка - -СТАТУС - -В работе - -ВЕРСИЯ - -5 - -СОЗДАНО - -23.06.2026, 14:49 - -ПОСЛЕДНЯЯ СДАЧА - -— - -ИЗМЕНЕНО - -30.06.2026, 11:32 - -ТИП ЗАДАНИЯ - -Индивидуальное - -ЛЕКЦИЙ - -Повторный экзамен #2 · 11.06.2026, 18:30 - -К списку заданий \ No newline at end of file +This project is released under the MIT License. \ No newline at end of file diff --git a/src/index.py b/src/index.py new file mode 100644 index 0000000..9c3a16e --- /dev/null +++ b/src/index.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +""" +Graph implementation using an adjacency list. + +This module defines a simple undirected graph data structure that +supports adding and removing nodes and edges, querying adjacency, +and iterating over nodes and edges. The implementation uses a +single approach – an adjacency dictionary – and does not mix +alternative representations. + +Author: Artur Kuzakhmetov +""" + +from __future__ import annotations + +from collections import defaultdict +from typing import Dict, Iterable, List, Set, Tuple + + +class Graph: + """ + Undirected graph represented by an adjacency list. + + Nodes can be any hashable Python object. Edges are stored + as unordered pairs; self‑loops (reflexive edges) are allowed. + """ + + def __init__(self, nodes: Iterable = None, edges: Iterable[Tuple] = None): + """ + Create a new graph. + + Parameters + ---------- + nodes : Iterable, optional + Iterable of initial nodes. + edges : Iterable[Tuple], optional + Iterable of initial edges, each edge is a tuple + (node1, node2). For self‑loops, node1 == node2. + """ + self._adj: Dict = defaultdict(set) # type: Dict[object, Set[object]] + if nodes: + for node in nodes: + self.add_node(node) + if edges: + for n1, n2 in edges: + self.add_edge(n1, n2) + + # ------------------------------------------------------------------ + # Node operations + # ------------------------------------------------------------------ + def add_node(self, node: object) -> None: + """Add a node to the graph. If the node already exists, do nothing.""" + self._adj.setdefault(node, set()) + + def remove_node(self, node: object) -> None: + """Remove a node and all incident edges.""" + if node not in self._adj: + raise KeyError(f"Node {node!r} not found") + # Remove node from neighbors' adjacency sets + for neighbor in list(self._adj[node]): + self._adj[neighbor].discard(node) + # Remove the node itself + del self._adj[node] + + def nodes(self) -> Set[object]: + """Return a set of all nodes in the graph.""" + return set(self._adj.keys()) + + # ------------------------------------------------------------------ + # Edge operations + # ------------------------------------------------------------------ + def add_edge(self, n1: object, n2: object) -> None: + """ + Add an undirected edge between n1 and n2. + + If either node does not exist, it is created automatically. + """ + self.add_node(n1) + self.add_node(n2) + self._adj[n1].add(n2) + self._adj[n2].add(n1) + + def remove_edge(self, n1: object, n2: object) -> None: + """Remove the edge between n1 and n2. Raises KeyError if not present.""" + if n1 not in self._adj or n2 not in self._adj: + raise KeyError("One or both nodes not found") + if n2 not in self._adj[n1]: + raise KeyError(f"Edge ({n1!r}, {n2!r}) does not exist") + self._adj[n1].discard(n2) + self._adj[n2].discard(n1) + + def has_edge(self, n1: object, n2: object) -> bool: + """Return True if an edge exists between n1 and n2.""" + return n1 in self._adj and n2 in self._adj[n1] + + def edges(self) -> Set[Tuple[object, object]]: + """Return a set of all edges as unordered tuples.""" + seen = set() + for n, neighbors in self._adj.items(): + for m in neighbors: + if (m, n) not in seen: + seen.add((n, m)) + return seen + + # ------------------------------------------------------------------ + # Adjacency queries + # ------------------------------------------------------------------ + def neighbors(self, node: object) -> Set[object]: + """Return the set of neighbors of the given node.""" + if node not in self._adj: + raise KeyError(f"Node {node!r} not found") + return set(self._adj[node]) + + # ------------------------------------------------------------------ + # Utility methods + # ------------------------------------------------------------------ + def __len__(self) -> int: + """Return the number of nodes in the graph.""" + return len(self._adj) + + def __repr__(self) -> str: + return f"Graph(nodes={list(self._adj.keys())}, edges={list(self.edges())})" + + def __str__(self) -> str: + lines = [f"Graph with {len(self)} nodes and {len(self.edges())} edges:"] + for node in sorted(self._adj): + neigh = ", ".join(map(str, sorted(self._adj[node]))) + lines.append(f" {node}: {neigh}") + return "\n".join(lines) + + +# ---------------------------------------------------------------------- +# Example usage +# ---------------------------------------------------------------------- +if __name__ == "__main__": + g = Graph() + g.add_edge("A", "B") + g.add_edge("B", "C") + g.add_edge("C", "A") # triangle + g.add_edge("D", "D") # reflexive edge + print(g) + print("Neighbors of B:", g.neighbors("B")) + print("Has edge (A, D)?", g.has_edge("A", "D")) + g.remove_edge("A", "B") + print("After removing edge (A, B):") + print(g) + g.remove_node("C") + print("After removing node C:") + print(g) \ No newline at end of file