树与二叉树
树
定义:树是一个有n个元素的集合,当n=0时,这是个空树,在一个非空树中,有一个根节点(root),它没有前驱节点,除根节点外的其他元素被分成的每个不相交的集合都是一棵树。
二叉树
1. 二叉树定义:
跟树的定义类似,但一个双亲节点最多只有两个子节点,一个叫左子树,一个叫右子树。
一个结点指向另一个结点就是双亲节点,因为前者衍生了后者,从前驱节点才能找到后继节点,是不是觉得前驱后继很眼熟吗?单链表也是一种特殊的树结构,后继节点就是子节点。因为一个节点也是棵树,所以我们管他叫左子树,和右子树。左右是人为规定划分的,一颗二叉树的左右子树互换后是一颗新的二叉树。
2. 满二叉树与完全二叉树定义:
满二叉树的意思是:所有非叶子节点,既有左孩子,又有右孩子,所有叶子节点都在同一层(叶子节点是指没有孩子的节点)
完全二叉树的意思是:叶子节点只能存在树的最底层,和最底层的上一层,最底层的上一层的所有非叶子节点的右孩子若不为空,则左孩子一定不为空,左孩子若为空,右孩子无所谓。且完全二叉树存在节点的序号跟满二叉树的节点序号是相同的。
3.
性质1 | 一颗二叉树上的第n层最多有 |
性质2 | 一个深度为k的二叉树,最多有 |
性质3 | 对于一颗非空二叉树,节点的孩子数为2的节点数+1=叶子节点个数 |
性质4 | 具有n个节点的完全二叉树的深度为 |
性质5 | 如果按从上到下从左到右从1开始编号,完全二叉树的双亲节点是孩子节点编号/2下去整,左孩子是双亲节点编号的两倍,右孩子是两倍+ |
解释说明:
1因为,第一层最多是一个根节点,第二层最多是两个孩子,第三层最多是四个,所以第n层是
2因为完全二叉树 ,若只有根节点则是1个,若有两层,则是1+2=3个,三层则是1+2+4=7,所以n层是,
3 这个结论是可以证明的,因为一棵树的节点总个数n,可以按节点的度(孩子个数)划分节点,所以n=n0+n2+n1 ,有得节点度为1,有得节点度为2,还有度为0的点。从另一种角度去想,我有一个度为2的点是不是会发出两个分支,一个度为1的点会发出一个分支,但是除了根节点,没有节点能让根节点做分支,所以n=n1+2*n2+1 前后两个式子联立就解得该性质3
4通过性质1 和性质2 可知,层数为k的二叉树中 总结点个数 n ,满足,前者是只有一层的情况下满足,后者是满二叉树的情况下满足,对其取对数求解得到性质四
二叉树的存储
顺序存储
#define MAX_TREE_SIZE 100 //二叉树的最大节点数
typedef TElemType SqBiTree[MAX_TREE_SIZE+1]
从一号单元当做根节点,假设一个节点的位置为n,则左孩子为2n,右孩子为2n+1
链式存储
typedef struct BiTNode{
TElemType data; //待存入数据的类型
strcuct BiTNode *lchild,*rchild;//指向左孩子和右孩子的指针 可以加个parent指针,使得节点不再是单方向的了
}BTNode,*BiTree;
二叉树的遍历
三种遍历方式 先中后,这里的先中后 指的是根的顺序,孩子的顺序默认是先左后右
先序遍历
访问该节点
自递归左子树
自递归右子树
中序遍历
自递归左子树
访问该节点
自递归右子树
后序遍历
自递归左子树
自递归右子树
访问该节点
二叉树的层序遍历
就是根节点入队,访问后出队,把左右孩子入队,接着访问队首元素,然后队首元素出队,然后左右孩子入队,就能实现层序遍历
线索二叉树
一颗二叉树有n个节点,如果是链式二叉树,有2n个指针,但是只有n-1个存储了孩子地址,所以可以利用剩下的指针,对二叉树进行 前中后三种顺序的线索化找到,按三种顺序遍历的前驱节点,增加两个标志,代表这里到底存储的是孩子还是线索,然后按需要的逻辑 给该节点若有空指针找到该节点按先中后三序变化的前驱节点,就完成了线索化,线索化后的二叉树没有空指针。
二叉树和森林的转化
用孩子兄弟法去转换,二叉树的左孩子还是左孩子,兄弟做森林的右孩子,就可以吧一颗二叉树拆成森林。
二叉树的前中序遍历和其转化的森林的前中序遍历得到的结果相同
哈夫曼树与霍夫曼编码
其实哈夫曼树很简单,我用电脑自带的画图给大家模拟一下哈夫曼树的建立过程大家立马就明白,咱也不会做动画,大家凑活凑活看,肯定能看懂
哈夫曼树就是每次都选择两颗最小的子树合并成一颗新的树
霍夫曼编码的目的是为了压缩,根据一篇文章出现的字母数量,出现一次给权值+1 让出现的少的先合并,然后哈夫曼树左分支代表0右分支代表1,用这个01编码串代表这个字母,就可以令出现次数多的字母以较少的01代码串表示,从而实现压缩的目的