|
1 | 1 | from __future__ import print_function, division
|
2 | 2 | from pydatastructs.utils import Node
|
| 3 | +from pydatastructs.miscellaneous_data_structures import Stack |
| 4 | +from pydatastructs.linear_data_structures import OneDimensionalArray |
| 5 | +# TODO: REPLACE COLLECTIONS QUEUE WITH PYDATASTRUCTS QUEUE |
| 6 | +from collections import deque as Queue |
3 | 7 |
|
4 | 8 | __all__ = [
|
5 | 9 | 'Node',
|
6 | 10 | 'BinaryTree',
|
7 |
| - 'BinarySearchTree' |
| 11 | + 'BinarySearchTree', |
| 12 | + 'BinaryTreeTraversal' |
8 | 13 | ]
|
9 | 14 |
|
10 | 15 | class BinaryTree(object):
|
@@ -243,3 +248,194 @@ def delete(self, key):
|
243 | 248 | self.tree[parent].right = child
|
244 | 249 |
|
245 | 250 | return True
|
| 251 | + |
| 252 | +class BinaryTreeTraversal(object): |
| 253 | + """ |
| 254 | + Represents the traversals possible in |
| 255 | + a binary tree. |
| 256 | +
|
| 257 | + Parameters |
| 258 | + ========== |
| 259 | +
|
| 260 | + tree: BinaryTree |
| 261 | + The binary tree for whose traversal |
| 262 | + is to be done. |
| 263 | +
|
| 264 | + Traversals |
| 265 | + ========== |
| 266 | +
|
| 267 | + - Depth First Search |
| 268 | + In Order, Post Order, Pre Order Out Order |
| 269 | +
|
| 270 | + - Breadth First Search |
| 271 | +
|
| 272 | + Examples |
| 273 | + ======== |
| 274 | +
|
| 275 | + >>> from pydatastructs import BinarySearchTree as BST |
| 276 | + >>> from pydatastructs import BinaryTreeTraversal as BTT |
| 277 | + >>> b = BST(2, 2) |
| 278 | + >>> b.insert(1, 1) |
| 279 | + >>> b.insert(3, 3) |
| 280 | + >>> trav = BTT(b) |
| 281 | + >>> dfs = trav.depth_first_search() |
| 282 | + >>> [str(n) for n in dfs] |
| 283 | + ['(None, 1, 1, None)', '(1, 2, 2, 2)', '(None, 3, 3, None)'] |
| 284 | + >>> bfs = trav.breadth_first_search() |
| 285 | + >>> [str(n) for n in bfs] |
| 286 | + ['(1, 2, 2, 2)', '(None, 1, 1, None)', '(None, 3, 3, None)'] |
| 287 | +
|
| 288 | + References |
| 289 | + ========== |
| 290 | +
|
| 291 | + .. [1] https://en.wikipedia.org/wiki/Tree_traversal |
| 292 | + """ |
| 293 | + |
| 294 | + __slots__ = ['tree'] |
| 295 | + |
| 296 | + def __new__(cls, tree): |
| 297 | + if not isinstance(tree, BinaryTree): |
| 298 | + raise TypeError("%s is not a binary tree"%(tree)) |
| 299 | + obj = object.__new__(cls) |
| 300 | + obj.tree = tree |
| 301 | + return obj |
| 302 | + |
| 303 | + def _pre_order(self, node): |
| 304 | + """ |
| 305 | + Utility method for computing pre-order |
| 306 | + of a binary tree using iterative algorithm. |
| 307 | + """ |
| 308 | + visit = [] |
| 309 | + if node == None: |
| 310 | + return visit |
| 311 | + tree, size = self.tree.tree, self.tree.size |
| 312 | + s = Stack(maxsize=size) |
| 313 | + s.push(node) |
| 314 | + while not s.is_empty: |
| 315 | + node = s.pop() |
| 316 | + visit.append(tree[node]) |
| 317 | + if tree[node].right != None: |
| 318 | + s.push(tree[node].right) |
| 319 | + if tree[node].left != None: |
| 320 | + s.push(tree[node].left) |
| 321 | + return visit |
| 322 | + |
| 323 | + def _in_order(self, node): |
| 324 | + """ |
| 325 | + Utility method for computing in-order |
| 326 | + of a binary tree using iterative algorithm. |
| 327 | + """ |
| 328 | + visit = [] |
| 329 | + tree, size = self.tree.tree, self.tree.size |
| 330 | + s = Stack(maxsize=size) |
| 331 | + while not s.is_empty or node != None: |
| 332 | + if node != None: |
| 333 | + s.push(node) |
| 334 | + node = tree[node].left |
| 335 | + else: |
| 336 | + node = s.pop() |
| 337 | + visit.append(tree[node]) |
| 338 | + node = tree[node].right |
| 339 | + return visit |
| 340 | + |
| 341 | + def _post_order(self, node): |
| 342 | + """ |
| 343 | + Utility method for computing post-order |
| 344 | + of a binary tree using iterative algorithm. |
| 345 | + """ |
| 346 | + visit = [] |
| 347 | + tree, size = self.tree.tree, self.tree.size |
| 348 | + s = Stack(maxsize=size) |
| 349 | + s.push(node) |
| 350 | + last = OneDimensionalArray(int, size) |
| 351 | + last.fill(False) |
| 352 | + while not s.is_empty: |
| 353 | + node = s.peek |
| 354 | + l, r = tree[node].left, tree[node].right |
| 355 | + cl, cr = l == None or last[l], r == None or last[r] |
| 356 | + if cl and cr: |
| 357 | + s.pop() |
| 358 | + visit.append(tree[node]) |
| 359 | + last[node] = True |
| 360 | + continue |
| 361 | + if not cr: |
| 362 | + s.push(r) |
| 363 | + if not cl: |
| 364 | + s.push(l) |
| 365 | + return visit |
| 366 | + |
| 367 | + def _out_order(self, node): |
| 368 | + """ |
| 369 | + Utility method for computing out-order |
| 370 | + of a binary tree using iterative algorithm. |
| 371 | + """ |
| 372 | + return reversed(self._in_order(node)) |
| 373 | + |
| 374 | + def depth_first_search(self, order='in_order', node=None): |
| 375 | + """ |
| 376 | + Computes the depth first search traversal of the binary |
| 377 | + trees. |
| 378 | +
|
| 379 | + Parameters |
| 380 | + ========== |
| 381 | +
|
| 382 | + order : str |
| 383 | + One of the strings, 'in_order', 'post_order', |
| 384 | + 'pre_order', 'out_order'. |
| 385 | + By default, it is set to, 'in_order'. |
| 386 | + node : int |
| 387 | + The index of the node from where the traversal |
| 388 | + is to be instantiated. |
| 389 | +
|
| 390 | + Returns |
| 391 | + ======= |
| 392 | +
|
| 393 | + list |
| 394 | + Each element is of type 'Node'. |
| 395 | + """ |
| 396 | + if node == None: |
| 397 | + node = self.tree.root_idx |
| 398 | + if order not in ('in_order', 'post_order', 'pre_order', 'out_order'): |
| 399 | + raise NotImplementedError( |
| 400 | + "%s order is not implemented yet." |
| 401 | + "We only support `in_order`, `post_order`, " |
| 402 | + "`pre_order` and `out_order` traversals.") |
| 403 | + return getattr(self, '_' + order)(node) |
| 404 | + |
| 405 | + def breadth_first_search(self, node=None, strategy='queue'): |
| 406 | + # TODO: IMPLEMENT ITERATIVE DEEPENING-DEPTH FIRST SEARCH STRATEGY |
| 407 | + """ |
| 408 | + Computes the breadth first search traversal of a binary tree. |
| 409 | +
|
| 410 | + Parameters |
| 411 | + ========== |
| 412 | +
|
| 413 | + strategy : str |
| 414 | + The strategy using which the computation has to happen. |
| 415 | + By default, it is set 'queue'. |
| 416 | + node : int |
| 417 | + The index of the node from where the traversal has to be instantiated. |
| 418 | + By default, set to, root index. |
| 419 | +
|
| 420 | + Returns |
| 421 | + ======= |
| 422 | +
|
| 423 | + list |
| 424 | + Each element of the list is of type `Node`. |
| 425 | + """ |
| 426 | + strategies = ('queue',) |
| 427 | + if strategy not in strategies: |
| 428 | + raise NotImplementedError( |
| 429 | + "%s startegy is not implemented yet"%(strategy)) |
| 430 | + if node == None: |
| 431 | + node = self.tree.root_idx |
| 432 | + q, visit, tree = Queue(), [], self.tree.tree |
| 433 | + q.append(node) |
| 434 | + while len(q) > 0: |
| 435 | + node = q.popleft() |
| 436 | + visit.append(tree[node]) |
| 437 | + if tree[node].left != None: |
| 438 | + q.append(tree[node].left) |
| 439 | + if tree[node].right != None: |
| 440 | + q.append(tree[node].right) |
| 441 | + return visit |
0 commit comments