Python实现树

时间:2019-08-16
本文章向大家介绍Python实现树,主要包括Python实现树使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

 树 (tree) 是一种非常高效的非线性存储结构。树,可以很形象的理解,有根,有叶子,对应在数据结构中就是根节点、叶子节点,同一层的叶子叫兄弟节点,邻近不同层的叫父子节点,非常好理解。

注:定义来自百度百科。

其他概念解释

  • 二叉树,就是每个节点都至多有二个子节点的树。

  • 满二叉树,就是除了叶子节点外,每个节点都有左右两个子节点,这种二叉树叫做满二叉树。

  • 完全二叉树,就是叶子节点都在最底下两层,最后一层叶子节都靠左排列,并且除了最后一层,其他层的节点个数都要达到最大,这种二叉树叫做完全二叉树。

在接下来的内容里,我们将逐步介绍二叉树的具体功能是如何实现的。

思路:

  1. 先定义一个节点 node 类,存储数据 data 和左子节点 left 以及 右子节点 right。

  2. 再实现二叉树 binary_tree 的类,应至少有以下属性和函数: 属性:有一个根节点(root) , 它是 node 类。 函数:添加子节点 add ,返回父节点 get_parent,删除子节点 delete。

步骤如下:

1. 创建 Node 类

创建一个 Node 的类,作为基础数据结构:链点,并初始化对应的内参。

具体实现代码如下:

class Node(object):
    def __init__(self,item):
        self.item = item #表示对应的元素
        self.left=None #表示左子节点
        self.right=None #表示右子节点
    def __str__(self):
        return str(self.item)  #print 一个 Node 类时会打印 __str__ 的返回值

2. 创建 Tree 类

创建一个 Tree 的类,定义根节点。

具体实现代码如下:

class Tree(object):
    def __init__(self):
        self.root=Node('root')  #根节点定义为 root 永不删除,作为哨兵使用。

3. 添加 add 函数

添加一个 add(item) 的函数,功能是添加子节点到树里面。

具体实现代码如下:

def add(self,item):
        node = Node(item)
        if self.root is None:  #如果二叉树为空,那么生成的二叉树最终为新插入树的点
            self.root = node
        else:
            q = [self.root] # 将q列表,添加二叉树的根节点
            while True:
                pop_node = q.pop(0)
                if pop_node.left is None: #左子树为空则将点添加到左子树
                    pop_node.left = node
                    return
                elif pop_node.right is None: #右子树为空则将点添加到右子树
                    pop_node.right = node
                    return
                else:
                    q.append(pop_node.left)
                    q.append(pop_node.right)

4. 添加 get_parent 函数

添加一个 get_parent(item) 函数,功能是找到 item 的父节点。

具体实现代码如下:

def get_parent(self, item):
        if self.root.item == item:
            return None  # 根节点没有父节点
        tmp = [self.root] # 将tmp列表,添加二叉树的根节点
        while tmp:
            pop_node = tmp.pop(0)
            if pop_node.left and pop_node.left.item == item: #某点的左子树为寻找的点
                return pop_node #返回某点,即为寻找点的父节点
            if pop_node.right and pop_node.right.item == item: #某点的右子树为寻找的点
                return pop_node #返回某点,即为寻找点的父节点
            if pop_node.left is not None: #添加tmp 元素
                tmp.append(pop_node.left)
            if pop_node.right is not None:
                tmp.append(pop_node.right)
        return None

5. 添加 delete 函数

添加一个 delete(item) 函数,功能是从二叉树中删除一个子节点。

思路如下:

先获取待删除节点 item 的父节点。
    如果父节点不为空,判断 item 的左右子树:
        如果左子树为空,那么判断 item 是父节点的左孩子,还是右孩子;
            如果是左孩子,将父节点的左指针指向 item 的右子树,反之将父节点的右指针指向 item 的右子树。
        如果右子树为空,那么判断 item 是父节点的左孩子,还是右孩子;
            如果是左孩子,将父节点的左指针指向 item 的左子树,反之将父节点的右指针指向 item 的左子树。
        如果左右子树均不为空,寻找右子树中的最左叶子节点 x ,将 x 替代要删除的节点。
    删除成功,返回 True。
    删除失败, 返回 False。

效果演示:对已知二叉树删除元素 32

具体实现代码如下:

def delete(self, item):
        if self.root is None:  # 如果根为空,就什么也不做
            return False

        parent = self.get_parent(item)
        if parent:
            del_node = parent.left if parent.left.item == item else parent.right  # 待删除节点
            if del_node.left is None:
                if parent.left.item == item:
                    parent.left = del_node.right
                else:
                    parent.right = del_node.right
                del del_node
                return True
            elif del_node.right is None:
                if parent.left.item == item:
                    parent.left = del_node.left
                else:
                    parent.right = del_node.left
                del del_node
                return True
            else:  # 左右子树都不为空
                tmp_pre = del_node
                tmp_next = del_node.right
                if tmp_next.left is None:
                    # 替代
                    tmp_pre.right = tmp_next.right
                    tmp_next.left = del_node.left
                    tmp_next.right = del_node.right

                else:
                    while tmp_next.left:  # 让tmp指向右子树的最后一个叶子
                        tmp_pre = tmp_next
                        tmp_next = tmp_next.left
                    # 替代
                    tmp_pre.left = tmp_next.right
                    tmp_next.left = del_node.left
                    tmp_next.right = del_node.right
                if parent.left.item == item:
                    parent.left = tmp_next
                else:
                    parent.right = tmp_next
                del del_node
                return True
        else:
            return False

最终完整代码如下:

class Node(object):
    def __init__(self,item):
        self.item=item #表示对应的元素
        self.left=None #表示左节点
        self.right=None #表示右节点
    def __str__(self):
        return str(self.item)  #print 一个 Node 类时会打印 __str__ 的返回值
class Tree(object):
    def __init__(self):
        self.root=Node('root')  #根节点定义为 root 永不删除,作为哨兵使用。
    def add(self,item):
        node = Node(item)
        if self.root is None:  #如果二叉树为空,那么生成的二叉树最终为新插入树的点
            self.root = node
        else:
            q = [self.root] # 将q列表,添加二叉树的根节点
            while True:
                pop_node = q.pop(0)
                if pop_node.left is None: #左子树为空则将点添加到左子树
                    pop_node.left = node
                    return
                elif pop_node.right is None: #右子树为空则将点添加到右子树
                    pop_node.right = node
                    return
                else:
                    q.append(pop_node.left)
                    q.append(pop_node.right)
    def get_parent(self, item):
        if self.root.item == item:
            return None  # 根节点没有父节点
        tmp = [self.root] # 将tmp列表,添加二叉树的根节点
        while tmp:
            pop_node = tmp.pop(0)
            if pop_node.left and pop_node.left.item == item: #某点的左子树为寻找的点
                return pop_node #返回某点,即为寻找点的父节点
            if pop_node.right and pop_node.right.item == item: #某点的右子树为寻找的点
                return pop_node #返回某点,即为寻找点的父节点
            if pop_node.left is not None: #添加tmp 元素
                tmp.append(pop_node.left)
            if pop_node.right is not None:
                tmp.append(pop_node.right)
        return None
    def delete(self, item):
        if self.root is None:  # 如果根为空,就什么也不做
            return False

        parent = self.get_parent(item)
        if parent:
            del_node = parent.left if parent.left.item == item else parent.right  # 待删除节点
            if del_node.left is None:
                if parent.left.item == item:
                    parent.left = del_node.right
                else:
                    parent.right = del_node.right
                del del_node
                return True
            elif del_node.right is None:
                if parent.left.item == item:
                    parent.left = del_node.left
                else:
                    parent.right = del_node.left
                del del_node
                return True
            else:  # 左右子树都不为空
                tmp_pre = del_node
                tmp_next = del_node.right
                if tmp_next.left is None:
                    # 替代
                    tmp_pre.right = tmp_next.right
                    tmp_next.left = del_node.left
                    tmp_next.right = del_node.right

                else:
                    while tmp_next.left:  # 让tmp指向右子树的最后一个叶子
                        tmp_pre = tmp_next
                        tmp_next = tmp_next.left
                    # 替代
                    tmp_pre.left = tmp_next.right
                    tmp_next.left = del_node.left
                    tmp_next.right = del_node.right
                if parent.left.item == item:
                    parent.left = tmp_next
                else:
                    parent.right = tmp_next
                del del_node
                return True
        else:
            return False

二叉搜索树又称二叉查找树,亦称二叉排序树,如下图所示:

它主要用于搜索。 它或者是一棵空树,或者是具有下列性质的二叉树:

  1. 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

  2. 若右子树不空,则右子树上所有结点的值均大于它的根结点的值;

  3. 左、右子树也分别为二叉排序树。

平衡二叉树(平衡二叉树又被称为 AVL 树 )是基于二分法的策略提高数据的查找速度的二叉树的数据结构。

特点:平衡二叉树是采用二分法思维把数据按规则组装成一个树形结构的数据,用这个树形结构的数据减少无关数据的检索,大大的提升了数据检索的速度;平衡二叉树的数据结构组装过程有以下规则:

  1. 非叶子节点只能允许最多两个子节点存在。

  2. 每一个非叶子节点数据分布规则为左边的子节点小于前节点的值,右边的子节点大于当前节点的值(这里值是基于自己的算法规则而定的,比如 hash 值)。

注:定义来自百度百科。

遍历原理

二叉树的遍历:是指从根结点出发,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次且仅被访问一次。

这里有两个关键词:访问和次序。

访问其实是要根据实际的需要来确定具体做什么,比如对每个结点进行相关计算,输出打印等。它算作是一个抽象操作。

二叉树的遍历次序不同于线性结构,最多也就是从头到尾、循环和双向等简单的遍历方式。树的结点之间不存在唯一的前驱和后继关系,在访问一个结点后,下一个被访问的结点面临着不同的选择。

二叉树的遍历方式可以有很多,如果我们限制从左到右的顺序,就主要分为三种:

  1. 中序遍历

  2. 后序遍历

  3. 前序遍历

中序遍历

  1. 先处理左子树,然后处理当前节点,再处理右子树;

  2. 对于一颗二叉查找树,所有的信息都是有序排列的,中序遍历可以是信息有序输出,且运行时间为 O(n);

  3. 递归实现中序遍历。

在之前的 Tree 类里面添加 inorder 函数

参考代码如下:

def inorder(self,node):  # 中序遍历
        if node is None:
            return []
        result = [node.item]
        left_item = self.inorder(node.left)
        right_item = self.inorder(node.right)
        return left_item + result + right_item

中序遍历的效果演示:

后序遍历
  1. 先处理左右子树,然后再处理当前节点,运行时间为 O(n)

  2. 递归实现后序遍历

参考代码如下:

def postorder(self,node):  # 后序遍历
        if node is None:
            return []
        result = [node.item]
        left_item = self.postorder(node.left)
        right_item = self.postorder(node.right)
        return left_item + right_item + result
 
先序遍历
  1. 先处理当前节点,再处理左右子树;

  2. 递归实现先序遍历。

参考代码如下:

def preorder(self,node):  # 先序遍历
        if node is None:
            return []
        result = [node.item]
        left_item = self.preorder(node.left)
        right_item = self.preorder(node.right)
        return result + left_item + right_item

  

原文地址:https://www.cnblogs.com/MasterMonkInTemple/p/11363382.html