13.二叉树基础

 

二叉树基础(上):什么样的二叉树适合用数组来存储?

 树,总共包含4节内容。具体如下:
1.树、二叉树
2.二叉查找树
3.平衡二叉树、红黑树
4.递归树

一、树
1.树的常用概念
根节点、叶子节点、父节点、子节点、兄弟节点,还有节点的高度、深度以及层数,树的高度。
2.概念解释
节点:树中的每个元素称为节点
父子关系:相邻两节点的连线,称为父子关系
根节点:没有父节点的节点
叶子节点:没有子节点的节点
父节点:指向子节点的节点
子节点:被父节点指向的节点
兄弟节点:具有相同父节点的多个节点称为兄弟节点关系
节点的高度:节点到叶子节点的最长路径所包含的边数
节点的深度:根节点到节点的路径所包含的边数
节点的层数:节点的深度+1(根节点的层数是1)
树的高度:等于根节点的高度


二、二叉树(最常用)
1.概念
①什么是二叉树?
每个节点最多只有2个子节点的树,这两个节点分别是左子节点和右子节点。
②什么是满二叉树?
有一种二叉树,除了叶子节点外,每个节点都有左右两个子节点,这种二叉树叫做满二叉树。
③什么是完全二叉树?
有一种二叉树,叶子节点都在最底下两层,最后一层叶子节都靠左排列,并且除了最后一层,其他层的节点个数都要达到最大,这种二叉树叫做完全二叉树。
2.完全二叉树的存储
①链式存储
每个节点由3个字段,其中一个存储数据,另外两个是指向左右子节点的指针。我们只要拎住根节点,就可以通过左右子节点的指针,把整棵树都串起来。这种存储方式比较常用,大部分二叉树代码都是通过这种方式实现的。
②顺序存储
用数组来存储,对于完全二叉树,如果节点X存储在数组中的下标为i,那么它的左子节点的存储下标为2*i,右子节点的下标为2*i+1,反过来,下标i/2位置存储的就是该节点的父节点。注意,根节点存储在下标为1的位置。完全二叉树用数组来存储时最省内存的方式。

 

3.二叉树的遍历(面试常见)
①前序遍历:对于树中的任意节点来说,先打印这个节点,然后再打印它的左子树,最后打印它的右子树。
②中序遍历:对于树中的任意节点来说,先打印它的左子树,然后再打印它的本身,最后打印它的右子树。
③后序遍历:对于树中的任意节点来说,先打印它的左子树,然后再打印它的右子树,最后打印它本身。
前序遍历的递推公式:
preOrder(r) = print r->preOrder(r->left)->preOrder(r->right)
中序遍历的递推公式:
inOrder(r) = inOrder(r->left)->print r->inOrder(r->right)
后序遍历的递推公式:
postOrder(r) = postOrder(r->left)->postOrder(r->right)->print r
时间复杂度:3种遍历方式中,每个节点最多会被访问2次,所以时间复杂度是O(n)。

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

def preTraverse(root):
    if root == None:
        return
    print(root.value,end=" ")
    preTraverse(root.left)
    preTraverse(root.right)

def midTraverse(root):
    if root == None:
        return
    midTraverse(root.left)
    print(root.value,end=" ")
    midTraverse(root.right)

def afterTraverse(root):
    if root == None:
        return
    afterTraverse(root.left)
    afterTraverse(root.right)
    print(root.value,end=" ")

if __name__ == "__main__":
    root = Node("A",Node("B",right=Node("F",right=Node("C"))),Node("G",Node("D",Node("E"))))
    preTraverse(root)
    print()
    midTraverse(root)
    print()
    afterTraverse(root)

 


三、思考
1.二叉树有哪几种存储方式?什么样的二叉树适合用数组来存储?
2.给定一组数据,比如1,3,5,6,9,10.你来算算,可以构建出多少种不同的二叉树?
3.我们讲了三种二叉树的遍历方式,前、中、后序。实际上,还有另一种遍历方式,也就是按层遍历,你知道如何实现吗?
4.如何用循环实现二叉树的遍历?

 

二叉树基础(下):有了如此高效的散列表,为什么还需要二叉树?

二叉查找(搜索)树支持动态数据集合的快速插入、删除、查找操作。散列表也是支持这些操作的,并且散列表的这些操作比二叉查找树更高效,时间复杂度是 O(1)。既然有了这么高效的散列表,使用二叉树的地方是不是都可以替换成散列表呢?有没有哪些地方是散列表做不了,必须要用二叉树来做的呢?

 

二叉查找树要求,在树中的任意一个节点,其左子树中的每个节点的值,都要小于这个节点的值,而右子树节点的值都大于这个节点的值。

 

class TreeNode(object):
    def __init__(self,val,left=None,right=None):
        self.val=val
        self.left=left
        self.right=right
class BST(object):
    def __init__(self,node_list):
        self.root =TreeNode(node_list[0])
        for data in node_list[1:]:
            self.insert(self.root,data)
    def query(self,root,val):#遍历root,并返回遍历结果
        if root == None:#递归停止的条件
            return
        if root.val is val:
            return 1
        if root.val >val:
            return self.query(root.left,val)#遍历root.left,并返回遍历结果
        else:
            return self.query(root.right,val)

    def insert(self,root,val):#对整个root完成插入
        if root == None:
            root = TreeNode(val)
        else:
            if root.val>val:
                root.left=self.insert(root.left,val)#对整个root.left完成插入,需要root.left从新指向
            elif root.val<val:
                root.right = self.insert(root.right,val)
        return root#对整个root完成插入,需要root从新指向

    def findMax(self,root):
        if root.right !=None:
            return self.findMax(root.right)
        else:
            return root

    def findMin(self,root):
        if root.left !=None:
            return self.findMin(root.left)
        else:
            return root

    def delete(self,root,val):
        """
        关于二叉查找树的删除操作,还有个非常简单、取巧的方法,就是单纯将要删除的节点标记为“已删除”,但是并不真正从树中将这个节
        点去掉。这样原本删除的节点还需要存储在内存中,比较浪费内存空间,但是删除操作就变得简单了很多。而且,这种处理方法也并没有
        增加插入、查找操作代码实现的难度。
        """
        if root == None:
            return
        if root.val<val:
            root.right = self.delete(root.right,val)
        elif root.val>val:
            root.left =self.delete(root.left,val)
        else:
            if root.left !=None and root.right!=None:
                temp = self.findMin(root.right)
                root.val = temp.val
                root.right = self.delete(root.right,temp.val)
            else:
                if root.left == None:
                    root = root.right
                elif root.right == None:
                    root = root.left
        return root


    def midTraverse(self,root):
        if root != None:
            self.midTraverse(root.left)
            print(root.val,end=" ")
            self.midTraverse(root.right)

if __name__ == "__main__":
    a = [49, 38, 65, 97, 60, 76, 13, 27, 5, 1]
    bst = BST(a)  # 创建二叉查找树
    bst.midTraverse(bst.root)  # 中序遍历
    print()
    print(bst.findMin(bst.root).val)
    bst.delete(bst.root,97)
    bst.midTraverse(bst.root)  # 中序遍历
    bst.insert(bst.root, 97)
    print()
    bst.midTraverse(bst.root)  # 中序遍历

二叉查找树的时间复杂度分析

不管操作是插入、删除还是查找,时间复杂度其实都跟树的高度成正比,也就是 O(height)。

对于完全二叉查找树,若节点个数为n,那么n满足:

n >= 1+2+4+8+...+2^(L-2)+1
n <= 1+2+4+8+...+2^(L-2)+2^(L-1)

也就是说,完全二叉树的高度小于等于 log2n。

 

class TreeNode:
    def __init__(self, x):
        self.left = None
        self.right = None
        self.val = x
class Solution(object):
    def __init__(self):
        self.listHead = None
        self.listTail = None

    def convert(self,root):
        """
        将二叉树转换为有序双向链表
        :param root: 
        :return: 
        """
        if root == None:
            return
        self.convert(root.left)
        if self.listHead == None:
            self.listHead = root
            self.listTail = root
        else:
            self.listTail.right = root
            root.left = self.listTail
            self.listTail = root
        self.convert(root.right)
        return self.listHead

    def printList(self,head):
        """
        获得链表的正向序和反向序
        :param head:
        :return:
        """
        while head.right:
            print(head.val,end=" ")
            head = head.right
        print(head.val)
        while head:
            print(head.val,end=" ")
            head =head.left
        print()
    def get_BST_with_pre_mid(self,pre,mid):
        """
        给定二叉树的前序遍历和中序遍历,获得该二叉树
        :param pre:
        :param mid:
        :return:
        """
        if len(pre) ==0 or len(mid)==0:
            return None

        root_c = TreeNode(pre[0])
        for order,item in enumerate(mid):
            if root_c.val == item:
                root_c.left = self.get_BST_with_pre_mid(pre[1:order+1],mid[:order])
                root_c.right = self.get_BST_with_pre_mid(pre[order+1:],mid[order+1:])
                return root_c

if __name__ == '__main__':
    solution = Solution()
    preorder_seq = [4,2,1,3,6,5,7]
    middleorder_seq = [1,2,3,4,5,6,7]
    root = solution.get_BST_with_pre_mid(preorder_seq, middleorder_seq)
    head = solution.convert(root)
    solution.printList(head)

 

posted on 2019-02-17 22:32  wzc521  阅读(141)  评论(0)    收藏  举报

导航