树是一种非线性结构
结点的度:结点拥有的子树个数或分支个数
树的度:树中各节点度的最大值
根节点的高度为树的高度
如果将树中结点各子树看成从左至右是有次序的,不能互换的,称该树为有序树否则为无序树
树的存储结构-顺序存储结构
双亲存储结构
int tree[maxsize];
数组元素的内容表示该节点的双亲节点 ,根节点位置上的内容为-1
树的存储结构-链式存储结构
孩子存储结构(图的邻接表)
孩子兄弟存储结构()
二叉树
满二叉树与完全二叉树
二叉树的性质:
1、非空二叉树上叶子结点数等于双分支节点数+1 即n0=n2+1
推导:由总分支数=总结点数-1 得n0+n1+n2-1=n1+2n2
2、二叉树第i层上最多有2^(i-1)个结点
3、高度为K的二叉树最多有2^k-1个结点 ,即满二叉树前k层结点数为2^k-1
4、i为某结点a的编号,i/2、2i、2i+1 为双亲和左右孩子
5、给定n个结点, 构成 [(n+1)*(n+2)*......2n]/[1*2*.....(n+1)]种树
6、具有n个结点的完全二叉树高度h为 h-1<=log2 n<h
二叉树存储结构
顺序存储结构
适合于完全二叉树,存储一般二叉树会浪费空间
链式存储结构
typedef struct BTNode
{
char data;
struct BTNode *lchild;
struct BTNode *rchild;
} BTNode;
二叉树的遍历
前序遍历
void preorder(BTNode *p)
{
if(p!=NULL)
{
Visit(p);
preorder(p->lchild);
preorder(p->rchild);
}
}
中序遍历
void inorder(BTNode *p)
{
if(p!=NULL)
{
inorder(p->lchild);
Visit(p);
inorder(p->rchild);
}
}
后序遍历
void postorder(BTNode *p)
{
if(p!=NULL)
{
postorder(p->lchild);
postorder(p->rchild);
Visit(p);
}
}
层次遍历
void level(BTNode *p)
{
int front,rear;
BTNode *que[maxsize];
front=rear=0;
BTNode *q;
if(p!=NULL)
{
rear=(rear+1)%maxsize;
que[rear]=p;
while(front!=rear)//队列不空时进行循环
{
front=(front+1)%maxsize;
q=que[front];
Visit(q);
if(q->child!=NULL)//左子树不空 左子树根节点入队
{
rear=(rear+1)%maxsize;
que[rear]=q->lchild;
}
if(q->rchild!=NULL)//右子树不空 右子树根节点入队
{
rear=(rear+1)%maxsizr;
que[rear]=q->rchild;
}
}
}
}
改进递归的遍历算法
方法一:用户自定义栈代替系统栈
先序遍历非递归
void preorderNonrecursion(BTNode *bt)
{
if(bt!=NULL)
{
BTNode *Stack[maxsize];
int top=-1;
BTNode *p;
Stack[++top]=bt;
while(top!=-1)//栈空则退出循环
{
p=Stack[top--];
Visit(p);
if(p->lchild!=NULL)
{
Stack[++top]=p->rchild;//右孩子先入栈
}
if(p->rchild!=NULL)
{
Stack[++top]=p->lchild;//左孩子后入栈
}
}
}
}
中序遍历非递归
void inorderNonrecursion(BTNode *bt)
{
if(bt!=NULL)
{
BTNode *Stack[maxsize];
int top=-1;
BTNode *p;
p=bt;
while(top!=-1||p!=NULL)
{
while(p!=NULL)//左孩子存在 左孩子入栈
{
Stack[++top]=p;
p=p->lchild;
}
if(top!=-1)//栈不空的情况下出栈
{
p=Stack[top--];
Visit(p);
p=p->child;
}
}
}
}
后序遍历非递归
void postorderNonrecursion(BTNode *bt)
{
if(bt!=NULL)
{
BTNode *Stack1[maxsize];
int top1=-1;
int top2=-1;
BTNode *Stack2[maxsize];
BTNode *p=NULL;
Stack1[++top1]=bt;
while(top1!=-1)
{
p=Stack1[top1--];
Stack2[++top2]=p;
if(p->lchild!=NULL)
{
Stack1[++top1]=p->lchild; //与先序相反 先左后右
}
if(p->rchild!=NULL)
{
Stack2[++top1]=p->rchild;
}
}
while(top2!=-1)
{
p=Stack2[top2--];
Visit(p);
}
}
}
方法二:线索二叉树
省掉栈,把二叉树的遍历过程线性化
n个结点的二叉树 一定有n-1个分支 有n+1个空链域
每个结点多了两个标识域ltag域rtag
如果ltag=0则表示lchild为指针 指向结点的左孩子;如果ltag=1则表示lchild为线索,指向结点的前驱
如果rtag=0则表示rchild为指针 指向结点的右孩子;如果rtag=1则表示rchild为线索,指向结点的后继
树转化为二叉树:1、每一层都横穿起来 2、每个父节点都只保留最左的分支
二叉树转化为树:上述过程逆置
森林转化为二叉树:每一棵树转化即可
二叉树转化为森林:断开右孩子 并转化为树
树的遍历:
先序遍历:树的先序遍历对应二叉树的先序遍历
后序遍历:树的后序遍历对应二叉树的中序遍历
森林的遍历:同样为先序遍历和后序遍历
赫夫曼树与赫夫曼编码
最优二叉树:带权路径最短
构造方法:从集合中选出权值最小的两个 作为左右子树 二者之和为新结点 以此类推
赫夫曼树特点:1、树中无度为1的点 2、权值越大,离根越近
赫夫曼编码:
任一字符的编码都不是另一字符编码串的前缀编码
左0右1
赫夫曼n叉树:(m-1)%(k-1)==0 k为叉数 m为补上0结点的结点个数
_____________________________________________________
例题6-2 求一颗二叉树深度
int getDepth(BTNode *p)
{
int LD,RD;
if(p==NULL)
{
return 0;
}
else
{
LD=getDepth(p->lchild);
RD=getDepth(p->rchild);
return (LD>RD?LD:RD)+1;
}
}
例题6-3 寻找data域值等于key的点
void search(BTNode *p,BTNode *&q,int key)
{
if(p!=key)
{
if(p->data==key)
{
q=p;
}
else
{
search(p->lchild,q,key);
if(q==NULL){//在左子树中没找到才到右子树查找
search(p->rchild,q,key);
}
}
}
}