四种遍历算法
遍历算法几乎是二叉树所有算法的基础,而我们已经知道了二叉树的遍历方法有先序遍历、中序遍历、后序遍历,层序遍历;下面我们用代码实现一下四种遍历方法。
先定义一下二叉树的结构体:
//
typedef struct BiNode{
TElemType data;
struct BiNode *lchild,*rchild;
}BiNode,*BiTree;
因为二叉树使用递归定义的,所以使用递归可以很轻易的写出遍历算法
先序遍历:
//先序遍历二叉树
void TraverseBiTree(BiTree T)
{
if (T == NULL)
return ;
visit(T);//访问当前根节点
TraverseBiTree(T->lChild);//递归遍历左子树
TraverseBiTree(T->rChlid);//递归遍历右子树
}
中序遍历也是可以写出的:
//中序遍历二叉树
void InOrderBiTree(BiTree T)
{
if (T == NULL)
return ;
InOrderBiTree(T->lChild);
visite(T);
InOrderBiTree(T->rChlid);
}
同样写出后序遍历算法:
//中序遍历二叉树
void InOrderBiTree(BiTree T)
{
if (T == NULL)
return ;
InOrderBiTree(T->lChild);
InOrderBiTree(T->rChlid);
visite(T);
}
三种遍历算法非常相似,只有访问根节点的代码位置不同,而遍历算法的时间复杂度是O(n)[每个结点只访问一次],空间复杂度也是O(n)[栈占用的最大辅助空间]
除了以上三种算法外,还有一种层次遍历法,顾名思义,就是二叉树从根节点开始按从上到下,从左到右的顺序访问每个结点,每个结点只被访问一次。
使用队列来完成这个算法:
- 将根节点入队
- 对不空时循环:从队列中出列一个结点p,访问它;
- 若它有左孩子结点,将左孩子入队
- 若它有左孩子结点,将左孩子入队 先定义一下使用的队列:
typedef struct{
BTnode data[MaxSize];//存放队中元素
int front,rear;//队头指针,队尾指针
}SqQueue
再来实现层序遍历:
void levelOrder(TreeNode *T){
BTNode *p;
SqQueue *qu;
QueueInit(qu);//初始化队列
Queueadd(qu,T);//根节点入队
while(!QueueEmpty(qu)){//队不为空,循环
QueueDelet(qu,p)//出队结点p,p指向当前出队的结点
visi(p);//访问结点p
if(p->lchild != NULL) Queueadd(qu,p->lchild);//根节点入队
if(p->rchild != NULL) Queueadd(qu,p->lchild);//根节点入队
}
}
二叉树的建立
在已知某种遍历顺序时候,我们可以通过遍历顺序创建二叉树;
首先是先序创建,获得了二叉树的结点信息,建立了二叉树的存储结构,在建立二叉树的过程中按照先序方式建立(只知道先序序列是不够的,还要知道空结点的位置才能得到唯一的二叉树,这里用'#'代表空结点):
//先序创建二叉树
bool DLRCreatBiTree(BiTree &T){
cin >> ch;
if(ch == "#") T = NULL;
else{
if(!T = (BiTNode*)malloc(sizeof(BiTNode)));//T = new BiNode;
exit(-1);
T->data = ch;//申请空间储存数据——>生成根节点
DLRCreatBiTree(T->lchild);//构建左子树
DLRCreatBiTree(T->rchild);//构建右子树
}
return true;
}
中序创建二叉树和后序创建爱你二叉树与先序创建也一般无二;
中序创建:
//中序创建二叉树
bool LDRCreatBiTree(BiTree &T){
cin >> ch;
if(ch == "#") T = NULL;
else{
LDRCreatBiTree(T->lchild);//构建左子树
if(!T = (BiTNode*)malloc(sizeof(BiTNode)));//T = new BiNode;
exit(-1);
T->data = ch;//申请空间储存数据——>生成根节点
LDRCreatBiTree(T->rchild);//构建右子树
}
return true;
}
后序创建:
bool LRDCreatBiTree(BiTree &T){
cin >> ch;
if(ch == "#") T = NULL;
else{
LRDCreatBiTree(T->lchild);//构建左子树
LRDCreatBiTree(T->rchild);//构建右子树
if(!T = (BiTNode*)malloc(sizeof(BiTNode)));//T = new BiNode;
exit(-1);
T->data = ch;//申请空间储存数据——>生成根节点
}
return true;
}
复制二叉树
复制算法也是使用递归来完成:
- 如果是空树,递归结束
- 否则申请新的结点空间,复制结点
- 递归复制左子树
- 递归复制右子树
当然是可以用多种遍历循序来完成复制的:
先序复制:
int DLRCopy(BiTree T,BiTree &newT){
if(T == NULL){
newT = NULL;
return 0;
}
else {
newT = (BiTNode*)malloc(sizeof(BiTNode));//newT = new BiTNode;
newT->data = T->data;
DLRcopy(T->lchild,newT->lchild);
DLRcopy(T->rchild,newT->rchild);
}
}
中序复制:
int LDRCopy(BiTree T,BiTree &newT){
if(T == NULL){
newT = NULL;
return 0;
}
else {
LDRcopy(T->lchild,newT->lchild);
newT = (BiTNode*)malloc(sizeof(BiTNode));//newT = new BiTNode;
newT->data = T->data;
LDRcopy(T->rchild,newT->rchild);
}
}
后序复制:
int LRDCopy(BiTree T,BiTree &newT){
if(T == NULL){
newT = NULL;
return 0;
}
else {
LRDcopy(T->lchild,newT->lchild);
LRDcopy(T->rchild,newT->rchild);
newT = (BiTNode*)malloc(sizeof(BiTNode));//newT = new BiTNode;
newT->data = T->data;
}
}
求二叉树的深度
求取二叉树的深度:
- 如果是空树,则深度为0
- 否则,递归计算左子树的深度为m,递归计算右子树的深度为n,二叉树的深度为m与n的较大者加1
int Depth(BiTree T){
if*T == NULL) return 0;
else {
int m = Depth(T->lchild);
int n = Depth(T->rchild);
if(m > n) return(m + 1);
else retrun (n + 1);
}
}
计算二叉树结点总数
- 如果是空树,则结点数为0
- 否则,结点个数为左子树的结点个数+右子树的结点数+1
int NodeCount(BiTree T){
if(T == NULL) return 0;
else{
return NodeCount(T->lchild) +NodeCount(T->rchild) + 1;
}
}
计算叶子结点的个数
- 如果是空树,则结点数为0
- 否则,结点个数为左子树的结点个数+右结点+右子树的结点数
int LeadCount(BiTree T){
if(T == NULL) return 0;
if(T->lchild == NULL && T->rchild == NULL) return 1;
else{
return NodeCount(T->lchild) +NodeCount(T->rchild) +;
}
}
线索二叉树(Threaded Binary Tree)
当使用二叉链表作为二叉树的存储结构时,可以很方便地找到某个结点的左右孩子:但是一般情况下,无法直接找到该节点在某种遍历序列中的前驱和后继结点;
有三种解决方法:
- 通过遍历寻找——浪费时间
- 再增设前驱,后继指针域——浪费空间
- 利用二叉链表的空指针域
线索华——利用二叉链表的空指针域:
如果某个结点的左孩子为空,则将空的左孩子的指针域改为指向其前驱;如果某个结点的右孩子为空,则将空的右孩子的指针域改为指向其后继;[这种改变指向的指针称为“”线索“”]
为了区分lchildhe==和rchild,对二叉链表中每个结点增设两个标志域ltag,rtag;并约定:
- ltage = 0 lchild 指向该结点的左孩子
- ltage = 1 lchild 指向该结点的前驱
- rtage = 0 rchild 指向该结点的右孩子
- rtage = 1 rchild 指向该结点的后继 增设一个头结点:ltag = 0,lchild之下部分根节点;rtag = 1rchild指向遍历序列中的最后一个结点(遍历序列中的第一个结点lc域和最后一个结点的rc域都指向头结点)
定义存储结构:
typedef steuct BiTHrNode{
int data;
bool ltag,rtag;
steuct BiTHrNode *lchild,*rchild;
}BiThrNode,*BiThrTree;