二叉树

16 篇文章 0 订阅
5 篇文章 0 订阅

一、基础知识总结

树的定义

树是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。
树具有的特点:
(1)每个结点有零个或多个子结点
(2)没有父节点的结点称为根节点
(3)每一个非根结点有且只有一个父节点
(4)除了根结点外,每个子结点可以分为多个不相交的子树。

若一个结点有子树,那么该结点称为子树根的“双亲”,子树的根称为该结点的“孩子”。有相同双亲的结点互为“兄弟”。一个结点的所有子树上的任何结点都是该结点的后裔。从根结点到某个结点的路径上的所有结点都是该结点的祖先。

树的基本术语:
结点的度:结点拥有的子树的数目
叶子结点:度为0的结点
分支结点:度不为0的结点
树的度:树中结点的最大的度
层次:根结点的层次为1,其余结点的层次等于该结点的双亲结点的层次加1
树的高度:树中结点的最大层次
森林:0个或多个不相交的树组成。对森林加上一个根,森林即成为树;删去根,树即成为森林。

二叉树

1、二叉树的定义

二叉树是每个结点最多有两个子树的树结构。它有五种基本形态:二叉树可以是空集;根可以有空的左子树或右子树;或者左、右子树皆为空。
这里写图片描述

2、二叉树的性质

性质1:二叉树第i层上的结点数目最多为 2i1(i>=1) 2 i − 1 ( i >= 1 )
性质2:深度为k的二叉树最多有 2k1k>=1 2 k − 1 ( k >= 1 ) 个结点
性质3:包含n个结点的二叉树的高度至少为 (log2n)+1 ( l o g 2 n ) + 1
性质4:在任意一棵二叉树中,若终端结点的个数为 N0 N 0 ,度为2的结点数为 N2 N 2 ,则 N0=N2+1 N 0 = N 2 + 1

性质4的证明
因为二叉树中所有结点的度数均不大于2,不妨设 N0 N 0 表示度为0的结点个数, N1 N 1 表示度为1的结点个数, N2 N 2 表示度为2的结点个数。三类结点加起来为总结点个数,于是便可得到: N=N0+N1+N2 N = N 0 + N 1 + N 2 (1)
由度之间的关系可得第二个等式: N=N00+N11+N22+1 N = N 0 ∗ 0 + N 1 ∗ 1 + N 2 ∗ 2 + 1
N=N1+2N2+1 N = N 1 + 2 N 2 + 1 (2)
将(1)(2)组合在一起可得到 N0=N2+1 N 0 = N 2 + 1

满二叉树、完全二叉树和二叉查找树

1、满二叉树

定义:高度为h,并且由 2h1 2 h − 1 个结点组成的二叉树,称为满二叉树

2、完全二叉树

定义:一棵二叉树中,只有最下面两层结点的度可以小于2,并且最下层的叶子结点集中在靠左的若干位置上,这样的二叉树称为完全二叉树。

特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。

一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。
如果一棵完全二叉树的结点总数为n,那么叶子结点等于 n/2 n / 2 (当n为偶数时)或者 (n+1)/2 ( n + 1 ) / 2 (当n为奇数时)

3、二叉查找树

定义:二叉查找树又被称为二叉搜索树。设x为二叉查找树中的一个结点,x结点包含关键字key,结点x的key值计为key[x]。如果y是x的左子树中的一个结点,则key[y]<=key[x];如果y是x的右子树的一个结点,则key[y]>=key[x]

在二叉查找树中:
(1)若任意结点的左子树不空,则左子树上所有结点的值均小于它的根结点的值。
(2)若任意结点的右子树不空,则右子树上所有结点的值均大于它的根结点的值。
(3)任意结点的左、右子树也分别为二叉查找树。
(4)没有键值相等的结点。

二叉树的遍历方式

二叉树的四种遍历方式,前序遍历,中序遍历,后序遍历和按层遍历。
前序遍历的排列方式为:根>左>右
中序遍历的排列方式为:左>根>右
后序遍历的排列方式为:左>右>根
按层遍历,从上往下,从左往右

二叉树代码实现

# coding=utf-8

class Node(object):
    """节点类"""
    def __init__(self, data=-1, lchild=None, rchild=None):
        self.data = data
        self.lchild = lchild
        self.rchild = rchild
        self.child_number = 0
        self.parent = None
        self.name = None

    def add_Rchild(self, node):
        if self.rchild is not None:
            self.rchild = node
        else:
            self.rchild = node
            self.child_number += 1
        node.set_parent(self)

    def drop_Rchild(self):
        self.rchild = None
        self.child_number -= 1

    def set_parent(self, node):
        self.parent = node

    def add_Lchild(self, node):
        if self.lchild is not None:
            self.lchild = node
        else:
            self.lchild = node
            self.child_number += 1
        node.set_parent(self)

    def drop_Lchild(self):
        self.lchild = None
        self.child_number -= 1

class Tree(object):
    """树类"""

    def __init__(self):
        self.root = Node()
        self.myQueue = []
        self.size = 1

        # 存放各层节点数目
        self.n = []
        # 初始化层,否则列表访问无效
        for item in range(pow(2, 5)):
            self.n.append(0)
        # 索引标识
        self.maxwidth = 0
        self.i = 0

    def width(self, root):
        """二叉树的最大宽度"""
        if root is None:
            return
        else:
            # 如果是访问根节点
            if self.i == 0:
                # 第一层加一
                self.n[0] = 1
                # 到达第二层
                self.i += 1
                if root.lchild:
                    self.n[self.i] += 1
                if root.rchild:
                    self.n[self.i] += 1
                # print('临时数据:', self.n)
            else:
                # 访问子树
                self.i += 1
                # print('二叉树所在层数:', self.i)
                if root.lchild:
                    self.n[self.i] += 1
                if root.rchild:
                    self.n[self.i] += 1
            # 开始判断, 取出最大值
            # maxwidth = max(maxwidth, n[i])
            # maxwidth.append(max(max(maxwidth), n[i]))
            self.maxwidth = max(self.maxwidth, self.n[self.i])
            # 遍历左子树
            self.width(root.lchild)
            # 往上退一层
            self.i -= 1
            # 遍历右子树
            self.width(root.rchild)

            return self.maxwidth

    def height(self, root):
        """二叉树的最大高度"""
        if not root:
            return 0
        ldeepth = self.height(root.lchild)
        rdeepth = self.height(root.rchild)
        return max(ldeepth + 1, rdeepth + 1)

    def deepth(self, root):
        """二叉树的最大深度"""
        return self.height(root) - 1

    def getsize(self):
        """二叉树包含的节点数目"""
        stack = [self.root]
        # 为了正确获取数目,这里需要先初始化一下
        self.size = 0
        while stack:
            temp = stack.pop(0)
            self.size += 1
            if temp.lchild:
                stack.append(temp.lchild)
            if temp.rchild:
                stack.append(temp.rchild)
        return self.size

    def add(self, data):
        """为树添加节点"""
        node = Node(data)
        if self.root.data == -1:  # 如果树是空的,则对根节点赋值
            self.root = node
            self.myQueue.append(self.root)
        else:
            treeNode = self.myQueue[0]  # 此结点的子树还没有齐。
            if treeNode.lchild is None:
                treeNode.lchild = node
                self.myQueue.append(treeNode.lchild)
            else:
                treeNode.rchild = node
                self.myQueue.append(treeNode.rchild)
                self.myQueue.pop(0)  # 如果该结点存在右子树,将此结点丢弃。

    def front_digui(self, root):
        """利用递归实现树的前序遍历"""
        if root is None:
            return
        print(root.data, end='\t')
        self.front_digui(root.lchild)
        self.front_digui(root.rchild)

    def middle_digui(self, root):
        """利用递归实现树的中序遍历"""
        if root is None:
            return
        self.middle_digui(root.lchild)
        print(root.data, end='\t')
        self.middle_digui(root.rchild)

    def later_digui(self, root):
        """利用递归实现树的后序遍历"""
        if root is None:
            return
        self.later_digui(root.lchild)
        self.later_digui(root.rchild)
        print(root.data, end='\t')

    def front_stack(self, root):
        """利用堆栈实现树的前序遍历"""
        if root is None:
            return
        myStack = []
        node = root
        while node or myStack:
            while node:  # 从根节点开始,一直找它的左子树
                print(node.data, end='\t')
                myStack.append(node)
                node = node.lchild
            node = myStack.pop()  # while结束表示当前节点node为空,即前一个节点没有左子树了
            node = node.rchild  # 开始查看它的右子树

    def middle_stack(self, root):
        """利用堆栈实现树的中序遍历"""
        if root is None:
            return
        myStack = []
        node = root
        while node or myStack:
            while node:  # 从根节点开始,一直找它的左子树
                myStack.append(node)
                node = node.lchild
            node = myStack.pop()  # while结束表示当前节点node为空,即前一个节点没有左子树了
            print(node.data, end='\t')
            node = node.rchild  # 开始查看它的右子树

    def later_stack(self, root):
        """利用堆栈实现树的后序遍历"""
        if root is None:
            return
        myStack1 = []
        myStack2 = []
        node = root
        myStack1.append(node)
        while myStack1:  # 这个while循环的功能是找出后序遍历的逆序,存在myStack2里面
            node = myStack1.pop()
            if node.lchild:
                myStack1.append(node.lchild)
            if node.rchild:
                myStack1.append(node.rchild)
            myStack2.append(node)
        while myStack2:  # 将myStack2中的元素出栈,即为后序遍历次序
            print(myStack2.pop().data, end='\t')

    def level_queue(self, root):
        """利用队列实现树的层次遍历"""
        if root is None:
            return
        myQueue = []
        node = root
        myQueue.append(node)
        while myQueue:
            node = myQueue.pop(0)
            print(node.data, end='\t')
            if node.lchild is not None:
                myQueue.append(node.lchild)
            if node.rchild is not None:
                myQueue.append(node.rchild)

if __name__ == '__main__':
    """主函数"""
    elems = range(10)  # 生成十个数据作为树节点
    tree = Tree()  # 新建一个树对象
    for elem in elems:
        tree.add(elem)  # 逐个添加树的节点
    print('队列实现层次遍历:')
    tree.level_queue(tree.root)
    print('\n\n递归实现前序遍历:')
    tree.front_digui(tree.root)
    print('\n递归实现中序遍历:')
    tree.middle_digui(tree.root)
    print('\n递归实现后序遍历:')
    tree.later_digui(tree.root)
    print('\n\n堆栈实现前序遍历:')
    tree.front_stack(tree.root)
    print('\n堆栈实现中序遍历:')
    tree.middle_stack(tree.root)
    print('\n堆栈实现后序遍历:')
    tree.later_stack(tree.root)
    print('\n递归实现树的节点数:')
    print(tree.getsize())
    print('\n递归实现树的高度:')
    print(tree.height(tree.root))
    print('\n递归实现树的深度:')
    print(tree.deepth(tree.root))
    print('\n递归实现树的宽度:')
    print(tree.width(tree.root))

霍夫曼树实现

霍夫曼树又称最优二叉树,是一种带路径长度最短的二叉树。
所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。
树的路径长度是从树根到每一结点的路径长度之和,记为 WPL=W1L1+W2L2+W3L3+...+WnLn W P L = ( W 1 ∗ L 1 + W 2 ∗ L 2 + W 3 ∗ L 3 + . . . + W n ∗ L n ) ,N个权值Wi(i=1,2,…n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,…n)。可以证明霍夫曼树的WPL是最小的。

霍夫曼树具有如下特性:
对于同一组权值,所能得到的霍夫曼树不一定是唯一的。
霍夫曼树的左右子树可以互换,因为这并不影响树的带权路径长度。
带权值的节点都是叶子节点,不带权值的节点都是某棵子二叉树的根节点。
权值越大的节点越靠近霍夫曼树的根节点,权值越小的节点越远离霍夫曼树的根节点。
霍夫曼树中只有叶子节点和度为2的节点,没有度为1的节点。
一棵有n个叶子节点的霍夫曼树共有2n-1个节点。
霍夫曼树的构建步骤如下:
1、将给定的n个权值看做n棵只有根节点(无左右孩子)的二叉树,组成一个集合HT,每棵树的权值为该节点的权值。
2、从集合HT中选出2棵权值最小的二叉树,组成一棵新的二叉树,其权值为这2棵二叉树的权值之和。
3、将步骤2中选出的2棵二叉树从集合HT中删去,同时将步骤2中新得到的二叉树加入到集合HT中。
4、重复步骤2和步骤3,直到集合HT中只含一棵树,这棵树便是霍夫曼树。
class Node(object):
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = None
        self.right = None

class Huffman(object):
    def __init__(self, items=[]):
        while len(items) != 1:
            a, b = items[0], items[1]
            newvalue = a.value + b.value
            newnode = Node(value=newvalue)
            newnode.left, newnode.right = a, b
            items.remove(a)
            items.remove(b)
            items.append(newnode)
            items = sorted(items, key=lambda node: int(node.value))
            # 每次都要记得更新新的霍夫曼树的根节点
            self.root = newnode

    def print(self):
        queue = [self.root]
        while queue:
            current = queue.pop(0)
            print(current.value, end='\t')
            if current.left:
                queue.append(current.left)
            if current.right:
                queue.append(current.right)

def create_huffman_tree(lists):
    while len(lists) > 1:
        a, b = lists[0], lists[1]
        node = Node(value=int(a.value+b.value))
        node.left, node.right = a, b
        lists.remove(a)
        lists.remove(b)
        lists.append(node)
        lists = sorted(lists, key=lambda node: node.value)
    return lists

def scan(root):
    if root:
        queue = [root]
        while queue:
            current = queue.pop(0)
            print(current.value, end='\t')
            if current.left:
                queue.append(current.left)
            if current.right:
                queue.append(current.right)

if __name__ == '__main__':
    ls = [Node(i) for i in range(1, 10)]
    huffman = Huffman(items=ls)
    huffman.print()
    print()
    ls = [Node(i) for i in range(1, 10)]
    root = create_huffman_tree(ls)[0]
    scan(root)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值