Python二叉树详解笔记

69 阅读9分钟

目录

 

二叉树数据结构

简介

为何选择树

树的主要应用包括:

二叉树的类型

满二叉树(Full binary tree)

完全二叉树(Complete binary tree)

完美二叉树(Perfect binary tree)

平衡二叉树(Balanced Binary Tree)

退化(或病态)树(Degenerate (or pathological) tree)

二叉排序树

代码实现

以class类创建树的单个节点

创建一个简单的树

创建二叉排序树(递归插入方法)

树遍历(前序,中序和后序)

前序遍历

中序遍历

后序遍历

删除树


二叉树数据结构

简介

元素最多包含2个子元素的树称为二叉树。由于二叉树中的每个元素只能有2个子元素,因此我们通常将它们命名为左右子元素。

二叉树节点包含以下部分。

  1. 数据
  2. 指向左子节点的指针
  3. 指向右子节点的指针

树: 与阵列,链接列表,堆栈和队列(线性数据结构)不同,树是分层数据结构。

树 词汇表: 最顶层的节点称为树的根。直接位于元素下的元素称为子元素。直接在某个东西上方的元素称为其父元素。例如,'a'是'f'的子元素,'f'是'a'的父元素。最后,没有孩子的元素被称为叶子。

      tree
      ----
       j    <-- root
     /   \
    f      k  
  /   \      \
 a     h      z    <-- leaves 

为何选择树

1. 使用树的一个原因可能是因为您希望存储自然形成层次结构的信息。例如,计算机上的文件系统:

file system
-----------
     /    <-- root
  /      \
...       home
      /          \
   ugrad        course
    /       /      |     \
  ...      cs101  cs112  cs113  

2. 树(具有一些排序,例如BST)提供适度的访问/搜索(比链接列表更快并且比数组慢)。
3. 树提供适度的插入/删除(比阵列更快,比无序链接列表慢)。
4. 与链接列表类似,与阵列不同,当节点使用指针链接时,树没有节点数量的上限。

树的主要应用包括:

1. 处理分层数据。
2. 使信息易于搜索(参见树遍历)。
3. 处理已排序的数据列表。
4. 作为合成视觉效果的数字图像的工作流程。
5. 路由器算法
6. 多阶段决策的形式(见商业象棋)。

 

二叉树的类型

满二叉树(Full binary tree)

如果每个节点有0或2个子节点,则二叉树已满。

以下是满二叉树的示例。我们还可以说满二叉树是一个其中除了叶子之外的所有节点都有两个子节点的二叉树。

               18
           /       \  
         15         30  
        /  \        /  \ 
      40    50    100   40 

             18
           /    \   
         15     20    
        /  \       
      40    50   
    /   \
   30   50

               18
            /     \  
          40       30  
                   /  \
                 100   40

在满二叉树文件中,叶节点数是内部节点数加1,内部节点是有子节点的的节点
L = I + 1
其中L =叶节点数,I =内部节点数

 

完全二叉树(Complete binary tree)

完全二叉树,所有层都被完全填充,除了可能是最后一级,最后一级是尽可能保留所有键

以下是完整二叉树的示例

               18
           /       \  
         15         30  
        /  \        /  \
      40    50    100   40


               18
           /       \  
         15         30  
        /  \        /  \
      40    50    100   40
     /  \   /
    8   7  9 

完美二叉树 (Perfect binary tree)

二叉树是完美的二叉树,其中所有内部节点都有两个子节点,所有叶子都在同一级别。
以下是完美二叉树的例子

              18
           /       \  
         15         30  
        /  \        /  \
      40    50    100   40


               18
           /       \  
         15         30  

高度为h的完美二叉树(其中高度是从根到叶的路径上的节点数)具有2^(h - 1)个节点。

平衡二叉树( Balanced Binary Tree

如果树的高度为O(Log n),则二叉树是平衡的,其中n是节点数。例如,AVL树通过确保左右子树高度之间的差异最大为1来维持O(Log n)高度。红黑树通过确保黑色节点的数量保持O(Log n)高度每个根到叶子路径是相同的,没有相邻的红色节点。平衡二进制搜索树在性能方面是明智的,因为它们为搜索,插入和删除提供了O(log n)时间。

退化(或病态)树( Degenerate (or pathological) tree

每个内部节点都有一个子树。这些树在性能方面与链表相同。

      10
      /
    20
     \
     30
      \
      40     

二叉排序树

二叉树

二叉排序树或者是一棵空树,或者是具有下列性质的二叉树

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

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

(3)左、右子树也分别为二叉排序树;

(4)没有键值相等的节点。

 

代码实现

以class类创建树的单个节点

    使用python的class类表示单个节点,节点中应包含当前节点的值、左节点指针、右节点指针

class Node:
    def __init__(self, data=None):
        self.val = data    # 节点值指针
        self.right = None  # 右节点指针
        self.left = None   # 左节点指针

创建一个简单的树

    先创建跟节点,然后依次分配左右子节点

if __name__ == '__main__':
    tree = Node(1)
    ''' 以下是上述声明后的树 
            1 
          /   \ 
         None  None'''
    tree.left = Node(2)
    tree.right = Node(3)
    ''' 2和3成为1的左右子树
               1 
             /   \ 
            2      3 
         /    \    /  \ 
       None None None None'''
    tree.left.left = Node(4)
    ''' 4成为2的左子树
               1 
           /       \ 
          2          3 
        /   \       /  \ 
       4    None  None  None 
      /  \ 
    None None'''

创建二叉排序树(递归插入方法)

步骤:

1、若根结点的值等于查找的值,成功

2、否则,若小于根结点的值,递归查左子树。

3、若大于根结点的值,递归查右子树。

4、若子树为空,查找不成功。

class Node:
    def __init__(self, data=None):
        self.value = data    # 节点值指针
        self.right = None    # 右节点指针
        self.left = None     # 左节点指针

    def insert(self, data):
        if self.value:                       # 判断是否存在根节点
            if data < self.value:            # 若小于当前根节点,则放左边
                if self.left is None:        # 若左边子树是空的,则添加到后面
                    self.left = Node(data)   # 新建节点后添加入末尾
                else:                        # 否则递归调用,直到到达末尾
                    self.left.insert(data)
            elif data > self.value:          # 若大于当前跟节点,则放右边
                if self.right is None:       # 若右边子树是空的,则添加到后面
                    self.right = Node(data)  # 新建节点后添加入末尾
                else:                        # 否则递归调用,直到到达末尾
                    self.right.insert(data)
        else:
            self.value = data                # 不存在根节点,就将值设置为根节点

树遍历(前序,中序和后序)

前序遍历

算法(树)
   1.访问root。
   2.遍历左子树,即调用Preorder(左子树)
   3.遍历右子树,即调用Preorder(右子树) 

ABDHIEJCFKG

 

class Node:
    def preorderTraversal(self, root):
        res = []
        if root:
            res.append(root.value)
            res += self.preorderTraversal(root.left)
            res += self.preorderTraversal(root.right)
        return res

if __name__ == '__main__':
    tree = Node()
    tree.insert(2)
    tree.insert(3)
    tree.insert(5)
    tree.insert(1)
    tree.insert(7)
    res = tree.preorderTraversal(tree)
    print(res)

# Output:
# [2, 1, 3, 5, 7]

 

中序遍历

算法顺序(树) 
1.遍历左子树,即调用Inorder(左子树) 
2.访问root。
3.遍历右子树,即调用Inorder(右子树)

HDIBEJAFKCG

class Node:
    def inorderTraversal(self, root):
        res = []
        if root:
            res += self.inorderTraversal(root.left)
            res.append(root.value)
            res += self.inorderTraversal(root.right)
        return res

if __name__ == '__main__':
    tree = Node()
    tree.insert(2)
    tree.insert(3)
    tree.insert(5)
    tree.insert(1)
    tree.insert(7)
    res = tree.inorderTraversal(tree)
    print(res)

# Output:
# [1, 2, 3, 5, 7]

 

后序遍历

算法(树)
   1.遍历左子树,即调用Postorder(左子树)
   2.遍历右子树,即调用Postorder(右子树)
   3.访问root。

HIDJEBKFGCA

class Node:
    def postorderTraversal(self, root):
        res = []
        if root:
            res += self.postorderTraversal(root.left)
            res += self.postorderTraversal(root.right)
            res.append(root.value)
        return res

if __name__ == '__main__':
    tree = Node()
    tree.insert(2)
    tree.insert(3)
    tree.insert(5)
    tree.insert(1)
    tree.insert(7)
    res = tree.postorderTraversal(tree)
    print(res)

# Output:
# [1, 7, 5, 3, 2]

删除树

要删除树,我们必须遍历树的所有节点并逐个删除它们。所以我们应该使用哪种遍历? - 前序或中序或后序?答案很简单 -> 后序,因为在删除父节点之前,我们应该首先删除它的子节点。删除了树,还要将root更改为NULL

对于以下树节点,按顺序删除 - 4,5,2,3,1

ç¤ºä¾æ 

def _deleteTree(root):
    if root:
        _deleteTree(root.left)          # 首先删除两个子树
        _deleteTree(root.right)         # 首先删除两个子树
        print(" 删除节点:", root.value)
        root = None                     # 然后删除节点

def deleteTree(root):
    _deleteTree(root)   # 删除树
    root = None         # 并将根设置为NULL

if __name__ == '__main__':
    tree = Node()
    tree.insert(2)
    tree.insert(3)
    tree.insert(5)
    tree.insert(1)
    tree.insert(7)
    res = tree.postorderTraversal(tree)
    print(res)
    deleteTree(tree)

# Output:
# [1, 7, 5, 3, 2]
# 删除节点: 1
# 删除节点: 7
# 删除节点: 5
# 删除节点: 3
# 删除节点: 2

 

 

未完待续... 

 

欢迎关注↓↓↓

  • 头条号:小锋学长
  • 微信公众号:xfxuezhang