简体   繁体   中英

Python BST not working

I'm new to Python thus the question,this is the implementation of my my BST

class BST(object):
    def __init__(self):
        self.root = None
        self.size = 0

    def add(self, item):
        return self.addHelper(item, self.root)

    def addHelper(self, item, root):
        if root is None:
            root = Node(item)
            return root

        if item < root.data:
            root.left = self.addHelper(item, root.left)
        else:
            root.right = self.addHelper(item, root.right)

This is the Node object

class Node(object):
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

This is my implmentation of str

def __str__(self):
    self.levelByLevel(self.root)
    return "Complete"



 def levelByLevel(self, root):

        delim = Node(sys.maxsize)
        queue = deque()
        queue.append(root)
        queue.append(delim)
        while queue:
            temp = queue.popleft()
            if temp == delim and len(queue) > 0:
                queue.append(delim)
                print()
            else:
                print(temp.data, " ")
                if temp.left:
                    queue.append(temp.left)
                if temp.right:
                    queue.append(temp.right)

This is my calling client,

def main():
    bst = BST()
    bst.root = bst.add(12)
    bst.root = bst.add(15)
    bst.root = bst.add(9)

    bst.levelByLevel(bst.root)


if __name__ == '__main__':
    main()

Instead of the expected output of printing the BST level by level I get the following output,

9  
9223372036854775807  

When I look in the debugger it seems that the every time the add method is called it starts with root as None and then returns the last number as root. I'm not sure why this is happening. Any help appreciated.

If the root argument of your addHelper is None , you set it to a newly-created Node object and return it. If it is not, then you modify the argument but return nothing, so you end up setting bst.root to None again. Try the following with your code above — it should help your understanding of what your code is doing.

bst = BST()

bst.root = bst.add(12)
try:   
    print(bst.root.data)
except AttributeError:
    print('root is None')

# => 12
# `bst.addHelper(12, self.root)` returned `Node(12)`,
#     which `bst.add` returned too, so now `bst.root`
#     is `Node(12)`

bst.root = bst.add(15)
try:   
    print(bst.root.data)
except AttributeError:
    print('root is None')

# => root is None
# `bst.addHelper(15, self.root)` returned `None`,
#     which `bst.add` returned too, so now `bst.root`
#     is `None`.


bst.root = bst.add(9)
try:   
    print(bst.root.data)
except AttributeError:
    print('root is None')

# => 9
# `bst.addHelper(9, self.root)` returned `Node(9)`,
#     which `bst.add` returned too, so now `bst.root`
#     is `Node(9)`

So you should do two things:

  1. make you addHelper always return its last argument — after the appropriate modifications —, and
  2. have your add function take care of assigning the result to self.root (do not leave it for the class user to do).

Here is the code:

def add(self, item):
    self.root = self.addHelper(item, self.root)
    self.size += 1    # Otherwise what good is `self.size`?

def addHelper(self, item, node):
    if node is None:
        node = Node(item)
    elif item < node.data:
        node.left = self.addHelper(item, node.left)
    else:
        node.right = self.addHelper(item, node.right)
    return node

Notice that I changed the name of the last argument in addHelper to node for clarity (there already is something called root : that of the tree!).

You can now write your main function as follows:

def main():
    bst = BST()
    bst.add(12)
    bst.add(15)
    bst.add(9)

    bst.levelByLevel(bst.root)

(which is exactly what @AaronTaggart suggests — but you need the modifications in add and addHelper ). Its output is:

12  

9  
15  
9223372036854775807

The above gets you to a working binary search tree. A few notes:

  1. I would further modify your levelByLevel to avoid printing that last value, as well as not taking any arguments (besides self , of course) — it should always print from the root of the tree.
  2. bst.add(None) will raise an error. You can guard against it by changing your add method. One possibility is

     def add(self, item): try: self.root = self.addHelper(item, self.root) self.size += 1 except TypeError: pass 

    Another option (faster, since it refuses to go on processing item if it is None ) is

     def add(self, item): if item is not None: self.root = self.addHelper(item, self.root) self.size += 1 
  3. From the point of view of design, I would expect selecting a node from a binary search tree would give me the subtree below it. In a way it does (the node contains references to all other nodes below), but still: Node and BST objects are different things. You may want to think about a way of unifying the two (this is the point in @YairTwito's answer).

  4. One last thing: in Python, the convention for naming things is to have words in lower case and separated by underscores, not the camelCasing you are using — so add_helper instead of addHelper . I would further add an underscore at the beginning to signal that it is not meant for public use — so _add_helper , or simply _add .

Based on the following, you can see that bst.root in None after the second call to add() :

>>> bst.root = bst.add(12)
>>> bst.root
<__main__.Node object at 0x7f9aaa29cfd0>
>>> bst.root = bst.add(15)
>>> type(bst.root)
<type 'NoneType'>

Your addHelper isn't returning the root node. Try this:

def addHelper(self, item, root):
    if root is None:
        root = Node(item)
        return root

    if item < root.data:
        root.left = self.addHelper(item, root.left)
    else:
        root.right = self.addHelper(item, root.right)

    return root

And then it works as expected:

>>> bst.root = bst.add(12)
>>> bst.root = bst.add(15)
>>> bst.levelByLevel(bst.root)
(12, ' ')
()
(15, ' ')
(9223372036854775807, ' ')
>>> bst.root = bst.add(9)
>>> bst.levelByLevel(bst.root)
(12, ' ')
()
(9, ' ')
(15, ' ')
(9223372036854775807, ' ')

You're using the BST object basically only to hold a root Node and the add function doesn't really operate on the BST object so it's better to have only one class ( BtsNode ) and implement the add there. Try that and you'll see that the add function would be much simpler. And, in general, when a member function doesn't use self it shouldn't be a member function (like addHelper ), ie, it shouldn't have self as a parameter (if you'd like I can show you how to write the BtsNode class).

I tried writing a class that uses your idea of how to implement the BST .

class BstNode:
    def __init__(self):
        self.left = None
        self.right = None
        self.data = None

    def add(self,item):
        if not self.data:
            self.data = item
        elif item >= self.data:
            if not self.right:
                 self.right = BstNode()
            self.right.add(item)
        else:
            if not self.left:
                 self.left = BstNode()
            self.left.add(item)

That way you can create a BST the following way:

bst = BstNode()
bst.add(13)
bst.add(10)
bst.add(20)

The difference is that now the add function actually operates on the object without any need for the user to do anything. The function changes the state of the object by itself.

In general a function should do only what it's expected to do. The add function is expected to add an item to the tree so it shouldn't return the root. The fact that you had to write bst.root = bst.add() each time should signal that there's some fault in your design.

Your add method probably shouldn't return a value. And you most certainly shouldn't assign the root of the tree to what the add method returns.

Try changing your main code to something like this:

def main():
    bst = BST()
    bst.add(12)
    bst.add(15)
    bst.add(9)

    bst.levelByLevel(bst.root)


if __name__ == '__main__':
    main()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM