diff --git a/graphs_trees/graph/graph.py b/graphs_trees/graph/graph.py index 1fad795..d7e3e96 100644 --- a/graphs_trees/graph/graph.py +++ b/graphs_trees/graph/graph.py @@ -9,35 +9,63 @@ class State(Enum): class Node: - def __init__(self, id): - self.id = id + def __init__(self, key): + self.key = key self.visit_state = State.unvisited - self.adjacent = {} # key = node, val = weight + self.incoming_edges = 0 + self.adj_nodes = {} # Key = key, val = Node + self.adj_weights = {} # Key = key, val = weight - def __str__(self): - return str(self.id) + def __repr__(self): + return str(self.key) + + def __lt__(self, other): + return self.key < other.key def add_neighbor(self, neighbor, weight=0): - self.adjacent[neighbor] = weight + if neighbor is None: + raise Exception('Invalid neighbor') + neighbor.incoming_edges += 1 + self.adj_weights[neighbor.key] = weight + self.adj_nodes[neighbor.key] = neighbor + + def remove_neighbor(self, neighbor): + if neighbor is None: + raise Exception('Invalid neighbor') + if neighbor.key in self.adj_nodes: + neighbor.incoming_edges -= 1 + del self.adj_weights[neighbor.key] + del self.adj_nodes[neighbor.key] + else: + raise Exception('Invalid neighbor') class Graph: def __init__(self): - self.nodes = {} # key = node id, val = node + self.nodes = {} # Key = key, val = Node - def add_node(self, id): - node = Node(id) - self.nodes[id] = node - return node + def add_node(self, key): + if key is None: + raise Exception('Invalid key') + if key in self.nodes: + return self.nodes[key] + self.nodes[key] = Node(key) + return self.nodes[key] - def add_edge(self, id_source, id_dest, weight=0): - if id_source not in self.nodes: - self.add_node(id_source) - if id_dest not in self.nodes: - self.add_node(id_dest) - self.nodes[id_source].add_neighbor(self.nodes[id_dest], weight) + def add_edge(self, source_key, dest_key, weight=0): + if source_key is None or dest_key is None: + raise Exception('Invalid key') + if source_key not in self.nodes: + self.add_node(source_key) + if dest_key not in self.nodes: + self.add_node(dest_key) + self.nodes[source_key].add_neighbor(self.nodes[dest_key], + weight) - def add_undirected_edge(self, source, dest, weight=0): - self.add_edge(source, dest, weight) - self.nodes[dest].add_neighbor(self.nodes[source], weight) \ No newline at end of file + def add_undirected_edge(self, source_key, dest_key, weight=0): + if source_key is None or dest_key is None: + raise Exception('Invalid key') + self.add_edge(source_key, dest_key, weight) + self.nodes[dest_key].add_neighbor(self.nodes[source_key], + weight) \ No newline at end of file diff --git a/graphs_trees/graph/graph_challenge.ipynb b/graphs_trees/graph/graph_challenge.ipynb index 2e9f767..cbaa31c 100644 --- a/graphs_trees/graph/graph_challenge.ipynb +++ b/graphs_trees/graph/graph_challenge.ipynb @@ -39,6 +39,12 @@ "* Do the edges have weights?\n", " * Yes\n", "* Can we assume the inputs are valid?\n", + " * Yes\n", + "* If we try to add a node that already exists, do we just do nothing?\n", + " * Yes\n", + "* If we try to delete a node that doesn't exist, do we just do nothing?\n", + " * Yes\n", + "* Can we assume this fits memory?\n", " * Yes" ] }, @@ -94,24 +100,43 @@ }, "outputs": [], "source": [ + "from enum import Enum # Python 2 users: Run pip install enum34\n", + "\n", + "\n", + "class State(Enum):\n", + " unvisited = 0\n", + " visiting = 1\n", + " visited = 2\n", + "\n", + "\n", "class Node:\n", "\n", - " def __init__(self, id):\n", - " # TODO: Implement me\n", - " self.adjacent = {} # key = node, val = weight\n", + " def __init__(self, key):\n", + " self.key = key\n", + " self.visit_state = State.unvisited\n", + " self.incoming_edges = 0\n", + " self.adj_nodes = {} # Key = key, val = Node\n", + " self.adj_weights = {} # Key = Node, val = weight\n", "\n", - " def __str__(self):\n", + " def __repr__(self):\n", " return str(self.id)\n", "\n", + " def __lt__(self, left, right):\n", + " return left.id < right.id\n", + "\n", " def add_neighbor(self, neighbor, weight=0):\n", " # TODO: Implement me\n", " pass\n", "\n", + " def remove_neighbor(self, neighbor):\n", + " # TODO: Implement me\n", + " pass\n", + "\n", + "\n", "class Graph:\n", "\n", " def __init__(self):\n", - " # TODO: Implement me\n", - " self.nodes = {} # key = node id, val = node\n", + " self.nodes = {} # Key = key, val = Node\n", "\n", " def add_node(self, id):\n", " # TODO: Implement me\n", @@ -156,13 +181,12 @@ "\n", " def create_graph(self):\n", " graph = Graph()\n", - " for id in range(0, 6):\n", - " graph.add_node(id)\n", + " for key in range(0, 6):\n", + " graph.add_node(key)\n", " return graph\n", "\n", " def test_graph(self):\n", " graph = self.create_graph()\n", - "\n", " graph.add_edge(0, 1, weight=5)\n", " graph.add_edge(0, 5, weight=2)\n", " graph.add_edge(1, 2, weight=3)\n", @@ -173,33 +197,46 @@ " graph.add_edge(5, 4, weight=8)\n", " graph.add_edge(5, 2, weight=9)\n", "\n", - " assert_equal(graph.nodes[0].adjacent[graph.nodes[1]], 5)\n", - " assert_equal(graph.nodes[0].adjacent[graph.nodes[5]], 2)\n", - " assert_equal(graph.nodes[1].adjacent[graph.nodes[2]], 3)\n", - " assert_equal(graph.nodes[2].adjacent[graph.nodes[3]], 4)\n", - " assert_equal(graph.nodes[3].adjacent[graph.nodes[4]], 5)\n", - " assert_equal(graph.nodes[3].adjacent[graph.nodes[5]], 6)\n", - " assert_equal(graph.nodes[4].adjacent[graph.nodes[0]], 7)\n", - " assert_equal(graph.nodes[5].adjacent[graph.nodes[4]], 8)\n", - " assert_equal(graph.nodes[5].adjacent[graph.nodes[2]], 9)\n", + " assert_equal(graph.nodes[0].adj_weights[graph.nodes[1].key], 5)\n", + " assert_equal(graph.nodes[0].adj_weights[graph.nodes[5].key], 2)\n", + " assert_equal(graph.nodes[1].adj_weights[graph.nodes[2].key], 3)\n", + " assert_equal(graph.nodes[2].adj_weights[graph.nodes[3].key], 4)\n", + " assert_equal(graph.nodes[3].adj_weights[graph.nodes[4].key], 5)\n", + " assert_equal(graph.nodes[3].adj_weights[graph.nodes[5].key], 6)\n", + " assert_equal(graph.nodes[4].adj_weights[graph.nodes[0].key], 7)\n", + " assert_equal(graph.nodes[5].adj_weights[graph.nodes[4].key], 8)\n", + " assert_equal(graph.nodes[5].adj_weights[graph.nodes[2].key], 9)\n", + "\n", + " assert_equal(graph.nodes[0].incoming_edges, 1)\n", + " assert_equal(graph.nodes[1].incoming_edges, 1)\n", + " assert_equal(graph.nodes[2].incoming_edges, 2)\n", + " assert_equal(graph.nodes[3].incoming_edges, 1)\n", + " assert_equal(graph.nodes[4].incoming_edges, 2)\n", + " assert_equal(graph.nodes[5].incoming_edges, 2)\n", + "\n", + " graph.nodes[0].remove_neighbor(graph.nodes[1])\n", + " assert_equal(graph.nodes[1].incoming_edges, 0)\n", + " graph.nodes[3].remove_neighbor(graph.nodes[4])\n", + " assert_equal(graph.nodes[4].incoming_edges, 1)\n", + "\n", + " assert_equal(graph.nodes[0] < graph.nodes[1], True)\n", "\n", " print('Success: test_graph')\n", "\n", " def test_graph_undirected(self):\n", " graph = self.create_graph()\n", - "\n", " graph.add_undirected_edge(0, 1, weight=5)\n", " graph.add_undirected_edge(0, 5, weight=2)\n", " graph.add_undirected_edge(1, 2, weight=3)\n", "\n", - " assert_equal(graph.nodes[0].adjacent[graph.nodes[1]], 5)\n", - " assert_equal(graph.nodes[1].adjacent[graph.nodes[0]], 5)\n", - " assert_equal(graph.nodes[0].adjacent[graph.nodes[5]], 2)\n", - " assert_equal(graph.nodes[5].adjacent[graph.nodes[0]], 2)\n", - " assert_equal(graph.nodes[1].adjacent[graph.nodes[2]], 3)\n", - " assert_equal(graph.nodes[2].adjacent[graph.nodes[1]], 3)\n", + " assert_equal(graph.nodes[0].adj_weights[graph.nodes[1].key], 5)\n", + " assert_equal(graph.nodes[1].adj_weights[graph.nodes[0].key], 5)\n", + " assert_equal(graph.nodes[0].adj_weights[graph.nodes[5].key], 2)\n", + " assert_equal(graph.nodes[5].adj_weights[graph.nodes[0].key], 2)\n", + " assert_equal(graph.nodes[1].adj_weights[graph.nodes[2].key], 3)\n", + " assert_equal(graph.nodes[2].adj_weights[graph.nodes[1].key], 3)\n", "\n", - " print('Success: test_graph')\n", + " print('Success: test_graph_undirected')\n", "\n", "\n", "def main():\n", diff --git a/graphs_trees/graph/graph_solution.ipynb b/graphs_trees/graph/graph_solution.ipynb index b113d03..e5b982b 100644 --- a/graphs_trees/graph/graph_solution.ipynb +++ b/graphs_trees/graph/graph_solution.ipynb @@ -37,7 +37,11 @@ " * Implement both\n", "* Do the edges have weights?\n", " * Yes\n", - "* Can we assume the inputs are valid?\n", + "* If we try to add a node that already exists, do we just do nothing?\n", + " * Yes\n", + "* If we try to delete a node that doesn't exist, do we just do nothing?\n", + " * Yes\n", + "* Can we assume this fits memory?\n", " * Yes" ] }, @@ -80,13 +84,24 @@ "Node will keep track of its:\n", "* id\n", "* visit state\n", - "* adjacent\n", - " * key: node\n", - " * value: weight\n", + "* incoming edge count (useful for algorithms such as topological sort)\n", + "* adjacent nodes and edge weights\n", "\n", "#### add_neighhbor\n", "\n", - "* Add the neighbor as a key and the weight as the value to `adjacent`\n", + "* If the neighbor doesn't already exist as an adjacent node\n", + " * Update the adjancet nodes and edge weights\n", + " * Increment the neighbor's incoming edge count\n", + "\n", + "Complexity:\n", + "* Time: O(1)\n", + "* Space: O(1)\n", + "\n", + "#### remove_neighhbor\n", + "\n", + "* If the neighbor exists as an adjacent node\n", + " * Decrement the neighbor's incoming edge count\n", + " * Remove the neighbor as an adjacent node\n", "\n", "Complexity:\n", "* Time: O(1)\n", @@ -96,13 +111,12 @@ "\n", "Graph will keep track of its:\n", "* nodes\n", - " * key: node id\n", - " * value: node\n", "\n", "#### add_node\n", "\n", - "* Create a node with the input id\n", - "* Add the newly created node to the list of nodes\n", + "* If node already exists, return it\n", + "* Create a node with the given id\n", + "* Add the newly created node to the collection of nodes\n", "\n", "Complexity:\n", "* Time: O(1)\n", @@ -110,10 +124,15 @@ "\n", "#### add_edge\n", "\n", - "* If the source node is not in the list of nodes, add it\n", - "* If the dest node is not in the list of nodes, add it\n", + "* If the source node is not in the collection of nodes, add it\n", + "* If the dest node is not in the collection of nodes, add it\n", "* Add a connection from the source node to the dest node with the given edge weight\n", "\n", + "#### add_undirected_edge\n", + "\n", + "* Call add_edge\n", + "* Also add a connection from the dest node to the source node with the given edge weight\n", + "\n", "Complexity:\n", "* Time: O(1)\n", "* Space: O(1)" @@ -154,38 +173,66 @@ "\n", "class Node:\n", "\n", - " def __init__(self, id):\n", - " self.id = id\n", + " def __init__(self, key):\n", + " self.key = key\n", " self.visit_state = State.unvisited\n", - " self.adjacent = {} # key = node, val = weight\n", + " self.incoming_edges = 0\n", + " self.adj_nodes = {} # Key = key, val = Node\n", + " self.adj_weights = {} # Key = key, val = weight\n", "\n", - " def __str__(self):\n", - " return str(self.id)\n", + " def __repr__(self):\n", + " return str(self.key)\n", + "\n", + " def __lt__(self, other):\n", + " return self.key < other.key\n", "\n", " def add_neighbor(self, neighbor, weight=0):\n", - " self.adjacent[neighbor] = weight\n", + " if neighbor is None:\n", + " raise Exception('Invalid neighbor')\n", + " neighbor.incoming_edges += 1\n", + " self.adj_weights[neighbor.key] = weight\n", + " self.adj_nodes[neighbor.key] = neighbor\n", + "\n", + " def remove_neighbor(self, neighbor):\n", + " if neighbor is None:\n", + " raise Exception('Invalid neighbor')\n", + " if neighbor.key in self.adj_nodes:\n", + " neighbor.incoming_edges -= 1\n", + " del self.adj_weights[neighbor.key]\n", + " del self.adj_nodes[neighbor.key]\n", + " else:\n", + " raise Exception('Invalid neighbor')\n", "\n", "\n", "class Graph:\n", "\n", " def __init__(self):\n", - " self.nodes = {} # key = node id, val = node\n", + " self.nodes = {} # Key = key, val = Node\n", "\n", - " def add_node(self, id):\n", - " node = Node(id)\n", - " self.nodes[id] = node\n", - " return node\n", + " def add_node(self, key):\n", + " if key is None:\n", + " raise Exception('Invalid key')\n", + " if key in self.nodes:\n", + " return self.nodes[key]\n", + " self.nodes[key] = Node(key)\n", + " return self.nodes[key]\n", "\n", - " def add_edge(self, id_source, id_dest, weight=0):\n", - " if id_source not in self.nodes:\n", - " self.add_node(id_source)\n", - " if id_dest not in self.nodes:\n", - " self.add_node(id_dest)\n", - " self.nodes[id_source].add_neighbor(self.nodes[id_dest], weight)\n", + " def add_edge(self, source_key, dest_key, weight=0):\n", + " if source_key is None or dest_key is None:\n", + " raise Exception('Invalid key')\n", + " if source_key not in self.nodes:\n", + " self.add_node(source_key)\n", + " if dest_key not in self.nodes:\n", + " self.add_node(dest_key)\n", + " self.nodes[source_key].add_neighbor(self.nodes[dest_key],\n", + " weight)\n", "\n", - " def add_undirected_edge(self, source, dest, weight=0):\n", - " self.add_edge(source, dest, weight)\n", - " self.nodes[dest].add_neighbor(self.nodes[source], weight)" + " def add_undirected_edge(self, source_key, dest_key, weight=0):\n", + " if source_key is None or dest_key is None:\n", + " raise Exception('Invalid key')\n", + " self.add_edge(source_key, dest_key, weight)\n", + " self.nodes[dest_key].add_neighbor(self.nodes[source_key],\n", + " weight)" ] }, { @@ -230,13 +277,12 @@ "\n", " def create_graph(self):\n", " graph = Graph()\n", - " for id in range(0, 6):\n", - " graph.add_node(id)\n", + " for key in range(0, 6):\n", + " graph.add_node(key)\n", " return graph\n", "\n", " def test_graph(self):\n", " graph = self.create_graph()\n", - "\n", " graph.add_edge(0, 1, weight=5)\n", " graph.add_edge(0, 5, weight=2)\n", " graph.add_edge(1, 2, weight=3)\n", @@ -247,33 +293,46 @@ " graph.add_edge(5, 4, weight=8)\n", " graph.add_edge(5, 2, weight=9)\n", "\n", - " assert_equal(graph.nodes[0].adjacent[graph.nodes[1]], 5)\n", - " assert_equal(graph.nodes[0].adjacent[graph.nodes[5]], 2)\n", - " assert_equal(graph.nodes[1].adjacent[graph.nodes[2]], 3)\n", - " assert_equal(graph.nodes[2].adjacent[graph.nodes[3]], 4)\n", - " assert_equal(graph.nodes[3].adjacent[graph.nodes[4]], 5)\n", - " assert_equal(graph.nodes[3].adjacent[graph.nodes[5]], 6)\n", - " assert_equal(graph.nodes[4].adjacent[graph.nodes[0]], 7)\n", - " assert_equal(graph.nodes[5].adjacent[graph.nodes[4]], 8)\n", - " assert_equal(graph.nodes[5].adjacent[graph.nodes[2]], 9)\n", + " assert_equal(graph.nodes[0].adj_weights[graph.nodes[1].key], 5)\n", + " assert_equal(graph.nodes[0].adj_weights[graph.nodes[5].key], 2)\n", + " assert_equal(graph.nodes[1].adj_weights[graph.nodes[2].key], 3)\n", + " assert_equal(graph.nodes[2].adj_weights[graph.nodes[3].key], 4)\n", + " assert_equal(graph.nodes[3].adj_weights[graph.nodes[4].key], 5)\n", + " assert_equal(graph.nodes[3].adj_weights[graph.nodes[5].key], 6)\n", + " assert_equal(graph.nodes[4].adj_weights[graph.nodes[0].key], 7)\n", + " assert_equal(graph.nodes[5].adj_weights[graph.nodes[4].key], 8)\n", + " assert_equal(graph.nodes[5].adj_weights[graph.nodes[2].key], 9)\n", + "\n", + " assert_equal(graph.nodes[0].incoming_edges, 1)\n", + " assert_equal(graph.nodes[1].incoming_edges, 1)\n", + " assert_equal(graph.nodes[2].incoming_edges, 2)\n", + " assert_equal(graph.nodes[3].incoming_edges, 1)\n", + " assert_equal(graph.nodes[4].incoming_edges, 2)\n", + " assert_equal(graph.nodes[5].incoming_edges, 2)\n", + "\n", + " graph.nodes[0].remove_neighbor(graph.nodes[1])\n", + " assert_equal(graph.nodes[1].incoming_edges, 0)\n", + " graph.nodes[3].remove_neighbor(graph.nodes[4])\n", + " assert_equal(graph.nodes[4].incoming_edges, 1)\n", + "\n", + " assert_equal(graph.nodes[0] < graph.nodes[1], True)\n", "\n", " print('Success: test_graph')\n", "\n", " def test_graph_undirected(self):\n", " graph = self.create_graph()\n", - "\n", " graph.add_undirected_edge(0, 1, weight=5)\n", " graph.add_undirected_edge(0, 5, weight=2)\n", " graph.add_undirected_edge(1, 2, weight=3)\n", "\n", - " assert_equal(graph.nodes[0].adjacent[graph.nodes[1]], 5)\n", - " assert_equal(graph.nodes[1].adjacent[graph.nodes[0]], 5)\n", - " assert_equal(graph.nodes[0].adjacent[graph.nodes[5]], 2)\n", - " assert_equal(graph.nodes[5].adjacent[graph.nodes[0]], 2)\n", - " assert_equal(graph.nodes[1].adjacent[graph.nodes[2]], 3)\n", - " assert_equal(graph.nodes[2].adjacent[graph.nodes[1]], 3)\n", + " assert_equal(graph.nodes[0].adj_weights[graph.nodes[1].key], 5)\n", + " assert_equal(graph.nodes[1].adj_weights[graph.nodes[0].key], 5)\n", + " assert_equal(graph.nodes[0].adj_weights[graph.nodes[5].key], 2)\n", + " assert_equal(graph.nodes[5].adj_weights[graph.nodes[0].key], 2)\n", + " assert_equal(graph.nodes[1].adj_weights[graph.nodes[2].key], 3)\n", + " assert_equal(graph.nodes[2].adj_weights[graph.nodes[1].key], 3)\n", "\n", - " print('Success: test_graph')\n", + " print('Success: test_graph_undirected')\n", "\n", "\n", "def main():\n", @@ -298,7 +357,7 @@ "output_type": "stream", "text": [ "Success: test_graph\n", - "Success: test_graph\n" + "Success: test_graph_undirected\n" ] } ], diff --git a/graphs_trees/graph/test_graph.py b/graphs_trees/graph/test_graph.py index e52549e..1ea2998 100644 --- a/graphs_trees/graph/test_graph.py +++ b/graphs_trees/graph/test_graph.py @@ -5,13 +5,12 @@ class TestGraph(object): def create_graph(self): graph = Graph() - for id in range(0, 6): - graph.add_node(id) + for key in range(0, 6): + graph.add_node(key) return graph def test_graph(self): graph = self.create_graph() - graph.add_edge(0, 1, weight=5) graph.add_edge(0, 5, weight=2) graph.add_edge(1, 2, weight=3) @@ -22,33 +21,46 @@ class TestGraph(object): graph.add_edge(5, 4, weight=8) graph.add_edge(5, 2, weight=9) - assert_equal(graph.nodes[0].adjacent[graph.nodes[1]], 5) - assert_equal(graph.nodes[0].adjacent[graph.nodes[5]], 2) - assert_equal(graph.nodes[1].adjacent[graph.nodes[2]], 3) - assert_equal(graph.nodes[2].adjacent[graph.nodes[3]], 4) - assert_equal(graph.nodes[3].adjacent[graph.nodes[4]], 5) - assert_equal(graph.nodes[3].adjacent[graph.nodes[5]], 6) - assert_equal(graph.nodes[4].adjacent[graph.nodes[0]], 7) - assert_equal(graph.nodes[5].adjacent[graph.nodes[4]], 8) - assert_equal(graph.nodes[5].adjacent[graph.nodes[2]], 9) + assert_equal(graph.nodes[0].adj_weights[graph.nodes[1].key], 5) + assert_equal(graph.nodes[0].adj_weights[graph.nodes[5].key], 2) + assert_equal(graph.nodes[1].adj_weights[graph.nodes[2].key], 3) + assert_equal(graph.nodes[2].adj_weights[graph.nodes[3].key], 4) + assert_equal(graph.nodes[3].adj_weights[graph.nodes[4].key], 5) + assert_equal(graph.nodes[3].adj_weights[graph.nodes[5].key], 6) + assert_equal(graph.nodes[4].adj_weights[graph.nodes[0].key], 7) + assert_equal(graph.nodes[5].adj_weights[graph.nodes[4].key], 8) + assert_equal(graph.nodes[5].adj_weights[graph.nodes[2].key], 9) + + assert_equal(graph.nodes[0].incoming_edges, 1) + assert_equal(graph.nodes[1].incoming_edges, 1) + assert_equal(graph.nodes[2].incoming_edges, 2) + assert_equal(graph.nodes[3].incoming_edges, 1) + assert_equal(graph.nodes[4].incoming_edges, 2) + assert_equal(graph.nodes[5].incoming_edges, 2) + + graph.nodes[0].remove_neighbor(graph.nodes[1]) + assert_equal(graph.nodes[1].incoming_edges, 0) + graph.nodes[3].remove_neighbor(graph.nodes[4]) + assert_equal(graph.nodes[4].incoming_edges, 1) + + assert_equal(graph.nodes[0] < graph.nodes[1], True) print('Success: test_graph') def test_graph_undirected(self): graph = self.create_graph() - graph.add_undirected_edge(0, 1, weight=5) graph.add_undirected_edge(0, 5, weight=2) graph.add_undirected_edge(1, 2, weight=3) - assert_equal(graph.nodes[0].adjacent[graph.nodes[1]], 5) - assert_equal(graph.nodes[1].adjacent[graph.nodes[0]], 5) - assert_equal(graph.nodes[0].adjacent[graph.nodes[5]], 2) - assert_equal(graph.nodes[5].adjacent[graph.nodes[0]], 2) - assert_equal(graph.nodes[1].adjacent[graph.nodes[2]], 3) - assert_equal(graph.nodes[2].adjacent[graph.nodes[1]], 3) + assert_equal(graph.nodes[0].adj_weights[graph.nodes[1].key], 5) + assert_equal(graph.nodes[1].adj_weights[graph.nodes[0].key], 5) + assert_equal(graph.nodes[0].adj_weights[graph.nodes[5].key], 2) + assert_equal(graph.nodes[5].adj_weights[graph.nodes[0].key], 2) + assert_equal(graph.nodes[1].adj_weights[graph.nodes[2].key], 3) + assert_equal(graph.nodes[2].adj_weights[graph.nodes[1].key], 3) - print('Success: test_graph') + print('Success: test_graph_undirected') def main():