What is your preferred method of traversing a tree data structure, since recursive method calls can be pretty inefficient in some circumstances. I am simply using a generator like the one above. Do you have any hints to make it faster?
def children(self):
stack = [self.entities]
while stack:
for e in stack.pop():
yield e
if e.entities:
stack.append(e.entities)
Here is some test data. The first one is recursive, the second uses the generator:
s = time.time()
for i in range(100000):
e.inc_counter()
print time.time() - s
s = time.time()
for i in range(100000):
for e in e.children():
e.inc_counter_s()
print time.time() - s
Results:
0.416000127792
0.298999786377
Test code:
import random
class Entity():
def __init__(self, name):
self.entities = []
self.name = name
self.counter = 1
self.depth = 0
def add_entity(self, e):
e.depth = self.depth + 1
self.entities.append(e)
def inc_counter_r(self):
for e in self.entities:
e.counter += 1
e.inc_counter_r()
def children(self):
stack = [self.entities]
while stack:
for e in stack.pop():
yield e
if e.entities:
stack.append(e.entities)
root = Entity("main")
def fill_node(root, max_depth):
if root.depth <= max_depth:
for i in range(random.randint(10, 15)):
e = Entity("node_%s_%s" % (root.depth, i))
root.add_entity(e)
fill_node(e, max_depth)
fill_node(root, 3)
import time
s = time.time()
for i in range(100):
root.inc_counter_r()
print "recursive:", time.time() - s
s = time.time()
for i in range(100):
for e in root.children():
e.counter += 1
print "generator:", time.time() - s
1) Create an empty stack S. 2) Initialize current node as root 3) Push the current node to S and set current = current->left until current is NULL 4) If current is NULL and stack is not empty then a) Pop the top item from stack.
There are three common ways to traverse them in depth-first order: in-order, pre-order and post-order. Beyond these basic traversals, various more complex or hybrid schemes are possible, such as depth-limited searches like iterative deepening depth-first search.
Pre-order Traversal In this traversal method, the root node is visited first, then the left subtree and finally the right subtree. In the below python program, we use the Node class to create place holders for the root node as well as the left and right nodes. Then we create a insert function to add data to the tree.
Level Order Traversal As BFS suggests, the breadth of the tree takes priority first and then move to depth. In simple words, we will visit all the nodes present at the same level one-by-one from left to right and then move to the next level to visit all the nodes of that level.
I can't think of any big algorithmic improvements, but a simple microoptimisation you can make is to bind frequently called methods (such as stack.append / stack.pop) to locals (this saves a dictionary lookup)
def children(self):
stack = [self.entities]
push = stack.append
pop = stack.pop
while stack:
for e in pop():
yield e
if e.entities:
push(e.entities)
This gives a small (~15%) speedup by my tests (using 100 traversals of an 8-deep tree with 4 children at each node gives me the below timings:)
children : 5.53942348004
children_bind: 4.77636131253
Not huge, but worth doing if speed is important.
Unless your tree is really large or you have really high (real) requirements for speed, I would choose the recursive method. Easier to read, easier to code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With