7.1 树及二叉树的概念及存储结构

终于来到数据结构中的核心内容——树。

树是最重要的数据结构,没有之一。树,表示一个元素与多个元素之间一对多的关系,因此可以用来表示事物中的层次关系,如家谱,如企业或政府机关的组织结构关系等,在逻辑上的用层次关系来存储,在管理时也方便查找等。

这一节从树的概念和存储开始,延伸至最重要的二叉树。以概念为主,大多数内容都在各教材和博客中可以找到。

树的基本概念

可以参考博客 https://www.cnblogs.com/tarbitrary/p/4030652.html

树是非线性的结构,树的定义是递归的,也就是树的子树还是树,这是树的基本特性,因此树的一些问题可以用递归方法来解决。树之间是不相交的,也就是没有环路(可以认为有环路了就是图了),基于此,最重要的一条性质就是一颗N个结点的树共N-1条边。

基本术语:

树的性质:

1) 树中的结点数等于所有结点的度数加1

2) 度为m的树中第i层上至多有 m^(i-1)个结点(i ≥ 1)

3) 高度为h的m次树(即度为m)至多有 (m^k - 1) / (m - 1) 个结点。由上一性质推论而来。

4) 具有n个结点的m次树的最小高度为 \log m \left ( n \left ( m - 1 \right ) + 1 \right )  (m 为底),结果向上取整,根据上一性质推论得出。

树的存储结构

介绍三种常用的存储结构,具体图示可看上面的博客。

一是双亲存储结构,是一种顺序存储结构,用一组连续空间存储树的所有结点,同时在每个结点中添一个伪指针指示父结点的位置(即数组下标)。特点是便于查找父结点,但是找子结点时,需要遍历整个结构。

二是孩子链存储结构,结点之间仍然是顺序存储,不同的是,每个结点添加一个域存储子节点链表的头指针,多个子节点之间以链表方式连接,最后一个子节点的指向为NULL。特点与上面的相反,找孩子容易,找父结点难。

三是孩子兄弟链存储结构。每个结点有三个域,一个数组元素域,一个指向该结点的第一个孩子结点的指针域,一个指向该结点的下一个兄弟结点指针域。特点:找父结点仍然不方便。

其实还有一种表示方法,综合一二两种方案,既存储父结点也存储孩子链的头指针。

孩子兄弟链存储结构

这一方法相对来说很复杂,对树结构的表示关系也没那么清晰。实际上,这一表示方法是将树转换为二叉树,后面还会提到。

习题一道:

给出一个以孩子兄弟链为存储结构的树,用递归方法求其高度。

class Node:
    def __init__(self, val):
        self.val = val
        self.firstChild = None
        self.nextSibling = None


def get_tree_height(head):
    p = head
    if not p:
        return 0
    elif not p.firstChild:
        return 1

    m, height = 0, 0
    p = p.firstChild   # 根节点 没有兄弟结点
    while p:
        m = get_tree_height(p)
        height = max(height, m)
        p = p.nextSibling
    return height + 1

二叉树的概念和性质

二叉树,分为五种基本形态,严格区分左子树和右子树。

特殊二叉树:

满二叉树(Full Binary Tree)、完全二叉树 (Complete Binary Tree)

概念不再重复,满二叉树是完全二叉树的一种特殊情况。需要特别注意的是我们对完全二叉树的编号体系,下面介绍性质时仔细体会。

二叉树的性质:

1) 非空二叉树第i层上最多有 2^(i-1)个结点

2) 高度为h的二叉树至多有 2^h - 1个结点

3) 最重要的一条性质: 非空二叉树上叶子结点(度为0)数等于双分支(度为2)结点数加1。

核心公式 : 总分支数 = 总结点数 - 1,由此推算。

4)完全二叉树的性质(结点编号为1~n),对于i结点,若i结点有左孩子,则编号为2i,若i结点有右孩子,则编号为(2i+1);除根结点外,i结点的父结点编号为i/2(向下取整);对于该完全二叉树,若i ≤ n/2(向下取整),则i结点为分支结点,否则为子结点。(编号最大的分支结点是n/2向下取整)

二叉树与树、森林之间的转换:

一般来说,树中结点的左右次序无关,只要保留层次关系即可;但是二叉树严格区分左右子树。我们按照约定的格式将树、森林按二叉树的结构进行表示即可。

树、森林转换为二叉树,就是用孩子兄弟链存储结构,从第一棵树的根节点开始,对于任意结点k,若有左子树,则左子树就是k原来最左边第一棵子树的根节点;若有右子树,则右子树为k原来右边相邻的第一个兄弟结点或右边第一棵相邻的树的根节点(当k为第一棵树的根结点时)。

参考博客中图示如下

  • 将第二棵二叉树作为第一棵二叉树根节点的右孩子,将第三棵二叉树作为第二棵二叉树根节点的右孩子,依次类推,最终得到的二叉树就是由森林转换成的二叉树。

 

二叉树的存储结构

二叉树主要有顺序存储结构和链式存储结构两种。
顺序存储结构,还是要用到完全二叉树的编号体系,参考完全二叉树的定义及性质。对于完全二叉树,顺序存储十分方便好用合适,对于普通二叉树来说,就会造成空间的浪费。而且这种存储结构对于二叉树的删除、插入等不方便。

最为常用的还是链式存储结构,通过链表来存储,每一个结点域包含结点的内容,左孩子结点,右孩子结点三部分。之后我们讨论的树,大多数情况都是这种存储结构。


基本概念到此结束了,关键是要理解。后面的内容都是围绕二叉树的各种运算和应用。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值