This is the tree problem
class TreeProblem(Problem):
# methods exported to Search class ---------------------------------------------
# initial "state" of a tree is just its root
def initialState(self):
return self.root.id
# goal test is whether goal matches node label
def goalTest(self, state):
node = self.nodes[state]
return node.isgoal
# successors of a node are its children
def successorFn(self, state):
node = self.nodes[state]
if node.isTerminal():
return []
else:
return [self._contents('l', node.lchild), \
self._contents('r', node.rchild)]
# step cost state--[action]-->result is just the random cost associated with result
def stepCost(self, state, action, result):
node = self.nodes[result]
return node.cost
# no action is actually taken; we just print out the state for debugging
def takeAction(self, state, action):
print 'visit ', state
return 'visit', state
This is the Node class
class Node:
"""A node in a search tree. Contains a pointer to the parent (the node
that this is a successor of) and to the actual state for this node. Note
that if a state is arrived at by two paths, then there are two nodes with
the same state. Also includes the action that got us to this state, and
the total path_cost (also known as g) to reach the node. Other functions
may add an f and h value; see best_first_graph_search and astar_search for
an explanation of how the f and h values are handled. You will not need to
subclass this class."""
def __init__(self, state, parent=None, action=None, path_cost=0):
"Create a search tree Node, derived from a parent by an action."
# state = force_hashable(state)
self.update(self, state=state, parent=parent, action=action,
path_cost=path_cost, depth=0)
if parent:
self.depth = parent.depth + 1
def __repr__(self):
return "<Node %s>" % (self.state,)
def expand(self, problem):
"List the nodes reachable in one step from this node."
return [self.child_node(problem, action)
for action in problem.takeAction(self.state, self.action)]
#print problem
#return [Node(next, self, act,
# problem.stepCost(self.state, act, next[1]))
# for (act, next) in problem.successorFn(self.state)]
def child_node(self, problem, action):
"Fig. 3.10"
(act, next) = problem.successorFn(self.state)
print str(next[1]) + "\n"
return Node(next, self, action,
problem.stepCost(self.state, action, next[1]))
def solution(self):
"Return the sequence of actions to go from the root to this node."
return [node.action for node in self.path()[1:]]
def path(self):
"Return a list of nodes forming the path from the root to this node."
node, path_back = self, []
while node:
path_back.append(node)
node = node.parent
return list(reversed(path_back))
def update(self, x, **entries):
"""Update a dict, or an object with slots, according to `entries` dict.
>>> update({'a': 1}, a=10, b=20)
{'a': 10, 'b': 20}
>>> update(Struct(a=1), a=10, b=20)
Struct(a=10, b=20)
"""
if isinstance(x, dict):
x.update(entries)
else:
x.__dict__.update(entries)
return x
# We want for a queue of nodes in breadth_first_search or
# astar_search to have no duplicated states, so we treat nodes
# with the same state as equal. [Problem: this may not be what you
# want in other contexts.]
def __eq__(self, other):
return isinstance(other, Node) and self.state == other.state
def __hash__(self):
return hash(str(self.state))
This is the dfs_search code:
def depth_limited_search(self, problem, limit=50):
return self.recursive_dls(Node(problem.initialState()), problem, limit)
def recursive_dls(self, node, problem, limit):
print str(type(node.state)) + "\n"
new_state=node.state
print new_state[1]
if problem.goalTest(node.state):
return node
elif node.depth == limit:
return 'cutoff'
else:
cutoff_occurred = False
for child in node.expand(problem):
result = self.recursive_dls(child, problem, limit)
if result == 'cutoff':
cutoff_occurred = True
elif result is not None:
return result
return if_(cutoff_occurred, 'cutoff', None)
here, node.state type is tuple
which has value ('r',2)
when i say node.state[1]
it gives int' object has no attribute 'getitem'
the above error
please guide here
Related
I am reading Chapter 7 of Data Structures and Algorithms in Python and I am finding the Positional List ADT quite hard to understand, the implementation given by the book looks like this:
class _DoublyLinkedBase:
""" A base class providing a doubly linked list representation."""
class _Node:
__slots__ = '_element' , '_prev' , '_next' # streamline memory
def __init__(self, element, prev, next): # initialize node’s fields
self._element = element # user’s element
self._prev = prev # previous node reference
self._next = next # next node reference
# MAIN METHODS
def __init__(self):
self._header = self._Node(None, None, None)
self._trailer = self._Node(None, None, None)
self._header._next = self._trailer # trailer is after header
self._trailer._prev = self._header # header is before trailer
self._size = 0 # number of elements
def __len__(self):
return self._size
def is_empty(self):
return self._size == 0
def _insert_between(self, e, predecessor, successor):
newest = self._Node(e, predecessor, successor) # linked to neighbors
predecessor._next = newest
successor._prev = newest
self._size += 1
return newest
def _delete_node(self, node):
predecessor = node._prev
successor = node._next
predecessor._next = successor
successor._prev = predecessor
self._size -= 1
element = node._element # record deleted element
node._prev = node._next = node._element = None # deprecate node
return element # return deleted element
class PositionalList(_DoublyLinkedBase):
""" A sequential container of elements allowing positional access."""
#-------------------------- nested Position class --------------------------
class Position:
""" An abstraction representing the location of a single element."""
def __init__(self, container, node):
""" Constructor should not be invoked by user."""
self._container = container
self._node = node
def element(self):
""" Return the element stored at this Position."""
return self._node._element
def __eq__(self, other):
""" Return True if other is a Position representing the same location."""
return type(other) is type(self) and other._node is self._node
def __ne__(self, other):
""" Return True if other does not represent the same location."""
return not (self == other) # opposite of eq
#-------------------------- End of nested Position class --------------------
#------------------------------- utility method -------------------------------
def _validate(self, p):
""" Return position s node, or raise appropriate error if invalid."""
if not isinstance(p, self.Position):
raise TypeError("p must be proper Position type")
if p._container is not self:
raise ValueError("p does not belong to this container")
if p._node._next is None: # convention for deprecated nodes
raise ValueError("p is no longer valid")
return p._node
#------------------------------- utility method -------------------------------
def _make_position(self, node):
""" Return Position instance for given node (or None if sentinel)."""
if node is self._header or node is self._trailer:
return None # boundary violation
else:
return self.Position(self, node) # legitimate position
#------------------------------- accessors -------------------------------
def first(self):
""" Return the first Position in the list (or None if list is empty)."""
return self._make_position(self._header._next)
def last(self):
""" Return the last Position in the list (or None if list is empty)."""
return self._make_position(self._trailer._prev)
def before(self, p):
""" Return the Position just before Position p (or None if p is first)."""
node = self._validate(p)
return self._make_position(node._prev)
def after(self, p):
""" Return the Position just after Position p (or None if p is last)."""
node = self._validate(p)
return self._make_position(node._next)
def __iter__(self):
""" Generate a forward iteration of the elements of the list."""
cursor = self.first( )
while cursor is not None:
yield cursor.element( )
cursor = self.after(cursor)
#------------------------------- mutators -------------------------------
# override inherited version to return Position, rather than Node
def _insert_between(self, e, predecessor, successor):
""" Add element between existing nodes and return new Position."""
node = super()._insert_between(e, predecessor, successor)
return self._make_position(node)
def add_first(self, e):
"""" Insert element e at the front of the list and return new Position."""
return self._insert_between(e, self._header, self._header._next)
def add_last(self, e):
""" Insert element e at the back of the list and return new Position."""
return self._insert_between(e, self._trailer._prev, self._trailer)
def add_before(self, p, e):
""" Insert element e into list before Position p and return new Position."""
original = self._validate(p)
return self._insert_between(e, original._prev, original)
def add_after(self, p, e):
""" Insert element e into list after Position p and return new Position."""
original = self._validate(p)
return self._insert_between(e, original, original._next)
def delete(self, p):
""" Remove and return the element at Position p."""
original = self._validate(p)
return self._delete_node(original) # inherited method returns element
def find(self, e):
if len(self) == 0:
raise ValueError('The list is empty')
position = self.first()
while position is not None:
element = position.element()
if element == e:
return position
position = self.after(position)
My question is about the position attribute. We can see that in the constructor of the Position class, there is a container attribute that never gets used explicitly, and this confuses me. I was thinking that maybe a position is something like an index that the user can use to access a certain node.
For instance, when I use the method find(e) to find an element e in the positional list, the method returns the position of the said element, and this position looks something like this:
<main.PositionalList.Position object at 0x7f43acade7f0>.
So I wonder what is the utility of the position if it is just an object and not an integer or string understandable by the user?
there is a container attribute that never gets used explicitly, and this confuses me
Actually, it is used: in the _validate method, which itself is used in several other methods.
Its purpose is to verify that a node -- that is passed to a container's before, after, add_before, add_after, or delete method -- is a node that already belongs to that container, without having to actually find it in the container, which would be a slow process.
I have below method where self contains a data structure as below
self.place = "India"
self.children = ["Tamil Nadu", "Karnataka"]
self.parent
Method
def get_node(self, value):
if value is None:
return self
if self.place == value:
return self
for node in self.children:
if node.place == value:
return node
elif len(node.children) > 0:
return node.get_node(value)
So via recursion, I am iterating on all possible child nodes to find the node I am looking for via return node.get_node(value) but I observed that, iteration happening via "Tamil Nadu" but not via "Karnataka".
I understood that, it took the first element of the list and then continued from there, but not coming back to 2nd element of the list.
is this expected behavior from recursion or am I doing something wrong ?
Full code( In case needed for testing)
class TreeNode:
def __init__(self, place):
self.place = place
self.children = []
self.parent = None
def add_child(self, child):
child.parent = self
self.children.append(child)
def print_tree(self):
prefix = ""
if self.parent is None:
print(self.place)
else:
prefix = prefix + (" " * self.get_level() * 3)
prefix = prefix + "|__"
print(prefix + self.place)
for child in self.children:
child.print_tree()
def get_level(self):
level = 0
p = self.parent
while p:
level = level + 1
p = p.parent
return level
def get_node(self, value):
if value is None:
return self
if self.place == value:
return self
for node in self.children:
if node.place == value:
return node
elif len(node.children) > 0:
return node.get_node(value)
def tree_map(self, nodes):
for node in nodes:
self.add_child(TreeNode(node))
def build_places():
root = TreeNode("Global")
india = TreeNode("India")
usa = TreeNode("USA")
root.add_child(india)
root.add_child(usa)
india_nodes = ["Gujarat" ,"Karnataka"]
gujarath_nodes = [ "Ahmedabad", "Baroda"]
karnataka_nodes = ["Bangalore", "Mysore"]
usa_nodes = ["New Jersey", "California"]
newjersey_nodes = ["Princeton", "Trenton"]
california_nodes = ["San Franciso", "Mountain View", "Palo Alto"]
for node in india_nodes:
india.add_child(TreeNode(node))
for node in usa_nodes:
usa.add_child(TreeNode(node))
gujarath_node = root.get_node("Gujarat")
print(gujarath_node.place)
for node in gujarath_nodes:
gujarath_node.add_child(TreeNode(node))
karnataka_node = root.get_node("Karnataka")
print(karnataka_node.place)
return root
if __name__ == "__main__":
root = build_places()
root.print_tree()
The problem is that in your loop you are always exiting the loop in its first iteration (when the node has at least some children). You should only exit on success, not when the recursive call comes back without success.
So change the loop to this:
for node in self.children:
if node.place == value:
return node
elif len(node.children) > 0:
result = node.get_node(value)
if result:
return result
Secondly, there is a strange base case you have at the start of this function. I would replace this:
if value is None:
return self
With:
if value is None:
return None
...since you didn't look for the value in that case: so then (in my opinion) it is not right to return a node instance (which might have any value -- you didn't verify it). It seems more consistent to return None or to remove this whole if block and not treat None in a special way.
I am trying to create a binary tree using a linked list. I want to delete a node in a binary tree, the way I implemented is to set the address(pointer) to the branch that I want to delete as None, but when I run the traversal methods, the branch still shows up.
here is my code
class tree:
def __init__(self,val):
self.val=val
self.left=None
self.right=None
def preorder(root):
if root is None:
return
print(root.val)
tree.preorder(root.left)
tree.preorder(root.right)
def inorder(root):
if root is None:
return
tree.inorder(root.left)
print(root.val)
tree.inorder(root.right)
def postorder(root):
if root is None:
return
tree.postorder(root.left)
tree.postorder(root.right)
print(root.val)
def levelorder(root):
if root is None:
return
Q=[]
Q.append(root)
while Q!=[]:
l=len(Q)
for i in range(l):
print(Q[i].val)
temp=[]
for i in Q:
if i.left is not None:
temp.append(i.left)
if i.right is not None:
temp.append(i.right)
Q.clear()
Q=temp.copy()
def search(root,value):
o=[]
o.append(tree.levelorder(root))
if value in o:
return "Found"
else:
return "Not Found"
def insert(root,value,where):
if root is None:
return
Q=[]
Q.append(root)
value=tree(value)
while Q!=[]:
for i in Q:
if i.val==where:
if i.left is not None:
i.right=value
else:
i.left=value
return
temp=[]
for i in Q:
if i.left is not None:
temp.append(i.left)
if i.right is not None:
temp.append(i.right)
Q.clear()
Q=temp.copy()
def delete(root,value):
if root is None:
return
Q=[]
Q.append(root)
value=tree(value)
while Q!=[]:
for i in Q:
if i.left is not None and i.left.val==value:
i.left.val=None
i.left=None
if i.right is not None and i.right.val==value:
i.right.val=None
i.right=None
return
temp=[]
for i in Q:
if i.left is not None:
temp.append(i.left)
if i.right is not None:
temp.append(i.right)
Q.clear()
Q=temp.copy()
and here is how I created the tree
base=tree("drinks")
L=tree("hot")
R=tree("cold")
LL=tree("coffe")
LR=tree("tea")
LRL=tree("w milk")
LRR=tree("wo milk")
base.left=L
base.right=R
L.left=LL
L.right=LR
LR.left=LRL
LR.right=LRR
Now when I run the delete method, the object that is supposed to be deleted still shows up.
tree.delete(base,"w milk")
tree.levelorder(base)
drinks
hot
cold
coffe
tea
ice cream
w milk
wo milk <=== the node i am trying to delete
The main issues:
delete method's while loop will exit immediately, because of the return statement
The value to compare with should not be turned into a node with value=tree(value)
Some remarks on your code:
You'll not be able to ever delete the root node of the tree, since you only check the values of child nodes. In order to allow the root to be deleted, and to represent an empty tree, you should really consider creating a second class. The current class could then be renamed to Node and the new class could become Tree.
It is a common habit to use a capital first letter for class names, while for variable names you would always start with a lower case letter. So not Q, but q.
It is not such good practice to print inside methods of such classes: these methods should just provide tools to iterate over nodes, but they should not print them. Use yield instead.
For instance, search should return the node when it is found and None otherwise. Don't print the result.
The insert method may actually delete node(s) when both the left and right child of the where node are already occupied. It would be better to reject such an insert.
In the OOP pattern, it is widely accepted to call the first argument of instance methods self. In OOP, you would call these methods with the dot notation: so instead of tree.postorder(root.left) you would do root.left.postorder().
You have quite some code repetition: mainly for traversals, and where you use the queue algorithm twice. Try to avoid that.
Here is how you could modify your code to align to these suggestions:
class Node:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def traverse(self, kind=0, parent=None):
if kind == 0: # pre-order
yield (self, parent) # don't print in methods
if self.left:
yield from self.left.traverse(kind, self)
if kind == 1: # in-order
yield (self, parent)
if self.right:
yield from self.right.traverse(kind, self)
if kind == 2: # post-order
yield (self, parent)
class Tree:
def __init__(self):
self.root = None # Representing an empty tree
def traversevalues(self, kind=0):
if self.root:
for node, parent in self.root.traverse(kind):
yield node.val
def preorder(self):
yield from self.traversevalues(0)
def inorder(self):
yield from self.traversevalues(1)
def postorder(self):
yield from self.traversevalues(2)
def levelorder(self):
if self.root:
q = [self.root]
while q:
temp = []
for node in q:
yield node.val
if node.left:
temp.append(node.left)
if node.right:
temp.append(node.right)
q = temp
def search(self, value):
if self.root:
return next((edge for edge in self.root.traverse() if edge[0].val == value), None)
def insert(self, value, where=None):
if not where:
if self.root:
raise ValueError("Must call insert with second argument")
else:
self.root = Node(value)
else:
edge = self.search(where)
if edge:
parent = edge[0]
if not parent.left:
parent.left = Node(value)
elif not parent.right:
parent.right = Node(value)
else:
raise ValueError(f"Node {where} already has 2 children")
else:
raise ValueError(f"Node {where} not found")
def deletesubtree(self, value):
edge = self.search(value)
if edge:
node, parent = edge
if parent.left == node:
parent.left = None
else:
parent.right = None
else:
raise ValueError(f"Node {value} not found")
tree = Tree()
tree.insert("drinks")
tree.insert("hot", "drinks")
tree.insert("cold", "drinks")
tree.insert("coffee", "hot")
tree.insert("tea", "hot")
tree.insert("with milk", "tea")
tree.insert("without milk", "tea")
print(*tree.levelorder())
tree.deletesubtree("without milk")
print(*tree.levelorder())
I am trying to delete the minimum node from a BST, so I search through the tree until I get the min (when root.leftnode is None) and then set root.rightnode to the root itself to continue the BST.
The issue is when I check the tree after doing this it does not show the deletion ever occurred.
Could someone point me in the right direction please, any advice is appreciated.
class node():
def __init__(self, key, data):
self.data = data
self.key = key
self.leftnode = None
self.rightnode = None
self.count = 1
class binarysearch():
def __init__(self):
self.size = 0
self.rootnode = None
def insert(self, key, data):
if self.rootnode is None:
self.rootnode = node(key, data)
else:
self.insertnode(self.rootnode, key, data)
def getroot(self):
return self.rootnode
def insertnode(self, root, key, data):
if root.key == key:
root.data = data
elif key < root.key:
if root.leftnode is None:
root.leftnode = node(key, data)
else:
self.insertnode(root.leftnode, key, data)
else:
if root.rightnode is None:
root.rightnode = node(key, data)
else:
self.insertnode(root.rightnode, key, data)
root.count = 1 + self.sizenode(root.leftnode) + self.sizenode(root.rightnode)
def inorder(self, root):
if root is not None:
self.inorder(root.leftnode)
print(root.key)
self.inorder(root.rightnode)
def deletemin(self):
if self.rootnode is None:
print("No nodes exist")
else:
self.deleteminnode(self.rootnode.leftnode)
def deleteminnode(self, root):
if root.leftnode is not None:
self.deleteminnode(root.leftnode)
else:
print (root.key, "deleted")
root = root.rightnode
if __name__ == '__main__':
a = binarysearch()
a.insert(7,7)
a.insert(1,1)
a.insert(8,8)
a.insert(3,3)
a.insert(9,9)
a.insert(2,2)
a.insert(4,4)
a.insert(11,11)
a.insert(10,10)
a.deletemin()
a.getnodes()
The issue you have is that root = root.rightnode only rebinds the local variable root. It doesn't change the other places you have references to that node (such as its parent in the tree).
To fix this, you need to change how your recursive function works. Rather than expecting it to do all the work in the last call, it should instead return the value that should be the left node of its parent. Of then that will be the node itself, but for the minimum node, it will be its right child instead.
def deletemin(self):
if self.rootnode is None:
print("No nodes exist")
else:
self.rootnode = self.deleteminnode(self.rootnode)
def deleteminnode(self, root):
if root.leftnode is not None:
root.leftnode = self.deleteminnode(root.leftnode)
return root
else:
return root.rightnode
A final note regarding names: It's a bit weird to use root as the name of a random node within the tree. Usually a tree has just the one root node, and others nodes aren't called root since they have parents. Unfortunately, the most conventional name node is already being used for your node class. Normally classes should be given CapitalizedNames, so that lowercase_names can exclusively refer to instances and other variables. This is just convention though (and builtin types like list break the rules). It might be easier for others to understand your code if you use standard name styles, but Python doesn't enforce them. It will allow you to use whatever names you want. Even the name self is not a requirement, though it would be very confusing if you used something different for the first argument of a method without a good reason (an example of a good reason: classmethods and methods of metaclasses often use cls as the name of their first arguments, since the object will be a class).
You can find all the nodes in the tree, along with the path to the node, find the minimum of the results, and then traverse the generated path to delete the node:
class Tree:
def __init__(self, **kwargs):
self.__dict__ = {i:kwargs.get(i) for i in ['val', 'left', 'right']}
def get_nodes(self, current = []):
yield [''.join(current), self.val]
yield from getattr(self.right, 'get_nodes', lambda _:[])(current+['1'])
yield from getattr(self.left, 'get_nodes', lambda _:[])(current+['0'])
def __iter__(self):
yield self.val
yield from [[], self.left][bool(self.left)]
yield from [[], self.right][bool(self.right)]
def _insert_back(self, _v):
if not self.val:
self.val = _v
else:
if _v < self.val:
getattr(self.left, '_insert_back', lambda x:setattr(x, 'left', Tree(val=x)))(_v)
else:
getattr(self.right, '_insert_back', lambda x:setattr(x, 'right', Tree(val=x)))(_v)
def remove(self, _path, _to_val, last=None):
'''_to_val: if _to_val is None, then the item is removed. If not, the node value is set to _to_val'''
if _path:
getattr(self, ['left', 'right'][int(_path[0])]).remove(_path[1:], _to_val, last = self)
else:
if _to_val is None:
last.left = None
last.right = None
for i in [[], self.left][bool(self.left)]:
last._insert_back(i)
for i in [[], self.right][bool(self.right)]:
last._insert_back(i)
else:
self.val = _to_val
Creating:
7
5 9
4 6 8 10
12
t = Tree(val = 7, left=Tree(val = 5, left=Tree(val=4), right=Tree(val=6)), right=Tree(val=9, left=Tree(val=8), right=Tree(val=10, right=Tree(val=12))))
path, _to_remove = min(t.get_nodes(), key=lambda x:x[-1])
print(f'Removing {_to_remove}')
t.remove(path, None)
print([i for i in t])
Output:
4
[7, 5, 9, 8, 10, 12]
I've been working on a school assignment, where I need to implement Dijkstra's algorithm. That wouldn't be too hard by itself but unfortunately, the automatic checking script disagrees with all of my implementations (I actually made like 8 different versions). All the initial data checking works correctly, only when the script generates random data, it differs. My path and script's path has the same distance, but different vertexes on the path. For example:
Teachers path: City2, City15, City16, City6,
Students path: City2, City15, City18, City0, City6,
I even contacted the teacher who just responded with "You have to use priority queue :-)" despite me using one (in fact, several implementations of one, from my own to heapq). Am I doing something wrong or is it the teacher script that's incorrect? I hope the code is self-commenting enough to be understandable. Thank you for any advice you can give me.
The algorithm is called on source vertex and computes shortest distance and path to every other connected node. If the vertex has same minDistance (ie. priority) as some that's already there, it should go in front of it, not after it.
class Node:
"""Basic node of the priority queue"""
def __init__(self, data, priority):
self.data = data
self.nextNode = None
self.priority = priority
self.id = data.id
class PriorityQueue:
"""Basic priority queue with add, remove and update methods"""
def __init__(self):
self.head = None
self.count = 0
def add(self, data, priority):
"""Adds data with priority in the proper place"""
node = Node(data, priority)
if not self.head:
self.head = node
elif node.priority <= self.head.priority:
node.nextNode = self.head
self.head = node
else:
checker = self.head
while True:
if not checker.nextNode or node.priority >= checker.nextNode.priority:
break
checker = checker.nextNode
node.nextNode = checker.nextNode
checker.nextNode = node
return 0
def remove(self, data):
"""Removes specified node and reconnects the remaining nodes, does nothing if node not found"""
checker = self.head
if not self.head:
return 0
if checker.id == data.id:
self.head = checker.nextNode
while True:
checker = checker.nextNode
if not checker or not checker.nextNode:
return 0
if checker.nextNode.id == data.id:
checker.nextNode = checker.nextNode.nextNode
break
return 0
def update(self, data):
"""Updates priority of existing node via removing and re-adding it"""
self.remove(data)
self.add(data, data.minDistance)
return 0
def getMin(self):
"""Returns the minimum priority data"""
min = self.head
return min.data
class Edge:
"""Edge of the graph, contains source, target and weight of line"""
def __init__(self, source, target, weight):
self.source = source
self.target = target
self.weight = weight
class Vertex:
"""Vertex of the graph, everything except id and name is filled later"""
def __init__(self, id, name):
self.id = id
self.name = name
self.minDistance = float('inf')
self.previousVertex = None
self.edges = []
self.visited = False
class Dijkstra:
"""Dijkstra's algorithm implementation"""
def __init__(self):
self.vertexes = []
self.nodes = {}
self.unvisited = PriorityQueue()
def createGraph(self, vertexes, edgesToVertexes):
"""Connects edges to appropriate vertexes, adds vertexes to node dictionary"""
self.vertexes = vertexes
for vertex in self.vertexes:
for edge in edgesToVertexes:
if vertex.id == edge.source:
vertex.edges.append(edge)
edgesToVertexes.remove(edge)
self.nodes[vertex.id] = vertex
return 0
def getVertexes(self):
"""Returns vertexes in graph, should be called after creating it just to check"""
return self.vertexes
def computePath(self, sourceId):
"""Fills in minDistance and previousVertex of all nodes from source"""
mainNode = self.nodes[sourceId]
mainNode.minDistance = 0
self.unvisited.add(mainNode, 0)
while self.unvisited.head:
mainNode = self.unvisited.getMin()
mainNode.visited=True
for edge in mainNode.edges:
tempDistance = mainNode.minDistance + edge.weight
targetNode = self.nodes[edge.target]
self.unvisited.remove(mainNode)
if tempDistance < targetNode.minDistance:
targetNode.minDistance = tempDistance
targetNode.previousVertex = mainNode
self.unvisited.update(targetNode)
return 0
def getShortestPathTo(self, targetId):
"""Returns list of shortest parth to targetId from source. Call only after doing ComputePath"""
path = []
mainNode = self.nodes[targetId]
while True:
path.append(mainNode)
mainNode = mainNode.previousVertex
if not mainNode:
break
return list(reversed(path))
def resetDijkstra(self):
"""Resets ComputePath but leaves graph untouched"""
for vertex in self.vertexes:
vertex.minDistance = float('inf')
vertex.previousVertex = None
return 0
def createGraph(self, vertexes, edgesToVertexes):
"""Connects edges to appropriate vertexes, adds vertexes to node dictionary"""
self.vertexes = vertexes
for vertex in self.vertexes:
for edge in edgesToVertexes:
if vertex.id == edge.source:
vertex.edges.append(edge)
edgesToVertexes.remove(edge)
self.nodes[vertex.id] = vertex
return 0
I belive this was wrong => edgesToVertexes.remove(edge)
I had similar home work and used some of your code and this one line was incorrect I think. It removed one path from vortex in every loop.