1#!/usr/bin/env python
2#coding:utf-8
3# Author:  mozman
4# Purpose: binary tree module
5# Created: 28.04.2010
6# Copyright (c) 2010-2013 by Manfred Moitzi
7# License: MIT License
8
9from __future__ import absolute_import
10
11from .treemixin import TreeMixin
12
13__all__ = ['BinaryTree']
14
15
16class Node(object):
17    """ Internal object, represents a treenode """
18    __slots__ = ['key', 'value', 'left', 'right']
19
20    def __init__(self, key, value):
21        self.key = key
22        self.value = value
23        self.left = None
24        self.right = None
25
26    def __getitem__(self, key):
27        """ x.__getitem__(key) <==> x[key], where key is 0 (left) or 1 (right) """
28        return self.left if key == 0 else self.right
29
30    def __setitem__(self, key, value):
31        """ x.__setitem__(key, value) <==> x[key]=value, where key is 0 (left) or 1 (right) """
32        if key == 0:
33            self.left = value
34        else:
35            self.right = value
36
37    def free(self):
38        """ Set references to None """
39        self.left = None
40        self.right = None
41        self.value = None
42        self.key = None
43
44
45class BinaryTree(TreeMixin):
46    """
47    BinaryTree implements an unbalanced binary tree with a dict-like interface.
48
49    see: http://en.wikipedia.org/wiki/Binary_tree
50
51    A binary tree is a tree data structure in which each node has at most two
52    children.
53
54    BinaryTree() -> new empty tree.
55    BinaryTree(mapping,) -> new tree initialized from a mapping
56    BinaryTree(seq) -> new tree initialized from seq [(k1, v1), (k2, v2), ... (kn, vn)]
57
58    see also TreeMixin() class.
59
60    """
61    def __init__(self, items=None):
62        """ x.__init__(...) initializes x; see x.__class__.__doc__ for signature """
63        self._root = None
64        self._count = 0
65        if items is not None:
66            self.update(items)
67
68    def clear(self):
69        """ T.clear() -> None.  Remove all items from T. """
70        def _clear(node):
71            if node is not None:
72                _clear(node.left)
73                _clear(node.right)
74                node.free()
75        _clear(self._root)
76        self._count = 0
77        self._root = None
78
79    @property
80    def root(self):
81        """ root node of T """
82        return self._root
83
84    @property
85    def count(self):
86        """ count of items """
87        return self._count
88
89    def _new_node(self, key, value):
90        """ Create a new tree node. """
91        self._count += 1
92        return Node(key, value)
93
94    def insert(self, key, value):
95        """ T.insert(key, value) <==> T[key] = value, insert key, value into Tree """
96        if self._root is None:
97            self._root = self._new_node(key, value)
98        else:
99            parent = None
100            direction = 0
101            node = self._root
102            while True:
103                if node is None:
104                    parent[direction] = self._new_node(key, value)
105                    break
106                if key == node.key:
107                    node.value = value  # replace value
108                    break
109                else:
110                    parent = node
111                    direction = 0 if key <= node.key else 1
112                    node = node[direction]
113
114    def remove(self, key):
115        """ T.remove(key) <==> del T[key], remove item <key> from tree """
116        node = self._root
117        if node is None:
118            raise KeyError(str(key))
119        else:
120            parent = None
121            direction = 0
122            while True:
123                if key == node.key:
124                    # remove node
125                    if (node.left is not None) and (node.right is not None):
126                        # find replacment node: smallest key in right-subtree
127                        parent = node
128                        direction = 1
129                        replacement = node.right
130                        while replacement.left is not None:
131                            parent = replacement
132                            direction = 0
133                            replacement = replacement.left
134                        parent[direction] = replacement.right
135                        #swap places
136                        node.key = replacement.key
137                        node.value = replacement.value
138                        node = replacement  # delete replacement!
139                    else:
140                        down_dir = 1 if node.left is None else 0
141                        if parent is None:  # root
142                            self._root = node[down_dir]
143                        else:
144                            parent[direction] = node[down_dir]
145                    node.free()
146                    self._count -= 1
147                    break
148                else:
149                    direction = 0 if key < node.key else 1
150                    parent = node
151                    node = node[direction]
152                    if node is None:
153                        raise KeyError(str(key))
154