MIT6.006 lecture06

114 阅读3分钟

Binary tree:

def:

pointer-based data structure,it has three pointers each node.

Binary tree is a tree of binary nodes.

representation:

node.{item,parent,left,right}

Initialiaze:

class Binary_Node:
    def __init__(self, A, x):
        A.item = x
        A.left = None
        A.right = None
        A.parent = None

Binary tree & Linked list:

In the linked list, we may cost O(n)O(n) time to get a item.

In the binary tree, which height is h.We only take O(h)=O(logn)O(h)=O(logn) time to get one. Thus that,we can take operations faster in binary tree.

Tree Navigation:

Traversal Order:

X.left_subtree -> X -> X.right_subtree

def subtree_iter(A):
    if A.left: yield from A.left.subtree_iter()
    yield A
    if A.right: yield from A.right.subtree_iter()

Find first node:

  • If x has left child,recursively return the first node in the left subtree.
  • Otherwise,x is the first node.
def subtree_first(A):
   if A.left:
       A.left.subtree_first
   else:
       return A


def subtree_last(A):
   if A.right:
       A.right.subtree_last
   else:
       return A

Find successor:

  • If x has right child, return thefirst node in the right subtree
  • Otherwise,return the low ancestor of x,for which,x is in the left subtree
def successor(A):
    if A.right:
        return A.right.subtree_first()
    else:
        while A.parents and A is (A.parens.right):
            A = A.parents


def predecessor(A):
    if A.left:
        return A.left.subtree_last()
    else:
        while A.parents and (A is A.parents.right):
            A = A.parents

Dynamic Operatins:

Insert:(insert < y > after < x >)

  • If < x > has no right child,make < y > the right child of < x >
  • Otherwise, make< y > the left child of < x>'s successor(which doesn't have left node)
def subtree_insert_before(A, B):
    if A.left:
        A = A.left.subtree_last
        A.right, B.parent = B, A
    else:
        A.left, B.parent = B, A
      A.maintain ()

def subtree_insert_after(A, B):
    if A.right:
        A = A.right.subtree_first
        A.left, B.parent = B, A
    else:
        A.right, B.parent = B, A
    A.maintain ()

Delete:

The key is that,if the node is leaf ,it's easy to delete.

If the node isn't a leaf node , just make it to leaf. What we need to do is swapping it with it's child,then recurse to be the leaf node.

  • If x is leaf node,delete it dirrectly
  • else if x has left child,swap it with it's predeccessor,and recurse
  • else if x has right child,swap it with it's successor,and recurse
def subtree_delete(A):
    if A.parent():
        if A is A.parent.left:
            A.parent.left = None
        else:
            A.parent.right = None
        A.parents.maintain()
    if A.left or A.right:
        if A.left:
            B = A.predecessor()
        else:
            B = A.successor
        A.item, B.item = B.item, A.item
        return B.subtree_delete()

Application Set:

Set Binary tree:(Binary Search Tree)

BST Property:left  subtreenodes  keyright  subtreeleft \;subtree \le node's\;key\le right\;subtree

We can use the binary seaerch to find the key k.

  • If k is samller than x.key,recurse in left subtree
  • If k is larger than x.key.recurse in the right subtree
  • If k == x.key,x is the item we find
class Binary_Search_Tree_Node(Binary_Node):
    def subtree_find(A, k):
        if k < A.item.key:
            if A.left: return A.left.subtree_find(k)
        if k > A.item.key:
            if A.right: return A.right.subtree_find(k)
        else:
            return A

    def subtree_find_next(A, k):
        if k >= A.item.key:
            if A.right:
                return A.right.subtree_find_next(k)
            else:
                return None
        elif A.left:
            B = A.left.subtree_find_next(k)
            if B: return B
        else:
            return A

    def subtree_find_prev(A, k):
        if k <= A.item.key:
            if A.left:
                return A.left.subtree_find_prev(k)
        elif A.right:
            B = A.right.subtree_find_prev(k)
            if B: return B
        else:
            return A

    def subtree_insert(A, B):
        if B.item.key < A.item.key:
            if A.left:
                A.left.subtree_insert(B)
            else:
                A.subtree_insert_before(B)
        elif B.item.key > A.item.key:
            if A.right:
                A.right.subtree_insert(B)
            else:
                A.subtree_insert_after(B)
        else:
            A.item = B.item


class Set_binary_tree(Binary_Search_Tree_Node):
    def __init__(self):
        super().__init__(Binary_Search_Tree_Node)

    def iter_order(self):
        yield from self

    def build(self, X):
        for x in X:
            self.insert(x)

    def find_min(self):
        if self.root:
            return self.root.subtree_first()

    def find_max(self):
        if self.root:
            return self.root.subtree_last

    def find(self, k):
        if self.root:
            node = self.root.subtree_find(k)
            if node:
                return node.item

    def find_next(self, k):
        if self.root:
            node = self.root.subtree_find_next(k)
            if node:
                return node.item

    def find_prev(self, k):
        if self.root:
            node = self.root.subtree_find_prev(k)
            if node:
                return node.item

    def insert(self, x):
        new_node = self.Node_Type(x)
        if self.root:
            self.root.subtree_insert(new_node)
            if new_node.parents is None: return False

        else:
            self.root = new_node

        self.size += 1
        return True

    def delete(self, x):
        assert self.root
        node = self.root.subtree_find(k)
        assert node
        ext = node.subtree_delete(node)
        if ext.parent is None: self.root = None
        self.size -= 1
        return ext
    

Application Sequence:

Sequence Binary Tree: Traversal order is sequence order

Maintain the size of each node's subtree at the node via augmentation.

  • add node.size field to each node
  • when adding new node,size+=1
  • when deleting the node,size-=1

We can surmise the approxiamate place in the binary tree.

  • define nLn_L is the size of left subtree
  • if i nL\le n_L ,recurse in the left subtree
  • else recurse in the right subtree,inew=i(nL+1)i_{new}=i-(n_L+1)

Full Implementation:

class Binary_node:
    def __init__(self, A, x):
        A.item = x
        A.left = None
        A.right = None
        A.parent = None
        A.subtree_update()

    def subtree_iter(self, A):
        if A.left: yield from A.left.subtree_iter()
        yield A
        if A.right: yield from A.right.subtree_iter()

    def subtree_first(self, A):
        if A.left:
            return A.left.subtree_first()
        else:
            return A


def subtree_last(A):
    if A.right:
        return A.right.subtree_last()
    else:
        return A


def successor(A):
    if A.right: return A.right.subtree_first()
    while A.parent and A.parent.right is A:
        A = A.parent
    return A.parent


def predecessor(A):
    if A.left: return A.left.subtree_last()
    while A.parent and (A.parent.left is A):
        A = A.parent
    return A.parent()


def subtree_insert_before(A, B):
    if A.left:
        A = A.left.subtree_last()
        A.right, B.parent = B, A
    else:
        A.left, B = B, A


def subtree_insert_after(A, B):
    if A.right:
        A = A.right.subtree_first()
        A.right = B
        B.parent = A
    else:
        A.right = B
        B.parent = A


def subtree_delete(A):
    if A.left or A.right:
        if A.left:
            B = A.predecessor()
        else:
            B = A.successor()
        A.item, B.item = B.item, A.item
        return B.subtree_delete()
    if A.parent:
        if A.parent.left is A:
            A.parent.left = None
        else:
            A.parent.right = None

    return A

Exercise:

  1. Construct a binary tree,in order that,(1):the ithi^th item of the tree is A[i],(2):T has height of O(logn)O(logn)

solution:The key is that, we maintain the middle ietem of the array as the root in the binary tree,and then recursively in the left and right subtree.

def build(X):
    A = [x for x in X]

    def build_subtree(A, i, j):
        c = (i + j) // 2
        root = self.Node_Type(A[c])
        if i < c:
            root.left.build_subtree(A, i, c - 1)
            root.left.parent = root
        if j > c:
            root.right.build_subtree(A, c + 1, j)
            root.right.parent = root

        return root

    self.root = build_subtree(A, 0, len(A) - 1)
  1. Analyse the running time of this code,the iterative procedure to return the nodes in order
def subtree_iter(A):
   node = A.subtree_first
   while node:
       yield node
       node = node.successor
      

Traverse each edge twice,once going down,once going back.The number of the edges is less than node's,so running time is O(n)O(n)