概念
二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。
满二叉树
一颗二叉树中,如果所有的分支节点都存在左子树和右子树,并且所有的叶节点都在同一层上,这样的一棵二叉树称作满二叉树。
完全二叉树
定义:
- 完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。满二叉树必定是完全二叉树,完全二叉树不一定是满二叉树。
- 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的节点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
二叉树的性质
- 一颗非空二叉树第k层上最多有2k-1个节点。
- 一颗深度为k的二叉树中,最多拥有2k-1个节点。
- 一个非空的二叉树,如果叶节点数为n0,度数为2的节点数为n2,则n0 = n2 + 1。
- 具有n个节点的完全二叉树的深度为[log2n]+1。(高斯符号 [] = floor())
- 具有n个节点的完全二叉树,如果按照从上至下和从左至右的顺序对二叉树中的所有节点从1开始顺序编码,则对于任意的序号为i的节点:
- 如果i>1,则序号为i的节点的双亲节点的序号为[i/2];如果i=1,的序号为i的节点为根节点,无双亲节点。
- 如果2i
n,则序号为i的节点的左孩子节点的序号为2i,如果2i>n,则序号为i的节点无左孩子。
- 如果2i + 1
n,则序号为i的节点的右孩子节点的序号为2i+1;如果2i + 1>n,则序号为i的节点无右孩子。
二叉树的存储
顺序存储结构
用一组连续的存储单元存放二叉树中的节点。一般是按照二叉树的节点从上至下,从左至右的顺序储存。节点在存储位置上关系并不一定就是他们在逻辑上的关系。
如上图,树按照从上至下,从左至右存储到内存中。
索引0为根节点,索引为i的节点的子节点的索引为2i + 1和2i + 2,索引为i的父节点的索引为[(i - 1)/2]。
例如索引为2的节点的子节点索引为2 * 2 + 1 = 5和2 * 2 + 2 = 6,父节点为[(2 - 1) / 2] = 0。
但是上图中用到的例子是一个完全二叉树,而非完全二叉树使用顺序存储结构的话,要想获取节点的子节点和父节点并且仍然满足上面的公式,就需要把非完全二叉树改造为完全二叉树再储存,否则就不会满足上面的公式,而改造一个非完全二叉树为完全二叉树,会造成大量的空间浪费。最坏的情况如下:一颗深度为k的右单支树,却需要分配2k - 1个存储单元。
所以,顺序存储结构仅适用于完全二叉树。
二叉链表存储结构
链表中每个节点由三个域组成,数据域和两个指针域(左子节点指针,右子节点指针)。
如果一个节点的子节点不存在,相应的指针域值为空。
节点的存储结构:
二叉链表示意图:
三叉链表存储结构
链表中每个节点由四个域组成,比二叉链表多了一个指向父节点的指针域。
节点的存储结构:
三叉链表示意图:
二叉树的遍历
将二叉树的根节点设为D,根节点的左子树设为L,根节点的右子树设为R。
那么遍历方式有:DLR,DRL,LDR,LRD,RLD,RDL。
限定先左后右,则仅有三种:DLR,LDR,LRD。
另外还有一种层次遍历,就是按照从上至下,从左至右,对节点进行逐个访问。
- 先序遍历(DLR)
- 访问根节点;
- 先序遍历根节点的左子树;
- 先序遍历根节点的右子树;
- 中序遍历(LDR)
- 中序遍历根节点的左子树;
- 访问根节点;
- 中序遍历根节点的右子树;
- 后序遍历(LRD)
- 后序遍历根节点的左子树;
- 后序遍历根节点的右子树;
- 访问根节点;
- 层次遍历
深度优先遍历:先序遍历,中序遍历,后序遍历。
广度优先遍历:层次遍历。
example1:
example2:
example3:
- 先序遍历:
- example1:1,2,4,8,9,5,10,11,3,6,12,13,7,14,15
- example2:f,b,a,d,c,e,g,i,h
- example3:a,b,d,e,g,h,i,c,f
- 中序遍历:
- example1:8,4,9,2,10,5,11,1,12,6,13,3,14,7,15
- example2:a,b,c,d,e,f,g,h,i
- example3:d,g,h,i,e,b,f,c,a
- 后序遍历
- example1:8,9,4,10,11,5,2,12,13,6,14,15,7,3,1
- example2:a,c,e,d,b,h,i,g,f
- example3:i,h,g,e,d,f,c,b,a
- 层次遍历
- example1:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
- example2:f,b,g,a,d,i,c,e,h
- example3:a,b,d,c,e,f,g,h,i
层次遍历算法:
- 建立一个队列;
- 将根节点指针入队列;
- 从队列取出一个元素;
- 访问该元素所指的节点,若该元素所指节点的左,右子节点非空,则将该元素所指节点的左子节点指针和右子节点指针顺序入队列;
- 重复3步骤;
以上面的example1为例:
- 将指向1的指针入队列;
- 取出指向1的指针,获取1节点,获取1节点左子节点指针和右子节点指针;
- 将指向2节点和指向3节点的指针顺序入队列;
- 取出指向2节点的指针,获取2节点,获取2节点左子节点指针和右子节点指针;
- 将指向4节点和指向5节点的指针顺序入队列;
- 取出指向3点节点的指针,获取3节点,获取3节点左子节点指针和右子节点指针;
- 将指向6节点和指向7节点的指针顺序入队列;
- ……
当队列取完,二叉树遍历完毕。