Python数据结构之二叉树

一、基本概念

二叉树的概念继承自。树是一种重要的非线性数据结构,在文件系统、图形用户界面、数据库、网站等系统中有着极其广泛的应用。一棵树的定义如下:

通常我们将 T T T 定义为存储一系列元素的有限节点集合,这些节点具有父亲-儿子关系并且满足如下属性:

  • 如果树 T T T 不为空,则它一定具有一个称为根节点的特殊节点,并且该节点没有父节点。
  • 每个非根节点 v v v 都具有唯一的父亲节点 w w w ,每个具有父节点 w w w 的节点都是节点 w w w 的一个儿子

此外,定义同一个父节点的儿子节点之间是兄弟关系。一个没有儿子的节点称为外部节点叶子节点。一个有一个或多个儿子的节点称为内部节点。节点之间还可以同理定义祖先子孙关系等。

对于树 T T T 的一个节点 p p p ,其儿子节点的数量称为 p p p 节点的。显然,叶子节点的度都为零。一棵树的度定义为其所有节点度的最大值。树还具有深度高度。假定 p p p 是树 T T T 中的一个节点,那么 p p p 的深度就是节点 p p p 的祖先个数,不包括 p p p 本身。一个节点的高度定义为其子节点中的最大高度加一,且叶子节点的高度为零。一棵非空树的高度等于其所有叶子节点深度的最大值。具有相同深度的节点称为在同一。定义一棵树的宽度为每一层节点数量的最大值。

而二叉树作为一种特殊的树,除了具有树的属性之外,还有以下规则:

  1. 每个节点最多有两个儿子节点。
  2. 每个儿子节点被命名为左儿子右儿子
  3. 对于每个节点的儿子节点,在顺序上,左儿子先于右儿子。

如果除了最后一个叶子节点的父节点以外,每个节点都有零个或两个子节点,则称这样的二叉树为完全二叉树。如果除了叶子节点之外的每一个节点都有两个儿子,且每一层都被填满,则称这样的二叉树为完美二叉树。如果除了叶子节点之外的每一个节点都有两个儿子,则称这样的二叉树为满二叉树

若树的儿子之间有顺序,则称这种树为有序树。有序树支持前序遍历后序遍历层次遍历三种遍历方式。二叉树还多支持一个中序遍历。其中,前中后表示根节点的遍历位置,前序遍历就是先遍历根节点然后遍历左子树和右子树,中序遍历就是先遍历左子树再遍历根节点然后遍历右子树,后序遍历同理。层次遍历就是一层一层地从左到右遍历。

二、代码实现

下面将尝试用Python来实现二叉树的数据结构。
二叉树有链式存储法和顺序存储法。本文将采用链式存储。

首先是节点类:

class Node:
    __slots__ = '_value', '_father', '_lson', '_rson'
    def __init__(self, value=None, father=None, lson=None, rson=None):
        self._value = value
        self._father = father
        self._lson = lson
        self._rson = rson
    def father(self):
        return self._father
    def lson(self):
        return self._lson
    def rson(self):
        return self._rson

然后是二叉树主类。由于我们只要抓住了二叉树的根节点,就可以根据节点间的父亲-儿子关系进行遍历。所以二叉树类只要存储根节点即可。

class BinaryTree:
    def __init__(self, root=None):
        self._root = Node(root)
    def root(self):
        return self._root

至此,在逻辑上,我们已经建立了一棵二叉树。不过现在它还很瘦弱,我们需要向其中添加一些方法来使它健壮起来。

由高度、深度、宽度的定义,可写出以下方法来分别求出:

##    Node
    def degree(self):
        return sum(1 for son in self.sons())
    def height(self):
        return 1 + max(son.height() for son in self.sons()) if self.degree() else 0
    def depth(self):
        return 1 + self._father.depth() if self._father else 0
##    BinaryTree
    def height(self):
        return self.root().height()
    def degree(self):
        return max(node.degree() for node in self._nodes())

其中self.sons()self._nodes()方法是生成器,实现方式如下:

##    Node
    def sons(self):
        for son in (self._lson, self._rson):
            if son:
                yield son
##    BinaryTree
    def _nodes(self):
        return self.preorder()

这里_nodes()直接调用self.preorder()方法,即按前序遍历顺序给出结点,实际操作中可以根据需要自行调整。以下是前、中、后、层,四种遍历的实现方式:

##    BinaryTree
    def preorder(self):
        for node in self._preorder_aux(self._root):
            yield node
    def _preorder_aux(self, root):
        yield root
        for son in root.sons():
            for other in self._preorder_aux(son):
                yield other
    def postorder(self):
        for node in self._postorder_aux(self._root):
            yield node
    def _postorder_aux(self, root):
        for son in root.sons():
            for other in self._postorder_aux(son):
                yield other
        yield root
    def inorder(self):
        for node in self._inorder_aux(self._root):
            yield node
    def _inorder_aux(self, root):
        if root.lson():
            for other in self._inorder_aux(root.lson()):
                yield other
        yield root
        if root.rson():
            for other in self._inorder_aux(root.rson()):
                yield other
    def levelorder(self):
        queue = [self.root()]
        while len(queue):
            node = queue.pop(0)
            yield node
            for son in node.sons():
                queue.append(son)

此外,不能打印的二叉树是不够形象的,因此我们还要重载一下__repr__()方法和__str__()方法:

##    Node
    def __repr__(self):
        return str(self._value)
    def __str__(self):
        lines = _tree_print_aux(self)[0]
        return '\n'.join(line.rstrip() for line in lines)
##    BinaryTree
    def __repr__(self):
        return self._root.__repr__()
    def __str__(self):
        return self._root.__str__()

其中的_tree_print_aux()方法是用来辅助输出的,该方法借鉴自官方模块binarytree_build_tree_string()函数。实现如下:

def _tree_print_aux(root, curr=0):
    if root is None:
        return [], 0, 0, 0
    line_l = []
    line_r = []
    node_repr = str(root._value)
    new_root_width = gap_size = len(node_repr)
    l_box, l_box_width, l_root_start, l_root_end = \
        _tree_print_aux(root._lson, curr<<1|1)
    r_box, r_box_width, r_root_start, r_root_end = \
        _tree_print_aux(root._rson, (curr<<1) + 2)
    if l_box_width > 0:
        l_root = ((l_root_start + l_root_end)>>1) + 1
        line_l.append(' ' * (l_root + 1))
        line_l.append('_' * (l_box_width - l_root))
        line_r.append(' ' * l_root + '/')
        line_r.append(' ' * (l_box_width - l_root))
        new_root_start = l_box_width + 1
        gap_size += 1
    else:
        new_root_start = 0
    line_l.append(node_repr)
    line_r.append(' ' * new_root_width)
    if r_box_width > 0:
        r_root = (r_root_start + r_root_end)>>1
        line_l.append('_' * r_root)
        line_l.append(' ' * (r_box_width - r_root + 1))
        line_r.append(' ' * r_root + '\\')
        line_r.append(' ' * (r_box_width - r_root))
        gap_size += 1
    new_root_end = new_root_start + new_root_width - 1
    gap = ' ' * gap_size
    new_box = [''.join(line_l), ''.join(line_r)]
    for i in range(max(len(l_box), len(r_box))):
        l_line = l_box[i] if i < len(l_box) else ' ' * l_box_width
        r_line = r_box[i] if i < len(r_box) else ' ' * r_box_width
        new_box.append(l_line + gap + r_line)
    return new_box, len(new_box[0]), new_root_start, new_root_end

刚才所写的方法都是在一棵二叉树已经建立好的情况下查看它的相关性质,下面我们来着手写一下二叉树的建立和更新相关的方法:

##    BinaryTree
    def __setitem__(self, index, node):
        parent_index = index >> 1
        parent = self[parent_index]
        if index & 1:
            parent._rson = node
        else:
            parent._lson = node
    def build(self, lst):
        self._root = Node(lst[0])
        for data in lst[1::]:
            self.insert(data)
    def insert(self, value):
        if self.is_empty():
            self._root = Node(value)
        else:
            index = len(self)
            self[index+1] = Node(value)

这三个方法都默认树上的结点按照从上到下、从左到右的顺序,且根节点的序号为1。
上述方法还需要重载__getitem__()__len__()方法以及is_empty()方法,实现如下:

##    BinaryTree
    def __getitem__(self, index):
        queue = [self.root()]
        curr = 1
        flag = True
        while flag:
            flag = False
            new_queue = []
            for node in queue:
                if curr == index:
                    return node
                curr += 1
                if node is None:
                    new_queue.extend((None, None))
                    continue
                new_queue.extend(node.sons())
                flag = bool(node.degree())
            queue = new_queue
    def __len__(self):
        return sum(1 for node in self._nodes())
    def is_empty(self):
        return self.root()._value is None

将上述代码汇总,并编写测试程序如下:

class Node:
    __slots__ = '_value', '_father', '_lson', '_rson'
    def __init__(self, value=None, father=None, lson=None, rson=None):
        self._value = value
        self._father = father
        self._lson = lson
        self._rson = rson
    def father(self):
        return self._father
    def lson(self):
        return self._lson
    def rson(self):
        return self._rson
    def degree(self):
        return sum(1 for son in self.sons())
    def height(self):
        return 1 + max(son.height() for son in self.sons()) if self.degree() else 0
    def depth(self):
        return 1 + self._father.depth() if self._father else 0
    def sons(self):
        for son in (self._lson, self._rson):
            if son:
                yield son
    def __repr__(self):
        return str(self._value)
    def __str__(self):
        lines = _tree_print_aux(self)[0]
        return '\n'.join(line.rstrip() for line in lines)

class BinaryTree:
    def __init__(self, root=None):
        self._root = Node(root)
    def root(self):
        return self._root
    def height(self):
        return self.root().height()
    def degree(self):
        return max(node.degree() for node in self._nodes())
    def _nodes(self):
        return self.preorder()
    def preorder(self):
        for node in self._preorder_aux(self._root):
            yield node
    def _preorder_aux(self, root):
        yield root
        for son in root.sons():
            for other in self._preorder_aux(son):
                yield other
    def postorder(self):
        for node in self._postorder_aux(self._root):
            yield node
    def _postorder_aux(self, root):
        for son in root.sons():
            for other in self._postorder_aux(son):
                yield other
        yield root
    def inorder(self):
        for node in self._inorder_aux(self._root):
            yield node
    def _inorder_aux(self, root):
        if root.lson():
            for other in self._inorder_aux(root.lson()):
                yield other
        yield root
        if root.rson():
            for other in self._inorder_aux(root.rson()):
                yield other
    def levelorder(self):
        queue = [self.root()]
        while len(queue):
            node = queue.pop(0)
            yield node
            for son in node.sons():
                queue.append(son)
    def __repr__(self):
        return self._root.__repr__()
    def __str__(self):
        return self._root.__str__()
    def __setitem__(self, index, node):
        parent_index = index >> 1
        parent = self[parent_index]
        if index & 1:
            parent._rson = node
        else:
            parent._lson = node
    def build(self, lst):
        self._root = Node(lst[0])
        for data in lst[1::]:
            self.insert(data)
    def insert(self, value):
        if self.is_empty():
            self._root = Node(value)
        else:
            index = len(self)
            self[index+1] = Node(value)
    def __getitem__(self, index):
        queue = [self.root()]
        curr = 1
        flag = True
        while flag:
            flag = False
            new_queue = []
            for node in queue:
                if curr == index:
                    return node
                curr += 1
                if node is None:
                    new_queue.extend((None, None))
                    continue
                new_queue.extend(node.sons())
                flag = bool(node.degree())
            queue = new_queue
    def __len__(self):
        return sum(1 for node in self._nodes())
    def is_empty(self):
        return self.root()._value is None

def _tree_print_aux(root, curr=0):
    if root is None:
        return [], 0, 0, 0
    line_l = []
    line_r = []
    node_repr = str(root._value)
    new_root_width = gap_size = len(node_repr)
    l_box, l_box_width, l_root_start, l_root_end = \
        _tree_print_aux(root._lson, curr<<1|1)
    r_box, r_box_width, r_root_start, r_root_end = \
        _tree_print_aux(root._rson, (curr<<1) + 2)
    if l_box_width > 0:
        l_root = ((l_root_start + l_root_end)>>1) + 1
        line_l.append(' ' * (l_root + 1))
        line_l.append('_' * (l_box_width - l_root))
        line_r.append(' ' * l_root + '/')
        line_r.append(' ' * (l_box_width - l_root))
        new_root_start = l_box_width + 1
        gap_size += 1
    else:
        new_root_start = 0
    line_l.append(node_repr)
    line_r.append(' ' * new_root_width)
    if r_box_width > 0:
        r_root = (r_root_start + r_root_end)>>1
        line_l.append('_' * r_root)
        line_l.append(' ' * (r_box_width - r_root + 1))
        line_r.append(' ' * r_root + '\\')
        line_r.append(' ' * (r_box_width - r_root))
        gap_size += 1
    new_root_end = new_root_start + new_root_width - 1
    gap = ' ' * gap_size
    new_box = [''.join(line_l), ''.join(line_r)]
    for i in range(max(len(l_box), len(r_box))):
        l_line = l_box[i] if i < len(l_box) else ' ' * l_box_width
        r_line = r_box[i] if i < len(r_box) else ' ' * r_box_width
        new_box.append(l_line + gap + r_line)
    return new_box, len(new_box[0]), new_root_start, new_root_end


if __name__ == '__main__':
    bt = BinaryTree()
    bt.build(list(range(10)))
    print(bt)
    print(bt.height())
    print(bt.degree())
    print(list(bt.preorder()))
    print(list(bt.postorder()))
    print(list(bt.inorder()))
    print(list(bt.levelorder()))

输出如下:

        ____0__
       /       \
    __1__       2
   /     \     / \
  3       4   5   6
 / \     /
7   8   9

3
2
[0, 1, 3, 7, 8, 4, 9, 2, 5, 6]
[7, 8, 3, 9, 4, 1, 5, 6, 2, 0]
[7, 3, 8, 1, 9, 4, 0, 5, 2, 6]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值