feat: solution for 'Повторный экзамен: Граф с рефлексией и доработкой'
This commit is contained in:
+136
-2
@@ -1,2 +1,136 @@
|
||||
export { Graph } from './graph.js';
|
||||
export { Node, ReflectionNode, RewritingNode } from './nodes.js';
|
||||
const { strict: assert } = require('assert');
|
||||
|
||||
class Node {
|
||||
constructor(id, type = 'generic') {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
class ReflectionNode extends Node {
|
||||
constructor(id) {
|
||||
super(id, 'reflection');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflects all outgoing edges of this node by creating copies of the target nodes.
|
||||
* @param {Graph} graph - The graph instance to operate on.
|
||||
*/
|
||||
reflect(graph) {
|
||||
const targets = graph.edges.get(this.id) || new Set();
|
||||
for (const targetId of targets) {
|
||||
const targetNode = graph.getNode(targetId);
|
||||
if (!targetNode) continue;
|
||||
const newId = `${targetId}_ref`;
|
||||
// Avoid duplicate reflection
|
||||
if (graph.getNode(newId)) continue;
|
||||
const newNode = new Node(newId, targetNode.type);
|
||||
graph.addNode(newNode);
|
||||
const targetTargets = graph.edges.get(targetId) || new Set();
|
||||
for (const tt of targetTargets) {
|
||||
graph.addEdge(newId, tt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RewritingNode extends Node {
|
||||
constructor(id) {
|
||||
super(id, 'rewriting');
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites a target node in the graph with a new node.
|
||||
* @param {Graph} graph - The graph instance to operate on.
|
||||
* @param {string} targetId - The id of the node to replace.
|
||||
* @param {Node} newNode - The new node that will replace the target.
|
||||
*/
|
||||
rewrite(graph, targetId, newNode) {
|
||||
graph.replaceNode(targetId, newNode);
|
||||
}
|
||||
}
|
||||
|
||||
class Graph {
|
||||
constructor() {
|
||||
this.nodes = new Map(); // id -> Node
|
||||
this.edges = new Map(); // id -> Set of target ids
|
||||
}
|
||||
|
||||
addNode(node) {
|
||||
assert(node && node.id, 'Node must have an id');
|
||||
this.nodes.set(node.id, node);
|
||||
if (!this.edges.has(node.id)) {
|
||||
this.edges.set(node.id, new Set());
|
||||
}
|
||||
}
|
||||
|
||||
addEdge(fromId, toId) {
|
||||
assert(this.nodes.has(fromId), `Source node ${fromId} does not exist`);
|
||||
assert(this.nodes.has(toId), `Target node ${toId} does not exist`);
|
||||
if (!this.edges.has(fromId)) {
|
||||
this.edges.set(fromId, new Set());
|
||||
}
|
||||
this.edges.get(fromId).add(toId);
|
||||
}
|
||||
|
||||
getNode(id) {
|
||||
return this.nodes.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces an existing node with a new node, preserving edges.
|
||||
* @param {string} oldId - The id of the node to replace.
|
||||
* @param {Node} newNode - The new node that will replace the old one.
|
||||
*/
|
||||
replaceNode(oldId, newNode) {
|
||||
if (!this.nodes.has(oldId)) {
|
||||
throw new Error(`Node ${oldId} not found`);
|
||||
}
|
||||
const oldTargets = this.edges.get(oldId) ? new Set(this.edges.get(oldId)) : new Set();
|
||||
|
||||
// Remove old node and its edges
|
||||
this.edges.delete(oldId);
|
||||
this.nodes.delete(oldId);
|
||||
|
||||
// Add new node
|
||||
this.addNode(newNode);
|
||||
|
||||
// Rewire edges from other nodes that pointed to oldId
|
||||
for (const [from, targets] of this.edges.entries()) {
|
||||
if (targets.has(oldId)) {
|
||||
targets.delete(oldId);
|
||||
targets.add(newNode.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Add edges from new node to oldTargets
|
||||
for (const target of oldTargets) {
|
||||
this.addEdge(newNode.id, target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Depth-first traversal starting from a node.
|
||||
* @param {string} startId - The starting node id.
|
||||
* @param {Set<string>} visited - Internal set to track visited nodes.
|
||||
* @returns {string[]} - Array of visited node ids in traversal order.
|
||||
*/
|
||||
traverse(startId, visited = new Set()) {
|
||||
if (!this.nodes.has(startId)) return [];
|
||||
if (visited.has(startId)) return [];
|
||||
visited.add(startId);
|
||||
const result = [startId];
|
||||
const targets = this.edges.get(startId) || new Set();
|
||||
for (const t of targets) {
|
||||
result.push(...this.traverse(t, visited));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Node,
|
||||
ReflectionNode,
|
||||
RewritingNode,
|
||||
Graph,
|
||||
};
|
||||
Reference in New Issue
Block a user