feat: solution for 'Повторный экзамен #2: Граф с рефлексией на код'

This commit is contained in:
2026-06-30 11:43:08 +03:00
parent 224adca90a
commit 898f43bf73
2 changed files with 191 additions and 78 deletions
+42 -78
View File
@@ -1,96 +1,60 @@
# Повторный экзамен #2: Граф с рефлексией на код # Graph with Reflection on Code Refactored Implementation
## Original assignment This repository contains a minimal, selfcontained 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 mixedstrategy code and provides a clean, welldocumented API.
Главная ## Features
Мои задания
Повторный экзамен #2: Граф с рефлексией на код
EN
Повторный экзамен #2: Граф с рефлексией на код
Зачёт
Версия 5
Дедлайн сдачи: 31.08.2026
В работе - **Add / remove nodes** Nodes are any hashable Python objects.
- **Add / remove edges** Undirected edges; selfloops (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
Тип ответа print(g) # Pretty print
Текст
Ссылка
Файлы
Ссылка (URL)
Прикреплённые файлы
Загрузить файл
Отправить на проверку
Отменить
ПОДРОБНЕЕ # Query
print("Neighbors of B:", g.neighbors("B"))
print("Has edge (A, D)?", g.has_edge("A", "D"))
Задание # Modify
Предыдущие версии g.remove_edge("A", "B")
ВЕРСИЯ 4 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 ## Running the Example
ВЕРСИЯ 3
30.06.2026, 00:23 ```bash
python -m src.index
```
https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-graf-s-refleksiey-na The script will output the graph state after each operation.
ВЕРСИЯ 2
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 This project is released under the MIT License.
На проверке
3
Завершено
Сводка
СТАТУС
В работе
ВЕРСИЯ
5
СОЗДАНО
23.06.2026, 14:49
ПОСЛЕДНЯЯ СДАЧА
ИЗМЕНЕНО
30.06.2026, 11:32
ТИП ЗАДАНИЯ
Индивидуальное
ЛЕКЦИЙ
Повторный экзамен #2 · 11.06.2026, 18:30
К списку заданий
+149
View File
@@ -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; selfloops (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 selfloops, 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)