数据结构-树形结构和二叉树的建立

数据结构-树形结构

首先,什么是树形结构,简单地说,树形结构就是你现在想的那样的结构,数据结构像树形的就是树形结构,典型的树形结构示例:Windows操作系统和Unix操作系统和文件系统均是树形结构的应用。

  1. 树的基本概念

    **树(Tree)**是一个由一个或者一个以上的节点(Node)组成的,存在一个特殊的节点,称为树根(Root),Root很熟悉吧,Linux的根目录。每个节点是一些数据和指针组合而成的记录

    A
    BCD

    A为根节点,B、C、D均是A的子节点

    树还可以组成森林(forest),也就是说森林是n个互斥树的集合(n>=0),移动树根即是森林

    简单地说,把根节点去掉,就是森林,够离谱

    树形结构的专有名词

    在树形结构中,有许多常用的专有名词

    • 度数(Degree):每个节点所有字数的个数,比如A的度数是3
    • 层数(level):树的层数,比如A在第一层,B、C、D在第二层
    • 高度(Height):树的最大层数。比如上面这个高度就是2
    • 树叶或者终端节点(Terminal Nodes):度数为零的节点就是树叶,B、C、D节点就是树叶
    • 父节点(Parent):每一个节点有连接的上一层节点
    • 子节点(Children):每一个节点有连接的下一层节点为子节点
    • 祖先(Ancestor)和子孙(Descendent):所谓祖先,是指从树根到该节点路径上所包含的节点,而子孙则是在该节点往下追溯子树中的任一节点。(这些东西就和你生物学的族谱图是一样的,就看着类比吧)
    • 兄弟节点(Siblings):有共同父节点的节点为兄弟节点
    • 非终端节点(Nonterminal Nodes):树叶以外的节点
    • 同代(Generation):在同一棵树中具有相同层数的节点
  2. 二叉树简介

    一般树形结构在计算机内存中的存储方式是以链表(Linked List)为主的。对于n叉树(n-way 树)来说,因为每个节点的度数都不相同,所以我们必须为每个节点都预留存放n个链接字段的最大存储空间,因为每个节点的数据结构如下:

    datalink1link2link3linkn

    在这种情况下,这种n叉树十分浪费链接存储空间。假设此n叉树有m个节点,那么此树共有nxm个链接字段。另外,因为除了树根外,每一个非空链接都指向一个节点,所以得知空链接个数为nxm-(m-1) = m x (n-1) + 1,而n叉树的链接浪费率为[m x (n - 1) + 1]/m x n,因此可以得出以下结论:

    ​ n = 2 时,2叉树的链接浪费率约为 1 / 2

    ​ n = 3 时,3叉树的链接浪费率约为 1 / 3

    ​ n = 4 时,4叉树的链接浪费率约为 1 / 4

    ​ … …

    ​ … …

    当n = 2 时,它的链接浪费率最低,所以为了改进存储空间浪费的缺点,最常使用二叉树(Binary Tree) 结构来取代其他树形结构

    1. 二叉树的定义

      二叉树(又称为Knuth 树)是一个由有限节点所组成的集合,此集合可以为空集合,或由一个数根及其左右两个子树组成。简单地说,二叉树最多只能有两个子节点,就是度数小于或等于2.期计算机中的数据结构如下:

      LLINKDataALINK

      二叉树和一般树的不同之处:

      • 树不可以为空集合,但是二叉树可以
      • 树的度数为d >= 0,但二叉树的节点度数为 0 <= d <= 2
      • 树的子树间没有次序关系,二叉树则有
    2. 特殊二叉树

      • 满二叉树(Full Binary Tree)

        如果二叉树的高度为h,树的节点数为2^h - 1 ,h >= 0,我们就称此树为“满二叉树”

      • 完全二叉树(Complete Binary Tree)

        如果二叉树的高度为h,所含的节点数小于2^h - 1,但其节点的编号方式如同高度为h的满二叉树一样,从左到右、从上到下的顺序一一对应

        对于完全二叉树而言,假设有N个节点,那么此二叉树的层数h为**[log2(N+1)]**

      • 斜二叉树(Skewed Binary Tree):当一个二叉树完全没有有节点或左节点时,我们就把它称为左斜二叉树或右斜二叉树

      • 严格二叉树(Strict binary Tree)

        二叉树中的每一个非终端节点均有非空的左右子树

    3. 二叉树的存储方式

      二叉树有很多的存储方式,在数据结构中,我们通常是用链表来表示二叉树,这样在删除或者增加节点时会很方便并且很便捷。但是也可以使用一维数组这样的连续存储空间来表示二叉树,不过在对树中的中间节点进行插入或者删除操作时,可能要大量移动数组中节点的存储位置来反应树节点的变动

      • 一维数组表示法

        使用有序的一维数组来表示二叉树,首先可将此二叉树假想为一个满二叉树,而且第K层具有2^k-1个节点,他们按照顺序存放在这个一维数组中。

        (1)A(根节点)
        (1)B(3)无
        (4)无(5)C(6)无(7)D

        将上面的二叉树存放到一维数组中,是什么样的

        索引值1234567
        内容值ABCD

        从二叉树和一位数组的索引值直接关系,可以看出

        • 左子树索引值是父节点索引值*2
        • 右子树索引值是父节点索引值*2+1

        接着来看以一维数组建立二叉树的实例,事实上就是创建一个二叉查找树,这是一种很好的排序应用模型,因为在建立二叉树的同时,数据就经过初步的比较判断并按照二叉树的建立规则来存放数据了。二叉查找树具有一下特点:

        • 可以是空集合,但若不是空集合,则节点上一定要有一个键值
        • 每一个树根的值需要大于左子树的值
        • 每一个树根的值需要小于右子树的值
        • 左右子树也是二叉查找树
        • 树的每个节点的值都不相同

        例子:设计一个程序,按需输入一颗二叉树

        注意,因为数组的大小是固定的,所以在data设计时很容易出现索引超限,所以找个data托配合一下

        def Btree_create(btree,data,length):
            for i in range(1,length):
                level = 1
                while btree[level] != 0:
                    if data[i] > btree[level]:      #判断data中的数据和树根的比较,大于放在右边
                        level = level * 2 + 1   #将level赋值为下一个节点,如果有数据循环遍历下一个
                    else:
                        level = level * 2       #小于就放在左边,*2,将level赋值为下一个节点,如果有数据循环遍历下一个
                btree[level] = data[i]      #遍历得到了没有数值的level,将其赋值
        data = [0,3,5,6,1,9,3,2,1]
        length = 9
        btree = [0] * 16
        print("原数组内容")
        for i in range(length):
            print('[%2d]'% data[i],end='')
        print('')
        Btree_create(btree,data,9)
        print('二叉树内容')
        for i in range(1,16):
            print('[%2d]'%btree[i],end='')
        print('')
        

        在这里插入图片描述

      • 链表表示法

        链表表示法,就是使用链表来存储二叉树。使用链表来表示二叉树的好处就是对于节点的增加和删除相当容易,缺点时很难找到父节点,除非在每一节点多增加一个父字段,以存放整数的数据类型为例

        class tree:
        		def __init__(self):
        				self.data = 0
        				self.left = None
        				self.right = None
        

        以链表方式建立二叉树

          
            def create_tree(self,root,val):
                newnode = tree()   #实例化一个节点
                newnode.data = val     #赋值data
                newnode.left = None    #左节点指向None
                newnode.right = None   #右节点指向None
                if root == None:
                    root = newnode      #如果没有父节点,将newnode赋值给root
                    return root
                else:
                    current = root
                    while current != None:
                        backup = current    #将每次遍历的current值给到backup
                        if current.data > val:  #节点的值大于val,要把val放在左边,并且把current设为左侧下一个节点遍历
                            current = current.left  #current为左侧下一个节点
                        else:       #节点的值小于val,要把val放在右边,并且把current设为右侧下一个节点遍历
                            current = current.right  #current为右侧下一个节点
                    if backup.data > val:   #比较此时的backup(current)与val,决定newnode放到backup的哪侧
                        backup.left = newnode
                    else:
                        backup.right = newnode
                return root
        

        总结一下以上的代码,while循环就是遍历节点current,直至找到下一个节点为空时放置newnode,第一个if是判断此current节点不为空时往左遍历还是往右遍历,第二个if是判断此节点为空时newnode应该放置到左侧还是右侧

        设计程序,使用链表建立二叉树

        class tree:
            def __init__(self):
                self.data = 0
                self.left = None
                self.right = None
        
        def create_tree(root,val):
            newnode = tree()   #实例化一个节点
            newnode.data = val     #赋值data
            newnode.left = None    #左节点指向None
            newnode.right = None   #右节点指向None
            if root == None:
                root = newnode      #如果没有父节点,将newnode赋值给root
                return root
            else:
                current = root
                while current != None:
                    backup = current    #将每次遍历的current值给到backup
                    if current.data > val:  #节点的值大于val,要把val放在左边,并且把current设为左侧下一个节点遍历
                        current = current.left  #current为左侧下一个节点
                    else:       #节点的值小于val,要把val放在右边,并且把current设为右侧下一个节点遍历
                        current = current.right  #current为右侧下一个节点
                if backup.data > val:   #比较此时的backup(current)与val,决定newnode放到backup的哪侧
                    backup.left = newnode
                else:
                    backup.right = newnode
            return root
        
        data = [5,4,8,1,12,3,6,7,9]
        ptr = None
        root = None
        for i in range(9):
            ptr = create_tree(ptr,data[i])
        print('左子树')
        root = ptr.left
        while root != None:
            print('%d' %root.data)
            root = root.left
        print('--------------')
        print('右子树')
        root = ptr.right
        while root != None:
            print('%d'%root.data)
            root = root.right
        print()
        

        在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牧码文

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值