2 基本数据结构
2.5 树
2.5.1 基本概念
首先是一些基本概念:结点、结点的度、分支结点、叶子结点、树的度、k叉树、结点的层次、树的深度。
然后是一些基于小学奥数的性质:
性质一:树中的结点数目等于所有结点的度数和加1。
性质二:若k叉树的层次从1开始,度为k的树中第i层上至多
性质三:深度为h的k叉树至多有
性质四:具有n个结点的k叉树最小深度为
2.5.2 二叉树
然后是二叉树以及基于小学奥数的二叉树的性质:
性质一:对任何一棵二叉树,如果其叶子结点个数为
性质二:若二叉树的层次从1开始,则在二叉树的第i层最多有
性质三:深度为h的二叉树最多有
性质四:具有n个结点的完全二叉树的深度为
2.5.3 二叉树的表示
顺序表示:用顺序表来存储和表示二叉树,按照从上到下从左到右的顺序写完全二叉树,如果有空的地方,在顺序表里也空出来。
链式表示:结点由数据域data+两个指针域lchild和rchild
typedef struct tnode
{
ElemType data;
struct tnode *lchild;
struct tnode *rchild;
}TreeNode;
2.5.4 二叉树的遍历
设访问根结点极为V,遍历根的左子树记为L,右R。
先序遍历:V-L-R
中序遍历:L-V-R
后序遍历:L-R-V
对于一棵二叉树1-2-3-4-5-6
先序遍历:0-1-3-4-2-5-6
中序遍历:3-1-4-0-5-2-6
后序遍历:3-4-1-5-6-2-0
这是用的递归的思想做的遍历,代码如下:
//先序遍历
void BinaryTree::PreOrder(
TNODE *t;
void(*Visit)(TNODE*))
{
if(t)
{
Visit(t);
PreOrder(t->lChild, Visit);
PreOrder(t->rChild, Visit);
}
}
//中序遍历
void BinaryTree::InOrder(
TNODE *t;
void(*Visit)(TNODE*))
{
if(t)
{
InOrder(t->lChild, Visit);
Visit(t);
InOrder(t->rChild, Visit);
}
}
//后序遍历
void BinaryTree::PostOrder(
TNODE *t;
void(*Visit)(TNODE*))
{
if(t)
{
PostOrder(t->lChild, Visit);
PostOrder(t->rChild, Visit);
Visit(t);
}
}
非递归算法利用栈来实现
还是这个图
先序遍历:
访问0,2入栈,指向1;访问1,4入栈,指向3;访问3,叶子结点,4出栈,指向4;访问4,叶子结点,2出栈,指向2;访问2,6入栈,指向5;访问5,叶子结点,6出栈,指向6;访问6,栈空,结束。
中序遍历:
0入栈,1入栈,3入栈,叶子结点,访问3,访问1,4入栈,访问4,访问0,2入栈,5入栈,访问5,访问2,6入栈,访问6。
后序遍历:
0a入栈,1a入栈,3a入栈,置为3b,访问3,置为1b,4a入栈,置为4b,访问4,访问1,置为0b,2a入栈,5a入栈,置为5b,访问5,置为2b,6a入栈,置为6b,访问6,访问2,访问0,结束。
层序遍历:
用队列实现:0入队,0出队;1入队,2入队;1出队,3入队,4入队;2出队,5入队,6入队;3出队,4出队,5出队,6出队,完成。
2.5.5 Huffman树与Huffman编码
对于Huffman Tree,先说它的一些概念:
结点到结点的分支称为路径。路径上的分支数称为路径长度,记为l。结点的路径长度定义为从根结点到该结点的路径上分支的数目。树的路径长度是从树根到所有叶子结点的路径长度之和。 树的带权路径长度为树中所有叶子结点的带权路径长度之和,记为WPL
在所有路径长度相同的二叉树中,WPL最小的树称为最优二叉树(Huffman Tree)。
Huffman Tree的构造:构造目标是根据n个给定的权值
构造过程:
构造n棵二叉树的集合,其中每棵二叉树中均只含一个带权值为的根结点。在F中选最小权值的两棵树,分别作为左、右子树构造新的,新树根结点权值为这两个之和,重复,直到F中只含一棵树。
下面是一个例子:
然后来看编码问题:
定长编码就没什么好说的了,0和1排列组合,重点是变长编码,这个就涉及到优化问题了。概率大的给短码,概率小的给长码。一种就是前缀编码,可以用二叉树来表示。
定长编码:
前缀编码:
最后登场的就是Huffman编码:
示意图:
编码:
除了单向的,还可以有双向的Huffman编码
Huffman编码的不足:没有错误保护功能,需要精确统计文本的字符出现概率,编码速度较慢。
2.5.6 二叉搜索树(BST)
空树或者有以下特点:
每个结点都有一个关键字并且:任意结点关键字大于等于该结点左子树中所有结点含有的关键字;同时该结点的关键字小于等于右子树中所有结点含有的关键字。
搜索就很简单啦,比较看谁大谁小然后顺着路就过去了。
插入类似于查找,在查找失败之后执行插入操作就ok了。
举个例子:A S E R C H I N,构造一棵BST
由二叉树的前序序列和中序序列可以唯一地确定一棵二叉树。
举个例子:
前序序列为:0-1-2-3-4-5-6-7-8;中序序列为:2-1-4-3-0-5-7-6-8
得到的二叉树为: