树及其衍生算法(Trees and tree algorithms)

1,二叉树(Binary tree)

    二叉树:每一个节点最多两个子节点,如下图所示:

      

    相关概念:节点Node,路径path,根节点root,边edge,子节点 children,父节点parent,兄弟节点sibling, 子树subtree,叶子节点leaf node, 度level,树高hight

节点Node:
路径path:从一个节点到拧一个节点间的边
根节点root,
边edge:节点间的连线
子节点 children,
父节点parent,
兄弟节点sibling,
子树subtree,
叶子节点leaf node,
度level:从当前节点到根节点的路径中边的数量
高度 hight:树中所有节点的最大level

    二叉树可以通过多级列表的形式实现,多级列表形式如下,根节点r,有两个子节点a , b,且a, b节点没有子节点。

           mytree =[ r,

                [ a, [ ], [ ] ],  [ b, [ ], [ ] ]

                ]

    python实现代码如下:

#coding:utf-8

#多级列表实现
def binaryTree(r):
    return [r,[],[]]  #root[]为根节点,root[1]左子树,root[2]右子树

def insertLeftTree(root,newbranch):
    t = root.pop(1)
    if len(t)>1:
        root.insert(1, [newbranch, t, []])
    else:
        root.insert(1,[newbranch, [], []])
    return root

def insertRightTree(root,newbranch):
    t = root.pop(2)
    if len(t)>1:
        root.insert(2, [newbranch, [], t])
    else:
        root.insert(2,[newbranch, [], []])
    return root
def getRootVal(root):
    return root[0]

def setRootVal(root,val):
    root[0]= val

def getLeftChildren(root):
    return root[1]

def getRightChildren(root):
    return root[2]

r = binaryTree(3)
insertLeftTree(r,4)
insertLeftTree(r,5)
insertRightTree(r,6)
insertRightTree(r,7)
l = getLeftChildren(r)
print(l)

setRootVal(l,9)
print(r)
insertLeftTree(l,11)
print(r)
print(getRightChildren(getRightChildren(r)))

多级列表形式

    二叉树可以通过节点的形式实现,如下所示:

          

    python实现代码如下:

class BinaryTree(object):
    def __init__(self,value):
        self.key = value
        self.leftChild = None
        self.rightChild = None

    def insertLeft(self,newNode):
        if self.leftChild != None:
            temp = BinaryTree(newNode)
            temp.leftChild = self.leftChild
            self.leftChild = temp
        else:
            self.leftChild = BinaryTree(newNode)

    def insertRight(self,newNode):
        if self.rightChild != None:
            temp = BinaryTree(newNode)
            temp.rightChild= self.rightChild
            self.rightChild = temp
        else:
            self.rightChild = BinaryTree(newNode)

    def getRootVal(self):
        return self.key

    def setRootVal(self,value):
        self.key = value

    def getLeftChild(self):
        return self.leftChild

    def getRightChild(self):
        return self.rightChild

节点形式

2,二叉树的应用

  2.1 解析树(parse tree)

    解析树常用于表示真实世界的结构表示,如句子和数学表达式。如下图是((7+3)*(5-2))的解析树表示,根据解析树的层级结构,从下往上计算,能很好的代替括号的表达式中括号的作用

    将一个全括号数学表达式转化为解析树的过程如下:

      遍历表达式:

          1,若碰到“(”,为当前节点插入左节点,并移动到左节点

          2,若碰到 + ,- ,* , /,设置当前节点的值为该符号,并为当前节点插入右节点,并移动到右节点

          3,若碰到数字,设置当前节点的值为该数字,并移动到其父节点

          4,若碰到“)”,移动到当前节点的父节点

      python实现代码如下:(Stack 参见数据结构之栈

from stackDemo import Stack  #参见数据结构之栈

def buildParseTree(expstr):
    explist = expstr.split()
    s = Stack()
    t = BinaryTree(‘‘)
    s.push(t)
    current = t
    for token in explist:
        #token = token.strip()
        if token ==‘(‘:
            current.insertLeft(‘‘)
            s.push(current)
            current = current.getLeftChild()
        elif token in [‘*‘,‘/‘,‘+‘,‘-‘]:
            current.setRootVal(token)
            current.insertRight(‘‘)
            s.push(current)
            current = current.getRightChild()
        elif token not in [‘(‘,‘*‘,‘/‘,‘+‘,‘-‘,‘)‘]:
            current.setRootVal(token)
            current = s.pop()
        elif token==‘)‘:
            current = s.pop()
        else:
            raise ValueError
    return t

t = buildParseTree("( ( 10 + 5 ) * 3 )")

构造解析树

    计算解析树:数学表达式转化为解析树后,可以对其进行计算,python代码如下: 

import operator
def evaluate(parseTree):
    operators={‘+‘:operator.add,‘-‘:operator.sub,‘*‘:operator.mul,‘/‘:operator.div }
    rootval = parseTree.getRootVal()
    left = parseTree.getLeftChild()
    right = parseTree.getRightChild()

    if left and right:
        fn = operators[rootval]
        return fn(evaluate(left),evaluate(right))
    else:
        return parseTree.getRootVal()

计算解析树

    中序遍历解析树,可以将其还原为全括号数学表达式,python代码如下:

#解析树转换为全括号数学表达式
def printexp(tree):
    val = ‘‘
    if tree:
        val = ‘(‘+printexp(tree.getLeftChild())
        val = val +str(tree.getRootVal())
        val = val +printexp(tree.getRightChild())+‘)‘
        if tree.getLeftChild()==None and tree.getRightChild()==None:
            val = val.strip(‘()‘)
    return val

t = buildParseTree("( ( 10 + 5 ) * 3 )")
exp = printexp(t)
print exp

 3,树的遍历

    树的遍历包括前序遍历(preorder),中序遍历(inorder)和后序遍历(postorder).

    前序遍历:先访问根节点,再访问左子树,最后访问右子树(递归),python代码实现如下:

def preorder(tree):
    if tree:
        print tree.getRootVal()
        preorder(tree.getLeftChild())
        preorder(tree.getRightChild())

#定义在类中的前序遍历
# def preorder(self):
#     print self.key
#     if self.leftChild:
#         self.leftChild.preorder()
#     if self.rightChild:
#         self.rightChild.preorder()

preorder

    中序遍历:先访问左子树,再访问根节点,最后访问右子树(递归),python代码实现如下:

#中序遍历inorder
def inorder(tree):
    if tree:
        preorder(tree.getLeftChild())
        print tree.getRootVal()
        preorder(tree.getRightChild())

    后续遍历:先访问左子树,再访问右子树,最后访问根节点,python代码实现如下:

def postorder(tree):
    if tree :
        postorder(tree.getLeftChild())
        postorder(tree.getRightChild())
        print(tree.getRootVal())

4,优先队列和二叉树(priority queue and binary heap)

    优先队列:优先队列和队列类似,enqueue操作能加入元素到队列末尾,dequeue操作能移除队列首位元素,不同的是优先队列的元素具有优先级,首位元素具有最高或最小优先级,因此当进行enqueue操作时,还需要根据元素的优先级将其移动到适合的位置。优先队列一般利用二叉堆来实现,其enqueue和dequeue的复杂度都为O(logn)。(也可以用list来实现,但list的插入复杂度为O(n),再进行排序的复杂度为O(n logn))

    二叉堆:二叉堆是一颗完全二叉树,当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆,当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。(完全二叉树:除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点;满二叉树:除叶子结点外的所有结点均有两个子结点。节点数达到最大值。所有叶子结点必须在同一层上)

    最小堆示例及操作如下:(父节点的值总是小于或等于子节点)

BinaryHeap() #创建空的二叉堆
insert(k)   #插入新元素
findMin()    #返回最小值,不删除
delMin()     #返回最小值,并删除
isEmpty()
size()
buildHeap(list)  #通过list创建二叉堆

                

    对于完全二叉树,若根节点的序号为p,则左右节点的序号应该为2p和2p+1,结合上图可以发现,可以用一个队列(首位元素为0)来表示二叉堆的结构。最小堆的python实现代码如下:(heaplist中第一个元素为0,不会用到,只是为了保证二叉堆的序列从1开始,方便进行除和乘2p,2p+1)

#coding:utf-8

class BinaryHeap(object):
    def __init__(self):
        self.heapList=[0]
        self.size = 0

    #将元素加到完全二叉树末尾,然后再根据其大小调整其位置
    def insert(self,k):
        self.heapList.append(k)
        self.size = self.size+1
        self._percUp(self.size)

    # 如果当前节点比父节点小,和父节点交换位置,一直向上重复该过程
    def _percUp(self,size):
        i = size
        while i>0:
            if self.heapList[i]<self.heapList[i//2]:
                temp = self.heapList[i]
                self.heapList[i] = self.heapList[i//2]
                self.heapList[i//2] = temp
            i=i//2

    # 将根元素返回,并将最末尾元素移动到根元素保持完全二叉树结构不变,再根据大小,将新的根元素向下移动到合适的位置
    def delMin(self):
        temp = self.heapList[1]
        self.heapList[1]=self.heapList[self.size]
        self.size = self.size-1
        self.heapList.pop()
        self._percDown(1)
        return temp

    # 如果当前节点比最小子节点大,和该子节点交换位置,一直向下重复该过程
    def _percDown(self,i):
        while (2*i)<=self.size:
            mc = self._minChild(i)
            if self.heapList[i]>self.heapList[mc]:
                temp = self.heapList[i]
                self.heapList[i]=self.heapList[mc]
                self.heapList[mc] =temp
            i = mc

    #返回左右子节点中较小子节点的位置
    def _minChild(self,i):
        if (2*i+1)>self.size:
            return 2*i
        else:
            if self.heapList[2*i] < self.heapList[2*i+1]:
                return 2*i
            else:
                return 2*i+1

    #通过一个list建立二叉堆
    def buildHeap(self,list):
        i = len(list)//2
        self.heapList = [0]+list[:]
        self.size = len(list)
        while i>0:
            self._percDown(i)
            i = i-1

     insert()插入过程示例图如下:将元素加到完全二叉树末尾,然后再根据其大小调整其位置

    delMin()操作过程示例如下:将根元素返回,并将最末尾元素移动到根元素保持完全二叉树结构不变,再根据大小,将新的根元素向下移动到合适的位置

    insert和delMin的复杂度都为O(log n), buildHeap的复杂度为O(n),利用二叉堆对list进行排序,复杂度为O(n log n),代码如下:

#通过list构造二叉堆,然后不断将堆顶元素返回,就得到排序好的list
alist = [54,26,93,17,98,77,31,44,55,20]
h = BinaryHeap()
h.buildHeap(alist)
s=[]
while h.size>0:
    s.append(h.delMin())
print s

5,二叉搜索树(Binary Search Tree, bst

    二叉搜索树:左节点的值,总是小于其父节点的值,右节点的值总是大于其父节点的值(bst property)。如下图所示:

                    

    利用二叉搜索树实现map(字典),常用操作如下:

Map()   # 创建字典
put(key,val)    #  字典中插入数据
get(key)        #  取键值
del                 # 删除
len()              # 求长度
in              #  是否存在

    python实现map代码如下:

#coding:utf-8

class TreeNode(object):
    def __init__(self,key, value, leftChild=None,rightChild=None,parent=None):
        self.key = key
        self.value = value
        self.leftChild = leftChild
        self.rightChild = rightChild
        self.parent = parent
        self.balanceFactor =0

    def hasLeftChild(self):
        return self.leftChild

    def hasRightChild(self):
        return self.rightChild

    def isLeftChild(self):
        return self.parent and self.parent.leftChild==self

    def isRightChild(self):
        return self.parent and self.parent.rightChild==self

    def isRoot(self):
        return not self.parent

    def isLeaf(self):
        return not (self.leftChild or self.rightChild)

    def hasAnyChildren(self):
        return self.leftChild or self.rightChild

    def hasBothChildren(self):
        return self.leftChild and self.rightChild

    def replaceNodeData(self,key,value,lc=None,rc=None):
        self.key=key
        self.value = value
        self.leftChild = lc
        self.rightChild = rc
        if self.hasLeftChild():
            self.leftChild.parent = self
        if self.hasRightChild():
            self.rightChild = self

    def __iter__(self):
        if self:
            if self.hasLeftChild():
                for elem in self.leftChild:  #调用self.leftChiLd.__iter__(),所以此处是递归的
                    yield elem
            yield self.key, self.value, self.balanceFactor
            if self.hasRightChild():
                for elem in self.rightChild:  #调用self.rightChiLd.__iter__()
                    yield elem

    def findSuccessor(self):  #寻找继承
        succ = None
        if self.hasRightChild():
            succ = self.rightChild._findMin()
        else:
            if self.parent:
                if self.isLeftChild():
                    succ = self.parent
                else:
                    self.parent.rightChild = None
                    succ = self.parent.findSuccessor()
                    self.parent.rightChild = self
        return succ

    def _findMin(self):
        current = self
        while current.hasLeftChild():
            current = current.leftChild
        return current

    def spliceOut(self):
        if self.isLeaf():
            if self.isLeftChild():
                self.parent.leftChild=None
            else:
                self.parent.rightChild=None
        elif self.hasAnyChildren():
            if self.hasLeftChild():
                if self.isLeftChild():
                    self.parent.leftChild = self.leftChild
                else:
                    self.parent.rightChild = self.leftChild
                self.leftChild.parent = self.parent
            else:
                if self.isLeftChild():
                    self.parent.leftChild = self.rightChild
                else:
                    self.parent.rightChild = self.rightChild
                self.rightChild.parent = self.parent

class BinarySearchTree(object):

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

    def length(self):
        return self.size

    def __len__(self):
        return self.size

    def __iter__(self):
        return self.root.__iter__()

    #加入元素
    def put(self,key,value):
        if self.root:
            self._put(key,value,self.root)
        else:
            self.root = TreeNode(key,value)
        self.size = self.size+1

    def _put(self,key,value,currentNode):
        if currentNode.key<key:
            if currentNode.hasRightChild():
                self._put(key,value,currentNode.rightChild)
            else:
                currentNode.rightChild=TreeNode(key,value,parent=currentNode)
        elif currentNode.key>key:
            if currentNode.hasLeftChild():
                self._put(key,value,currentNode.leftChild)
            else:
                currentNode.leftChild=TreeNode(key,value,parent=currentNode)
        else:
            currentNode.replaceNodeData(key,value)

    def __setitem__(self, key, value):
        self.put(key,value)

    #获取元素值
    def get(self,key):
        if self.root:
            node = self._get(key,self.root)
            if node:
                return node.value
            else:
                return None
        else:
            return None

    def _get(self,key,currentNode):
        if not currentNode:
            return None
        if currentNode.key==key:
            return currentNode
        elif currentNode.key<key:
            return self._get(key,currentNode.rightChild)  #rightChild可能不存在
        else:
            return self._get(key,currentNode.leftChild)  #leftChild可能不存在

    # def _get(self,key,currentNode):
    #     if currentNode.key == key:
    #         return currentNode
    #     elif currentNode.key<key:
    #         if currentNode.hasRightChild():
    #             return self._get(key,currentNode.rightChild)
    #         else:
    #             return None
    #     else:
    #         if currentNode.hasLeftChild():
    #             return self._get(key,currentNode.leftChild)
    #         else:
    #             return None

    def __getitem__(self, key):
        return self.get(key)

    def __contains__(self, key): #实现 in 操作
        if self._get(key,self.root):
            return True
        else:
            return False

    def delete(self,key):
        if self.size>1:
            node = self._get(key,self.root)
            if node:
                self._del(node)
                self.size = self.size - 1
            else:
                raise KeyError(‘Error, key not in tree‘)
        elif self.size==1 and self.root.key==key:
            self.root = None
            self.size = self.size - 1
        else:
            raise KeyError(‘Error, key not in tree‘)

    def _del(self,currentNode):
        if currentNode.isLeaf():
            if currentNode.isLeftChild():
                currentNode.parent.leftChild = None
            elif currentNode.isRightChild():
                currentNode.parent.rightChild = None
        elif currentNode.hasBothChildren():
            successor = currentNode.findSuccessor()  #此处successor为其右子树的最小值,即最左边的值
            successor.spliceOut()
            currentNode.key = successor.key
            currentNode.value = successor.value
        elif currentNode.hasAnyChildren():
            if currentNode.hasLeftChild():
                if currentNode.isLeftChild():
                    currentNode.parent.leftChild = currentNode.leftChild
                    currentNode.leftChild.parent = currentNode.parent
                elif currentNode.isRightChild():
                    currentNode.parent.rightChild = currentNode.leftChild
                    currentNode.leftChild.parent = currentNode.parent
                else:  # currentNode has no parent (is root)
                    currentNode.replaceNodeData(currentNode.leftChild.key,
                                        currentNode.leftChild.value,
                                        currentNode.leftChild.leftChild,
                                        currentNode.leftChild.rightChild)
            elif currentNode.hasRightChild():
                if currentNode.isLeftChild():
                    currentNode.parent.leftChild = currentNode.rightChild
                    currentNode.rightChild.parent = currentNode.parent
                elif currentNode.isRightChild():
                    currentNode.parent.rightChild = currentNode.rightChild
                    currentNode.rightChild.parent = currentNode.parent
                else:  # currentNode has no parent (is root)
                    currentNode.replaceNodeData(currentNode.rightChild.key,
                                        currentNode.rightChild.value,
                                        currentNode.rightChild.leftChild,
                                        currentNode.rightChild.rightChild)

    def __delitem__(self, key):
        self.delete(key)
if __name__ == ‘__main__‘:
    mytree = BinarySearchTree()
    mytree[8]="red"
    mytree[4]="blue"
    mytree[6]="yellow"
    mytree[5]="at"
    mytree[9]="cat"
    mytree[11]="mat"

    print(mytree[6])
    print(mytree[5])
    for x in mytree:
        print x

    del mytree[6]
    print ‘-‘*12
    for x in mytree:
        print x

    在上述代码中最复杂的为删除操作,删除节点时有三种情况:节点为叶子节点,节点有两个子节点,节点有一个子节点。当节点有两个子节点时,对其删除时,应该用其右子树的最小值来代替其位置(即右子树中最左边的值)。

    对于map进行复杂度分析,可以发现put,get取决于tree的高度,当节点随机分配时复杂度为O(log n),但当节点分布不平衡时,复杂度会变成O(n),如下图所示:

6, 平衡二叉搜索树 (Balanced binary search tree, AVL tree)

    平衡二叉搜索树:又称为AVL Tree,取名于发明者G.M. Adelson-Velskii 和E.M. Landis,在二叉搜索树的基础上引入平衡因子(balance factor),每次插入和删除节点时都保持树平衡,从而避免上面出现的搜索二叉树复杂度会变成O(n)。一个节点的balance factor的计算公式如下,即该节点的左子树高度减去右子树高度。

    当树所有节点的平衡因子为-1,0,1时,该树为平衡树,平衡因子大于1或小于-1时,树不平衡需要调整,下图为一颗树的各个节点的平衡因子。(1时树left-heavy,0时完全平衡,-1时right-heavy)

    相比于二叉搜索树,AVL树的put和delete操作后,需要对节点的平衡因子进行更新,如果某个节点不平衡时,需要进行平衡处理,主要分为左旋转和右旋转。

    左旋转:如图,节点A的平衡因子为-2(right heavy),不平衡,对其进行左旋转,即以A为旋转点,AB边逆时针旋转。

        详细操作为:1,A的右节点B作为新的子树根节点

              2,A成为B的左节点,如果B有左节点时,将其左节点变为A的右节点(A的右节点原来为B,所以A的右节点现在为空)

    右旋转:如图,节点E的平衡因子为2(left heavy),不平衡,对其进行右旋转,即以E为旋转点,EC边顺时针旋转。

        详细操作为:1,E的左节点C作为新的子树根节点

              2,E成为C的右节点,如果C有右节点时,将其右节点变为E的左节点(E的左节点原来为C,所以E的左节点现在为空)

    特殊情况:当出现下面的情况时,如图所示,A依旧为right heavy,但若进行左旋转,又会出现left heavy,无法完成平衡操作。 所以在进行左旋转和右旋转前需要进行一步判断,具体操作如下:

      1,如果某节点需要进行左旋转平衡时(right heavy),检查其右子节点的平衡因子,若右子节点为left heavy,先对右子节点右旋转,然后对该节点左旋转

      2,如果某节点需要进行右旋转平衡时(left heavy),检查其左子节点的平衡因子,若左子节点为right heavy,先对左子节点左旋转,然后对该节点右旋转

    AVL tree用python实现的代码如下:

#coding:utf-8

from binarySearchTree import TreeNode, BinarySearchTree

# class AVLTreeNode(TreeNode):
#
#     def __init__(self,*args,**kwargs):
#         self.balanceFactor = 0
#         super(AVLTreeNode,self).__init__(*args,**kwargs)

class AVLTree(BinarySearchTree):

    def _put(self,key,value,currentNode):
        if currentNode.key<key:
            if currentNode.hasRightChild():
                self._put(key,value,currentNode.rightChild)
            else:
                currentNode.rightChild=TreeNode(key,value,parent=currentNode)
                self.updateBalance(currentNode.rightChild)
        elif currentNode.key>key:
            if currentNode.hasLeftChild():
                self._put(key,value,currentNode.leftChild)
            else:
                currentNode.leftChild=TreeNode(key,value,parent=currentNode)
                self.updateBalance(currentNode.leftChild)
        else:
            currentNode.replaceNodeData(key,value)

    def _del(self,currentNode):
        if currentNode.isLeaf():
            if currentNode.isLeftChild():
                currentNode.parent.leftChild = None
                currentNode.parent.balanceFactor -=1
            elif currentNode.isRightChild():
                currentNode.parent.rightChild = None
                currentNode.parent.balanceFactor += 1
            if currentNode.parent.balanceFactor>1 or currentNode.parent.balanceFactor<-1:
                self.reblance(currentNode.parent)
        elif currentNode.hasBothChildren():
            successor = currentNode.findSuccessor()  #此处successor为其右子树的最小值,即最左边的值
            # 先更新parent的balanceFactor
            if successor.isLeftChild():
                successor.parent.balanceFactor -= 1
            elif successor.isRightChild():
                successor.parent.balanceFactor += 1
            successor.spliceOut()
            currentNode.key = successor.key
            currentNode.value = successor.value

            # 删除后,再判断是否需要再平衡,然后进行再平衡操作
            if successor.parent.balanceFactor>1 or successor.parent.balanceFactor<-1:
                self.reblance(successor.parent)
        elif currentNode.hasAnyChildren():

            #先更新parent的balanceFactor
            if currentNode.isLeftChild():
                currentNode.parent.balanceFactor -= 1
            elif currentNode.isRightChild():
                currentNode.parent.balanceFactor += 1

            if currentNode.hasLeftChild():
                if currentNode.isLeftChild():
                    currentNode.parent.leftChild = currentNode.leftChild
                    currentNode.leftChild.parent = currentNode.parent
                elif currentNode.isRightChild():
                    currentNode.parent.rightChild = currentNode.leftChild
                    currentNode.leftChild.parent = currentNode.parent
                else:  # currentNode has no parent (is root)
                    currentNode.replaceNodeData(currentNode.leftChild.key,
                                        currentNode.leftChild.value,
                                        currentNode.leftChild.leftChild,
                                        currentNode.leftChild.rightChild)
            elif currentNode.hasRightChild():
                if currentNode.isLeftChild():
                    currentNode.parent.leftChild = currentNode.rightChild
                    currentNode.rightChild.parent = currentNode.parent
                elif currentNode.isRightChild():
                    currentNode.parent.rightChild = currentNode.rightChild
                    currentNode.rightChild.parent = currentNode.parent
                else:  # currentNode has no parent (is root)
                    currentNode.replaceNodeData(currentNode.rightChild.key,
                                        currentNode.rightChild.value,
                                        currentNode.rightChild.leftChild,
                                        currentNode.rightChild.rightChild)
             #删除后,再判断是否需要再平衡,然后进行再平衡操作
            if currentNode.parent!=None: #不是根节点
                if currentNode.parent.balanceFactor>1 or currentNode.parent.balanceFactor<-1:
                    self.reblance(currentNode.parent)

    def updateBalance(self,node):
        if node.balanceFactor>1 or node.balanceFactor<-1:
            self.reblance(node)
            return
        if node.parent!=None:
            if node.isLeftChild():
                node.parent.balanceFactor +=1
            elif node.isRightChild():
                node.parent.balanceFactor -=1
            if node.parent.balanceFactor!=0:
                self.updateBalance(node.parent)

    def reblance(self,node):
        if node.balanceFactor>1:
            if node.leftChild.balanceFactor<0:
                self.rotateLeft(node.leftChild)
            self.rotateRight(node)
        elif node.balanceFactor<-1:
            if node.rightChild.balanceFactor>0:
                self.rotateRight(node.rightChild)
            self.rotateLeft(node)

    def rotateLeft(self,node):
        newroot = node.rightChild
        node.rightChild = newroot.leftChild
        if newroot.hasLeftChild():
            newroot.leftChild.parent = node
        newroot.parent = node.parent
        if node.parent!=None:
            if node.isLeftChild():
                node.parent.leftChild = newroot
            elif node.isRightChild():
                node.parent.rightChild = newroot
        else:
            self.root = newroot
        newroot.leftChild = node
        node.parent = newroot
        node.balanceFactor = node.balanceFactor+1-min(newroot.balanceFactor,0)
        newroot.balanceFactor = newroot.balanceFactor+1+max(node.balanceFactor,0)

    def rotateRight(self,node):
        newroot = node.leftChild
        node.leftChild = newroot.rightChild
        if newroot.rightChild!=None:
            newroot.rightChild.parent = node
        newroot.parent = node.parent
        if node.parent!=None:
            if node.isLeftChild():
                node.parent.leftChild = newroot
            elif node.isRightChild():
                node.parent.rightChild = newroot
        else:
            self.root = newroot
        newroot.rightChild = node
        node.parent = newroot
        node.balanceFactor = node.balanceFactor-1-max(newroot.balanceFactor,0)
        newroot.balanceFactor = newroot.balanceFactor-1+min(node.balanceFactor,0)

if __name__ == ‘__main__‘:

    mytree = AVLTree()
    mytree[8]="red"
    mytree[4]="blue"

    mytree[6]="yellow"

    mytree[5]="at"

    mytree[9]="cat"

    mytree[11]="mat"

    print(mytree[6])
    print(mytree[5])

    print ‘-‘*12
    print (‘key‘,‘value‘,‘balanceFactor‘)
    for x in mytree:
        print x
    print ‘root:‘,mytree.root.key

    del mytree[6]
    print ‘-‘*12
    print (‘key‘,‘value‘,‘balanceFactor‘)
    for x in mytree:
        print x
    print ‘root:‘,mytree.root.key

    AVL Tree继承了二叉搜索树,对其插入和删除方法进行了重写,另外对TreeNode增加了balanceFactor属性。再进行左旋转和右旋转时,对于balanceFactor的需要计算一下,如图的左旋转过程中,D成为了新的根节点,只有B和D的平衡因子发生了变化,需要对其进行更新。(右旋转和左旋转类似)

      B的平衡因子计算过程如下:(newBal(B)为左旋转后B的平衡因子,oldBal(B)为原来的节点B的平衡因子,h为节点的高度)

      D的平衡因子计算过程如下:

    

    由于AVL Tree总是保持平衡,其put和get操作的复杂度能保持为O(log n)

7.总结

    到目前为止,对于map(字典)数据结构,用二叉搜索树和AVL树实现了,也用有序列表和哈希表实现过,对应操作的复杂度如下:

参考:http://interactivepython.org/runestone/static/pythonds/Trees/toctree.html

原文地址:https://www.cnblogs.com/silence-cho/p/10056097.html

时间: 2024-08-28 01:30:56

树及其衍生算法(Trees and tree algorithms)的相关文章

树状数组(Binary Indexed Tree,BIT)

树状数组(Binary Indexed Tree) 前面几篇文章我们分享的都是关于区间求和问题的几种解决方案,同时也介绍了线段树这样的数据结构,我们从中可以体会到合理解决方案带来的便利,对于大部分区间问题,线段树都有其绝对的优势,今天这篇文章,我们就来欣赏由线段树变形的另外一个数据结构--树状数组,树状数组通常也用于解决区间求和.单点更新的问题,而且效率比线段树高一些(树状数组区间求和和单点更新的时间复杂度均为o(log n)),相对而言,线段树的应用范围可能更广泛一些.但不得不承认,树状数组确

leetcode中关于树的dfs算法题

Validate Binary Search Tree Recover Binary Search Tree Symmetric Tree Same Tree Maximum Depth of Binary Tree Construct Binary Tree from Preorder and Inorder Traversal Construct Binary Tree from Inorder and Postorder Traversal Convert Sorted Array to

深入学习高级非线性回归算法 --- 树回归系列算法

前言 前文讨论的回归算法都是全局且针对线性问题的回归,即使是其中的局部加权线性回归法,也有其弊端(具体请参考前文:) 采用全局模型会导致模型非常的臃肿,因为需要计算所有的样本点,而且现实生活中很多样本都有大量的特征信息. 另一方面,实际生活中更多的问题都是非线性问题. 针对这些问题,有了树回归系列算法. 回归树 在先前决策树 (链接) 的学习中,构建树是采用的 ID3 算法.在回归领域,该算法就有个问题,就是派生子树是按照所有可能值来进行派生. 因此 ID3 算法无法处理连续性数据. 故可使用二

hdu3078 建层次树+在线LCA算法+排序

题意:n个点,n-1条边构成无向树,每个节点有权,Q次询问,每次或问从a->b的最短路中,权第k大的值,/或者更新节点a的权, 思路:在线LCA,先dfs生成树0,标记出层数和fa[](每个节点的父亲节点).在对每次询问,走一遍一次公共祖先路上 的权,保持,快排.n*logn*q #include<iostream> //187MS #include<algorithm> #include<cstdio> #include<vector> using

树常见的算法操作

树是数据结构中很重要的一部分,也是各大公司面试常考部分. 继树的各种遍历算法之后,今天又整理一下树的常见算法操作. 本文包括: 1.求节点的最近公共祖先 2.树的序列化与反序列化 3.已知先序遍历和中序遍历构造二叉树 4.已知中序遍历和后序遍历构造二叉树 1.求节点最近的公共祖先 此题不同的要求有不同的解法 如果已知树中的每一个结点有指向父节点的指针: 思路:从给定节点遍历到根节点,当父节点相等时返回. 解法1 private ArrayList<TreeNode> getPath2Root(

Leetcode 树 Unique Binary Search Trees

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie Unique Binary Search Trees Total Accepted: 13478 Total Submissions: 37858 Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For example, Given n = 3, there

POJ1990 MooFest 树状数组(Binary Indexed Tree,BIT)

N头牛排成一列,每头牛的听力是Vi,每头牛的位置Pi,任意两头牛i,j相互交流时两头牛都至少需要发出声音的大小为max(Vi,Vj) * |Pi-Pj|,求这N头牛两两交流总共发出的声音大小是多少.N,V,P都是1-20000的范围. 这题首先对Vi从小到大进行排序,排序过后就可以依次计算i,将所有比Vi小的牛到i之间的距离之和乘以Vi得到Ri,然后累加Ri就是最终结果.问题是Ri具体该如何求. 假设听力比Vi小的牛并且位置也比Pi小的牛的个数为Ci,并且这些距离之和为Si,听力比Vi小的所有牛

提升树之Adaboost算法的介绍

主要内容: 模型介绍 损失函数的介绍 Adaboost分类算法步骤 Adaboost回归算法步骤 Adaboost函数语法 提升树之Adaboost算法的应用——信用卡是否违约的识别 1.模型介绍 提升树算法与线性回归模型的思想类似,所不同的是该算法实现了多棵基础决策树??(??)的加权运算,最具代表的提升树为AdaBoost算法,即 2.损失函数的介绍 对于Adaboost算法而言,每一棵基础决策树都是基于前一棵基础决策树的分类结果对样本点设置不同的权重,如果在前一棵基础决策树中将某样本点预测

图及其衍生算法(Graphs and graph algorithms)

1. 图的相关概念 树是一种的图,相比树,图更能用来表示现实世界中的的实体,如路线图,网络节点图,课程体系图等,一旦能用图来描述实体,能模拟和解决一些非常复杂的任务.图的相关概念和词汇如下: 顶点vertex:图的节点 边Edge:顶点间的连线,若边具有方向时,组成有向图(directed graph) 权重weight:从一个顶点到其他不同顶点的距离不一样,因此边具有权重,来表示不同的距离 路径path:从一个顶点到另一个的所有边的集合 回路cycle:在有向图中,从一个顶点开始,最后又回到起