feat: solution for 'Повторный экзамен #2: Граф с рефлексией на код'
This commit is contained in:
@@ -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.
|
||||||
|
|
||||||
Главная
|
## Features
|
||||||
Мои задания
|
|
||||||
Повторный экзамен #2: Граф с рефлексией на код
|
|
||||||
5Д
|
|
||||||
EN
|
|
||||||
Повторный экзамен #2: Граф с рефлексией на код
|
|
||||||
Зачёт
|
|
||||||
Версия 5
|
|
||||||
Дедлайн сдачи: 31.08.2026
|
|
||||||
|
|
||||||
В работе
|
- **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
|
||||||
|
|
||||||
Тип ответа
|
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
@@ -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)
|
||||||
Reference in New Issue
Block a user