简体   繁体   中英

Confusion with recursion and property setters in Python

I'm trying to implement a Binary Search Tree class. I have two classes; BSTNode , and BST . I try to enforce the search tree property in the setters for left and right :

class BSTNode(object):


    def __init__(self,new):
        if type(new) is BSTNode:
            self._data = new.data
        else:
            self._data = new
        self._left = None
        self._right = None


    @property
    def data(self):
         return self._data


    @property
    def left(self):
        return self._left


   @left.setter
   def left(self,data):
        if data is None:
            self._left = None
        else:
            n = BSTNode(data)
            if n.data >= self.data:
                del n
                raise ValueError("Value must be less-than parent!")
            self._left = n


    @property
    def right(self):
        return self._right


    @right.setter
    def right(self,data):
        if data is None:
            self._right = None
        else:
            n = BSTNode(data)
            if n.data < self.data:
                del n
                raise ValueError("Value must be greater-than or equal-to parent!")
            self._right = n


class BST(object):


    def __init__(self):
        self._root = None


    @property
    def root(self):
        return self._root


    @root.setter
    def root(self,value):
        self._root = BSTNode(value)


    def binary_insert(self,list_in):
        self.root = binary_insert(list_in,0,len(list_in) - 1)

Now, I am trying to implement a method binary_insert(self,list_in) where I insert a sorted list into the tree such that the tree is balanced (using essentially binary search); however, my left and right nodes off of root are always None , though I assign them explicitly in the function, and I am sure my indices are correct, as I get the following to print when I run it:

> t = BST()
> list_in = [0,1,2,3,4,5,6,7,8]
> t.binary_insert(list_in)
4
1
0
2
3
6
5
7
8

Here is my function (note instance method binary_insert above in class BST ):

def binary_insert(list_in,imin,imax):
    if imax < imin:
        return None
    imid = int(floor((imax + imin) / 2))
    n = BSTNode(list_in[imid])
    print(n.data)
    n.left = binary_insert(list_in,imin,imid-1)
    n.right = binary_insert(list_in,imid+1,imax)
    return n

I am always returning a BSTNode , which is None only when the input to the setter is None , though the only node in the tree after the function runs is root . I suspect there is something going on with the properties that I don't understand. I'd love some clarification on this.

 > t = BST()
 > list_in = [0,5,12]
 > t.binary_insert(list_in)
 5
 0
 12
 > t.root.data 
 5
 > t.root.left
 None
 > t.root.right
 None

Expected:

 > t.root.left.data
 0
 > t.root.right.data
 12

The issue occurs because the following line executes after all the recursion is done and root is created as a BSTNode -

self.root = binary_insert(list_in,0,len(list_in) - 1)

That is at the end binary_insert() returns a BSTNode which is the root, this calls the setter for root , which is -

@root.setter
def root(self,value):
    self._root = BSTNode(value)

This causes self._root to be initialized with a new BSTNode reference whose data is same as that of root returned from binary_insert() , this calls the __init__() for BSTNode passing in the root as the argument. And the __init__() function of BSTNode does this -

def __init__(self,new):
    if type(new) is BSTNode:
        self._data = new.data
    else:
        self._data = new
    self._left = None
    self._right = None

Here , you are setting self._left to None and self._right to None . So the root's left and right values are none, as you observed.

Two ways you can solve this issue, one is -

change the line where you are setting self.root to -

def binary_insert(self,list_in):
    self._root = binary_insert(list_in,0,len(list_in) - 1)

Or you can also, change the __init__() BSTNode , such that if the type(new) is BSTNode , you copy over the left and right values from the new BSTNode as well. Example -

def __init__(self,new):
    if type(new) is BSTNode:
        self._data = new.data
        self._left = new.left
        self._right = new.right
    else:
        self._data = new
        self._left = None
        self._right = None

It looks like your insert method is building a tree, but it's not attaching to the root and all references to the tree are being lost.

By the way, note that your method for balancing the tree (choosing the middle of the partition of the list) only works if the list is sorted. You'll need to either sort the list beforehand or use a more general balancing scheme like an AVL tree or a Red-Black tree

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