Bi-Directional Binary Search Trees? - python

I have tried to implement a BST. As of now it only adds keys according to the BST property(Left-Lower, Right-Bigger). Though I implemented it in a different way.
This is how I think BST's are supposed to be
Single Direction BST
How I have implemented my BST
Bi-Directional BST
The question is whether or not is it the correct implementation of BST?
(The way i see it in double sided BST's it would be easier to search, delete and insert)
import pdb;
class Node:
def __init__(self, value):
self.value=value
self.parent=None
self.left_child=None
self.right_child=None
class BST:
def __init__(self,root=None):
self.root=root
def add(self,value):
#pdb.set_trace()
new_node=Node(value)
self.tp=self.root
if self.root is not None:
while True:
if self.tp.parent is None:
break
else:
self.tp=self.tp.parent
#the self.tp varible always is at the first node.
while True:
if new_node.value >= self.tp.value :
if self.tp.right_child is None:
new_node.parent=self.tp
self.tp.right_child=new_node
break
elif self.tp.right_child is not None:
self.tp=self.tp.right_child
print("Going Down Right")
print(new_node.value)
elif new_node.value < self.tp.value :
if self.tp.left_child is None:
new_node.parent=self.tp
self.tp.left_child=new_node
break
elif self.tp.left_child is not None:
self.tp=self.tp.left_child
print("Going Down Left")
print(new_node.value)
self.root=new_node
newBST=BST()
newBST.add(9)
newBST.add(10)
newBST.add(2)
newBST.add(15)
newBST.add(14)
newBST.add(1)
newBST.add(3)
Edit: I have used while loops instead of recursion. Could someone please elaborate as why using while loops instead of recursion is a bad idea in this particular case and in general?

BSTs with parent links are used occasionally.
The benefit is not that the links make it easier to search or update (they don't really), but that you can insert before or after any given node, or traverse forward or backward from that node, without having to search from the root.
It becomes convenient to use a pointer to a node to represent a position in the tree, instead of a full path, even when the tree contains duplicates, and that position remains valid as updates or deletions are performed elsewhere.
In an abstract data type, these properties make it easy, for example, to provide iterators that aren't invalidated by mutations.

You haven't described how you gain anything with the parent pointer. An algorithm that cares about rewinding to the parent node, will do so by crawling back up the call stack.
I've been there -- in my data structures class, I implemented my stuff with bi-directional pointers. When we got to binary trees, those pointers ceased to be useful. Proper use of recursion replaces the need to follow a link back up the tree.

Related

Graphs - what is the functional difference between "pure" Dijkstra and my hybrid BFS-Dijkstra solution?

I am (or was supposed to, at least) make a Dijkstra implementation, but upon reviewing what I've done it looks more like a breadth-first search. But I wonder if I have come across a way to kind of do both things at the same time?
Essentially by using an OOP approach I can perform a BFS that also preserves knowledge of the shortest weighted path, thereby eliminating the need to determine during the search process whether some node has a lower cost than its alternatives like Dijkstra does.
I've searched for clues as to why a more "pure" implementation of Dijkstra should be faster than this, most prominently the answers in these two threads:
What is difference between BFS and Dijkstra's algorithms when looking for shortest path?
Why use Dijkstra's Algorithm if Breadth First Search (BFS) can do the same thing faster?
I haven't seen anything that made me understand the question to what I'm wondering, though.
My approach is essentially this:
Start at whatever node we require, this becomes a "path"
Step out to each adjacent node, and each of these steps create a new path that we store in a path collection
Every path contains an ordered list of which nodes it has visited from the starting node all the way to wherever it is, as well as the associated weight/cost
Mark the "parent" path as closed (stop iterating on it)
Select the first open path in the path collection and repeat the above
When there are no more open paths, delete all paths that didn't make it to the destination node
Compare the lengths and return the path with the lowest weight
I struggle to see where the performance difference between this and pure Dijkstra would come from. Dijkstra would still have to iterate over all possible paths, right? So I perform exactly the same number of steps with this implementation as if I change returnNextOpenPath() (serving the function of a normal queue, just not implemented as one) to a more priority queue-looking returnShortestOpenPath().
There's presumably a marginal performance penalty at the end where I examine all the collected, non-destroyed paths before I can print a result instead of just popping from a queue - but aside from that, am I just not seeing where else my implementation would also be worse than "pure" Dijkstra?
I don't think it matters, but in case it does: The actual code I have for this is gigantic so my first instinct is to hold off on posting it for now, but here's a stripped down version of it.
class DijkstraNode:
def getNeighbors(self):
# returns a Dict of all adjacent nodes and their cost
def weightedDistanceToNeighbor(self, neighbor):
# returns the cost associated with traversing from current node to the chosen neighbor node
return int(self.getNeighbors()[neighbor])
class DijkstraEdge:
def __init__(self, startingNode: DijkstraNode, destinationNode: DijkstraNode)
self.start = startingNode
self.goal = destinationNode
def weightedLength(self):
return self.start.weightedDistanceToNeighbor(self.goal)
class DijkstraPath:
def __init__(self, startingNode: DijkstraNode, destinationNode: DijkstraNode):
self.visitedNodes: list[DjikstraNode] = [startingNode]
self.previousNode = self.start
self.edges: list[DijkstraEdge] = []
def addNode(self, node: DijkstraNode)
# if the node we're inspecting is new, add it to all the lists
if not node in self.visitedNodes:
self.edges.append(DijkstraEdge(self.prevNode, node))
self.visitedNodes.append(node)
self.prevNode = node
# if the node we just added above is our destination, stop iterating on this path
if node = self.goal:
self.closed = True
self.valid = True
class DijkstraTree:
def bruteforceAllPaths(self, startingNode: DijkstraNode, destinationNode: DijkstraNode):
self.pathlist = []
self.pathlist.append(DjikstraPath(startingNode, destinationNode))
cn: DjikstraNode
# iterate over all open paths
while self.hasOpenPaths():
currentPath = self.returnNextOpenPath()
neighbors: Dict = currentPath.lastNode().getNeighbors()
for c in neighbors:
cn = self.returnNode(c)
# copy the current path
tmpPath = deepcopy(currentPath)
# add the child node onto the newly made path
tmpPath.addNode(cn)
# add the new path to pathlist
if tmpPath.isOpen() or tmpPath.isValid():
self.pathlist.append(tmpPath)
# then we close the parent path
currentPath.close()

K array Tree Height using DFS with Recursion, is the code logic wrong?

I feel embarrassed asking this question but I spent close to four hours trying to make sense of why this code works. My problem is that this code to me looks like it works to me when the longest path is selected the first time but not when sub-optimal path is selected first time. My guess is that this code works because when when the not longest path is selected the depth value and height resets and then the next path is selected??? Can someone please explain?
Picture:
'''
For your reference:
class TreeNode:
def __init__(self):
self.children = []
'''
def find_height(root):
global max
max=0
if not root:
return 0
traverse(root,0)
return max-1
def traverse(node, depth):
global max
depth +=1
if depth>max:
max=depth
for child in node.children:
traverse(child, depth)
The code is correct, and it is like you say, but maybe it helps to just take a bit of abstraction. The code essentially traverses the whole tree. It does not really matter in which order it does that. In this code it performs a depth-first traversal, probably because that is the easiest to implement and has a small memory footprint. But it is not really relevant for understanding how it can return the length of the longest path.
Just note that the algorithm guarantees to visit each node and knows at which depth this node occurs. It should be clear that when you take the maximum depth of all the depths that you encounter during the traversal, that you have found the length of the longest path from the root, or in other words, the height of the tree.
The following statement makes sure the code always updates the maximum depth in light of the current depth:
if depth>max:
max=depth
I should maybe also highlight that depth is a local variable, which belongs to one execution context of the function traverse. So each execution of the function creates a new instance of that variable. When you return out of a recursive call, the execution comes back to where you have a previous version of this variable. A recursive call does not modify the value of depth from where that call is made. So it truly reflects the depth of the current node (after depth += 1 is executed)
In contrast, max is a global variable, and so there is only one version of that variable. And so the effect of max=depth persists also after a call of traverse terminates.
As a side note, many would say that modifying a global variable from inside a function is not ideal (it represents a side effect), and there are better ways to code this. The recursive function should better return the height of the subtree rooted in the node that is passed as argument. This also has as advantage that you don't need the depth argument, and you don't need a second function:
def find_height(root):
if not root:
return 0
# the height of the tree is 1 more than that of the tallest subtree below it
return 1 + max(find_height(child) for child in root.children, default=0)

Refactoring between public and private methods in Python

I'm looking at the Binary Search Trees section in the tutorial "Problem Solving with Algorithms and Data Structures", (http://interactivepython.org/runestone/static/pythonds/Trees/SearchTreeImplementation.html). On several occasions, they use "public" and "private" helper methods with the same name, e.g. for the "put" method:
def put(self,key,val):
if self.root:
self._put(key,val,self.root)
else:
self.root = TreeNode(key,val)
self.size = self.size + 1
def _put(self,key,val,currentNode):
if key < currentNode.key:
if currentNode.hasLeftChild():
self._put(key,val,currentNode.leftChild)
else:
currentNode.leftChild = TreeNode(key,val,parent=currentNode)
else:
if currentNode.hasRightChild():
self._put(key,val,currentNode.rightChild)
else:
currentNode.rightChild = TreeNode(key,val,parent=currentNode)
I also have seen this approach elsewhere, but I don't really understand the motivation. What is the advantage compared to putting everything directly into one method, is it just to improve readability?
The rationale is that the user of the class should not know anything about "current node". The current node only makes sense during the recursive insert process, it's not a permanent property of the tree. The user treats the tree as a whole, and only does insert/lookup operations on that.
That said, you could mix both methods into one, by using a default value currentNode=None and checking it. However, the two methods are doing significantly different things. The put method just initialises the root, while the _put does the recursive insertion, so it would probably be better to keep them separate.
Here, the motivation is to use recursion. As you probably notice, _put method calls itself and method signatures are different. If you put _put method into public method, you have to change signature of public method to handle put operation on a given node. Simply, you have to add currentNode parameter. However, original public method does not have this parameter. I assume, it is because the author does not want to expose this functionality to end user.

Testing if an object is dependent to another object

Is there a way to check if an object is dependent via parenting, constraints, or connections to another object? I would like to do this check prior to parenting an object to see if it would cause dependency cycles or not.
I remember 3DsMax had a command to do this exactly. I checked OpenMaya but couldn't find anything. There is cmds.cycleCheck, but this only works when there currently is a cycle, which would be too late for me to use.
The tricky thing is that these 2 objects could be anywhere in the scene hierarchy, so they may or may not have direct parenting relationships.
EDIT
It's relatively easy to check if the hierarchy will cause any issues:
children = cmds.listRelatives(obj1, ad = True, f = True)
if obj2 in children:
print "Can't parent to its own children!"
Checking for constraints or connections is another story though.
depending on what you're looking for, cmds.listHistory or cmds.listConnections will tell you what's coming in to a given node. listHistory is limited to a subset of possible connections that drive shape node changes, so if you're interested in constraints you'll need to traverse the listConnections for your node and see what's upstream. The list can be arbitrarily large because it may include lots of hidden nodes like like unit translations, group parts and so on that you probably don't want to care about.
Here's simple way to troll the incoming connections of a node and get a tree of incoming connections:
def input_tree(root_node):
visited = set() # so we don't get into loops
# recursively extract input connections
def upstream(node, depth = 0):
if node not in visited:
visited.add(node)
children = cmds.listConnections(node, s=True, d=False)
if children:
grandparents = ()
for history_node in children:
grandparents += (tuple(d for d in upstream(history_node, depth + 1)))
yield node, tuple((g for g in grandparents if len(g)))
# unfold the recursive generation of the tree
tree_iter = tuple((i for i in upstream(root_node)))
# return the grandparent array of the first node
return tree_iter[0][-1]
Which should produce a nested list of input connections like
((u'pCube1_parentConstraint1',
((u'pSphere1',
((u'pSphere1_orientConstraint1', ()),
(u'pSphere1_scaleConstraint1', ()))),)),
(u'pCube1_scaleConstraint1', ()))
in which each level contains a list of inputs. You can then troll through that to see if your proposed change includes that item.
This won't tell you if the connection will cause a real cycle, however: that's dependent on the data flow within the different nodes. Once you identify the possible cycle you can work your way back to see if the cycle is real (two items affecting each other's translation, for example) or harmless (I affect your rotation and you affect my translation).
This is not the most elegant approach, but it's a quick and dirty way that seems to be working ok so far. The idea is that if a cycle happens, then just undo the operation and stop the rest of the script. Testing with a rig, it doesn't matter how complex the connections are, it will catch it.
# Class to use to undo operations
class UndoStack():
def __init__(self, inputName = ''):
self.name = inputName
def __enter__(self):
cmds.undoInfo(openChunk = True, chunkName = self.name, length = 300)
def __exit__(self, type, value, traceback):
cmds.undoInfo(closeChunk = True)
# Create a sphere and a box
mySphere = cmds.polySphere()[0]
myBox = cmds.polyCube()[0]
# Parent box to the sphere
myBox = cmds.parent(myBox, mySphere)[0]
# Set constraint from sphere to box (will cause cycle)
with UndoStack("Parent box"):
cmds.parentConstraint(myBox, mySphere)
# If there's a cycle, undo it
hasCycle = cmds.cycleCheck([mySphere, myBox])
if hasCycle:
cmds.undo()
cmds.warning("Can't do this operation, a dependency cycle has occurred!")

Depth first iterative deepening algorithm to print a binary tree breadth first

I'm not a programmer but as part of a personal project of mine I'm keen to understand if there is a recursive solution to being able to print a binary tree breadth first, level order? I understand an iterative depth first algorithm could be used?
#Helper method
def getChildren(node):
children=[]
hasLeft = node.left is not None
hasRight = node.right is not None
if not hasLeft and not hasRight:
return []
if hasLeft:
children.append(node.left)
if hasRight:
children.append(node.right)
return children
def DLS(node, depth):
"""Depth Limited Search"""
if (depth == 0):
return node
elif (depth > 0):
print node.value,
children = getChildren(node)
for child in children:
DLS(child, depth-1)
else:
return False
For the following binary tree:
(1)3
(2)2 (3)1
(4)1 (5)1 (6)1 (7)0
(8)1 (9)0
I'm getting this traversal output:
(1)3 (2)2 (4)1 (8)1 (9)0 (5)1 (3)1 (6)1 (7)0 None
Which is not level order but pre-order depth first.
Do I have to iterate the depth to the DLS function? How would I implement for a level order printout of the binary tree?
Many thanks
Alex
In terms of data structures, the difference between depth-first and breadth-first is that depth-first uses a stack and breadth-first uses a queue.
In depth-first, the idea is "deal with the consequences of the last thing you dealt with". So with a stack you always pop the last node that was pushed, and you get the right behaviour.
In breadth-first, the idea is "first deal with all the consequences of the chosen node, then move on". So you first find out the consequences (and store them), and only then you start dealing with them in the order you found them. The simplest data structure to support this is a queue.
The problem is that recursion uses a stack (the call stack). So you don't see the data structure, but it's actually there. Because there is no (simple) way of queuing calls, you can't translate a breadth-first search to code without using an explicit queue.
If you need information about breadth-first implementation with a queue, I suggest you check out Wikipedia.

Categories