Reputation: 6343
Currently I'm coding up some graph theory stuff in Python, at this point DFS. I've made a Node class which holds the name of the Node (vertex if you like) and a list of strings with the names of all the other nodes with which it is connected.
# Simplified for the sake of the question.
class Node:
""" Node using an adjacency list to know which other
nodes it's connected to. """
def __init__(self, name, edges):
self.name = name # Letter normally, like C.
self.edges = sorted(edges) # List of letters for Nodes.
self.visited = False
I found it unrealistic to try and include a list of Node objects instead of a string for the name of each node because that would involve making sure all the Nodes exist before creating the list of connected nodes.
This means that when I want to lookup a node for doing something like DFS, I need a function like this:
def getNode(nodeLetter, nodes):
for node in nodes:
if node.name == nodeLetter:
return node
return None
This function can take O(|V|) time, proportional to the number of vertices. This will build up over time because throughout an explore I will need to access every vertex, making a total complexity of O(|V|^2). If I had used a dictionary instead, for example like this:
{"A": ["C", "E"], "E", ["A"]}
I could have constant time lookup. This does of course however introduce problems such as where I store the extra attributes like visited
, as well as pre and post numbers, and anything else I might need.
How can I get the best of both worlds here, with the neatness of a class and the efficiency of a dict. Is there such a thing as an efficient dict of classes with a given key? Keen to learn how best to go about this.
Upvotes: 0
Views: 105
Reputation: 1424
Why you don't create a dict that link letters to nodes instead of traversing all nodes whenever you want to know which node has the letter.
So the idea is simple
by that way you can eliminate this loop
def getNode(nodeLetter, nodes):
for node in nodes:
if node.name == nodeLetter:
return node
return None
to this
def getNode(nodeLetter, nodesandlettersdict):
return nodesandlettersdict(nodeLetter)
Upvotes: 0
Reputation: 14614
You can subclass most Python types (besides the NoneType, to my knowledge, and the GeneratorType).
Just do something like this:
class MyDict(dict):
'''This walks and talks like a dict'''
def do_something(self):
print(repr(self))
You can extend it however you like, you can even override the __getitem__
and __setitem__
magic methods to control how data is accessed and stored in the dict.
In this case, we can then instantiate the dict, and get the best of both worlds:
>>> mydict = MyDict([('a', 3)])
>>> kwd_init = MyDict(a=3)
>>> dict_init = MyDict({'a': 3})
>>> mydict.do_something()
{'a': 3}
In your specific case, just apply it to the node architecture.
class Node(dict):
'''Defines a node object'''
get_node = dict.__getitem__
set_node = dict.__setitem__
Now we can extend this to store previously visited nodes, most likely by using a list.
class Node(dict):
'''Defines a node object'''
def __init__(self, *args, **kwds):
super(Node, self).__init__(*args, **kwds)
self.visited = []
def __getitem__(self, name):
'''Get the node but also store it as the last visited item'''
value = dict.__getitem__(self, name)
self.visited.append(name)
return value
get_node = __getitem__
set_node = dict.__setitem__
If you want to have constant lookup time for the visited nodes, use a set. If you want both, try a custom OrderedSet.
Not too hard, was that?
Upvotes: 1