二叉树的顺序存储和链式存储

来源:互联网 发布:资产管理公司待遇 知乎 编辑:程序博客网 时间:2024/06/06 05:54

作为一个机械屌丝男,最近一直在恶补数据结构,昏昏沉沉地状态下终于看到了鸡冻人心的二叉树。据说这玩意儿成就了不少人,也打败了不少人,瞬间有一股膜拜的心有木有,有木有!

好啦,言归正传,先看看万能的二叉树都是怎么存储的吧。由于线性表有两种存储方式:顺序存储和链式存储。二叉树的存储也是一个线性表结构,因此也具有顺序存储和链式存储两种方式。

一、顺序存储

顺序存储是一个让人又爱又恨的玩意儿。爱是因为它理解起来比较简单,查找起来也比较方便;恨是因为这玩意儿太傻瓜,它只一次性地问内存要一段连续的空间,而且坚决不管在实际应用中够用不够用,而且这货在插入和删除元素时居然需要O(n^2)的时间复杂度,这个让人有点无语。那二叉树的顺序存储怎么样呢?让我们来分析分析。

1.比如说我有一棵完全二叉树,如图1(a)所示。该二叉树中有6个元素,可以将这六个元素按照层次从上到下,每一层从左到右的规则依次放入一个数组中,如图1(b)所示。除了数组的内存空间的问题外,这个看起来还过得去。但是,悲剧就要来了...


2.比如说,我有一棵一般二叉树,这棵二叉树只有四个结点(A/B/C/D),A拥有左右子树B/C,B是光棍没有孩子,C计划生育贯彻得好,仅有一个孩子D。但是为了能够方便地从双亲找到孩子(或者从孩子找到双亲)(关于双亲与孩子的关系,请参见数据结构之树的基本术语与性质总结),需要构建一些“虚结点”,从而使得一般二叉树“虚化”为完全二叉树,如图2(a)所示,B的两个孩子就是虚结点。然后按照完全二叉树的顺序存储方式进行存储,存储结果如图2(b)所示,数组中脚标为3的位置存放B的左子树,由于是虚结点,所以为空;脚标为4的位置存放B的右子树,同样因为是虚结点,所以为空。可以看出,对于一般二叉树,如果虚结点过多的话,数组中会有很多的空值,贼浪费空间。当然,如果您还觉得这样能过得去的话,让我们看看下面的退化二叉树吧,纯属洗脑了。


3.现在来演示一下万恶的退化二叉树,如图3(a)所示,该二叉树深度为3,但是恶心的地方到了,总共的元素个数也为3,这样,如果按照2中的方式顺序存储的话,数组中势必会有更多的元素为空。随着二叉树层数的增多,空元素是以指数级在增长的。如果这样的话,该会浪费多少内存空间啊,太可怕了。


当然,没有解决不了的问题,链式存储就是解决该问题的神器!

二、链式存储

链式存储的结构分为两种:二叉链表结构和三叉链表结构。

1.二叉链表结构

一个二叉链表结点由三部分组成:数据域,左孩子指针和右孩子指针,如下图所示。


其中,数据域data用来存放结点信息,左孩子指针lchild用来指向该结点的左子树,右孩子指针rchild用来指向该结点的右子树。图4(a)所示的完全二叉树的二叉链表结构如图4(b)所示。


2.三叉链表结构

三叉链表结点是在二叉链表结点的基础上,增加了一个指向双亲结点的指针域,如下图所示。


其中,data表示二叉树结点的数据域内容,lchild指向该结点的左孩子,rchild指向该结点的右孩子,parent指向该结点的双亲结点。图5(a)所示的完全二叉树的三叉链表结构如图5(b)所示。


三、比较

1.顺序存储与链式存储比较

(a)顺序存储分配的内存空间大小是固定的,不好根据二叉树结点数目的增多而动态扩展。如果内存空间分配过大,势必造成内存空间的浪费;如果分配过小,则会产生数组溢出的问题。另外,顺序存储在增加和删除结点时时间复杂度为O(n^2)。顺序存储的好处是方便查找结点。

(b)链式存储可以动态分配内存空间,比顺序存储更加灵活、方便,结点的最大数目仅与系统存储空间相关。另外,在插入/删除结点时的时间复杂度仅为O(n)。因此,在对二叉树操作时更多采用链式存储结构。

2.二叉链表与三叉链表

三叉链表结点比二叉链表结点多了一个指向双亲结点的指针,因此增加了空间开销。但是,三叉链表的好处是既可以查找孩子结点,也可以查找双亲结点,在需要频繁查找双亲结点的应用中更为方便。

0 0