实验内容
实现功能(选做代码未贴出,需要请私信) |
---|
二叉树的二叉链表的创建 |
二叉树的前、中、后序遍历的递归算法 |
二叉树的前、中、后序遍历的非递归算法 |
层序遍历 |
求叶子结点数目 |
求数的深度 |
判断两棵树是否相似和左右子树互换的操作 |
求二叉树中位于后序序列第k个结点的值(选做) |
判断二叉树是否是完全二叉树(选做) |
数据结构定义
算法思想及算法设计
功能 | 算法设计 | 函数名 |
---|---|---|
二叉链表的创建 | 以先序输入来创建二叉树,其中与完全二叉树相比缺少结点的位置用“#”代替。采用递归调用的方式,依次创建根节点的左右子树。 | createBT |
前、中、后序遍历的递归算法 | 以前序遍历为例,如果根节点存在,先输出当前根节点的值,之后进行左右子树的探索。 | preordertraverse |
前、中、后序非递归算法 | 以前序遍历为例,若根节点不为空,输出根节点的值,然后根节点进栈, 一直向左走到头,之后逐步退回根节点,访问右子树,直到栈空或指针为空。 | preorder |
层序遍历 | 需要利用队列辅助解决问题,若根节点不为空,进队。输出队头元素的值,访问其左右子树,若不为空,则依次进队,直到队为空时结束循环。 | leveltraverse |
求叶子结点数目 | 若节点为空返回0,若不为空,但左右孩子都不存在时,返回1,其他情况,要分别调用自身函数,遍历左右子树,最后结果相加。 | nodenum |
求树的深度 | 分别递归求左右子树的深度,最后比较左右子树的深度,选择深度大的作为该二叉树的深度。 | depth |
判断两颗树是否相似 | 只有当每个节点的左右孩子存在性一致的时候,我们才能说这个两棵树相似。若节点的左右子树都不存在,显然相似;若只有一个孩子存在,显然不相似;其他情况要进一步递归两棵树的左孩子和右孩子。 | similar |
左右子树互换 | 设置一个中间指针,用于过渡。将左右子树的指针进行交换,之后再调用自身,分别互换以左孩子为根和以右孩子为根的子树。 | exchange |
实验代码
基本功能函数(若对函数名不理解,请看上表第三列:函数名)
#include<iostream>
#include<stack>
#include<iomanip>
#include<queue>
using namespace std;
typedef char BTelemtype;
typedef int Status;
typedef struct BTNode
{
BTelemtype data;
struct BTNode*lchild;
struct BTNode*rchild;
}BTNode,*BiTree;
void createBT(BiTree&T)
{
char c;
cin>>c;
if(c=='#')
T=NULL;
else
{
T=new BTNode;
T->data=c;
createBT(T->lchild);
createBT(T->rchild);
}
}
void preordertraverse(BiTree T)
{
if(T)
{
cout<<T->data;
preordertraverse(T->lchild);
preordertraverse(T->rchild);
}
}
void inordertraverse(BiTree T)
{
if(T)
{
inordertraverse(T->lchild);
cout<<T->data;
inordertraverse(T->rchild);
}
}
void postordertraverse(BiTree T)
{
if(T)
{
postordertraverse(T->lchild);
postordertraverse(T->rchild);
cout<<T->data;
}
}
void leveltraverse(BiTree T)
{
queue<BiTree>q;
if(T==NULL)
return;
else
q.push(T);
while(!q.empty())
{
BiTree p;
p=q.front();
cout<<p->data;
q.pop();//输出后立即出队
if(p->lchild)
q.push(p->lchild);
if(p->rchild)
q.push(p->rchild);
}
}
void preorder(BiTree T)
{
if(T==NULL)
return;
BiTree p=T;
stack<BiTree>s;
while(!s.empty()||p)
{
if(p)
{
cout<<p->data;
s.push(p);//保留根节点进栈,访问左子树
p=p->lchild;
}
else
{
p=s.top();//记录根节点,根节点出栈,遍历右子树
s.pop();
p=p->rchild;
}
}
}
void inorder(BiTree T)
{
if(T==NULL)
return;
BiTree p=T;
stack<BiTree>s;
while(!s.empty()||p)
{
if(p)
{
s.push(p);//遍历左子树,根节点进栈
p=p->lchild;
}
else
{
p=s.top();//当左子树遍历完成,根节点出栈,并输出值,进而遍历右子树
s.pop();
cout<<p->data;
p=p->rchild;
}
}
}
void postorder(BiTree T)
{
if(T==NULL)
return;
stack<BiTree>s;
BiTree curp,lastp;
curp=T;
lastp=NULL;
while(curp)
{
s.push(curp);
curp=curp->lchild;
}
while(!s.empty())
{
curp=s.top();
s.pop();
if(curp->rchild==NULL||curp->rchild==lastp)
{
cout<<curp->data;
lastp=curp;
}
else
{
s.push(curp);
curp=curp->rchild;
while (curp)
{
s.push(curp);
curp=curp->lchild;
}
}
}
}
int nodenum(BiTree T)
{
if(!T)
return 0;
else
return nodenum(T->lchild)+nodenum(T->rchild)+1;//自身加上自己的两个孩子结点
}
int leafnum(BiTree T)
{
if(!T)
return 0;
if(!T->lchild&&!T->rchild)
return 1;
else
return leafnum(T->lchild)+leafnum(T->rchild);
}
int depth(BiTree T)
{
if(!T)
return 0;
else
{
int a=depth(T->lchild);
int b=depth(T->rchild);
return (a>b)?(a+1):(b+1);//加上子树根结点
}
}
void exchange(BiTree &T)
{
BiTree temp;
if(T!=NULL)
{
temp=T->lchild;
T->lchild=T->rchild;
T->rchild=temp;
exchange(T->lchild);
exchange(T->rchild);
}
}
bool similar(BiTree T1,BiTree T2)
{
bool l,r;
l=r=false;
if(T1==NULL&&T2==NULL)
return true;
else if(T1==NULL||T2==NULL)
return false;
else
{
l=similar(T1->lchild,T2->lchild);
r=similar(T1->rchild,T2->rchild);
return l&&r;
}
}
bool equal(BiTree T1,BiTree T2)
{
bool l,r;
if(T1==NULL&&T2==NULL)
return true;
else if(T1==NULL||T2==NULL)
return false;
else
{
if(T1->data!=T2->data)
return false;
l=equal(T1->lchild,T2->rchild);
r=equal(T1->rchild,T2->rchild);
return l&&r;
}
}
主函数
int main()
{
BiTree Tree;
cout<<"以先序遍历输入二叉树(空结点用‘#’表示):";
createBT(Tree);
cout<<endl<<"先序输出:";
preordertraverse(Tree);
cout<<endl<<"中序输出:";
inordertraverse(Tree);
cout<<endl<<"后序输出:";
postordertraverse(Tree);
cout<<endl<<"层序输出:";
leveltraverse(Tree);
cout<<endl<<"树的深度:"<<depth(Tree);
cout<<endl<<"结点的个数:"<<nodenum(Tree);
cout<<endl<<"叶结点的个数:"<<leafnum(Tree);
cout<<endl<<"左右子树互换:";
exchange(Tree);
cout<<endl<<"先序输出:";
preorder(Tree);
cout<<endl<<"中序输出:";
inorder(Tree);
cout<<endl<<"后序输出:";
postorder(Tree);
cout<<endl<<"层序输出:";
leveltraverse(Tree);
cout<<endl<<"相似判断"<<endl<<"以先序遍历输入另一颗二叉树(空结点用‘#’表示):";
BiTree Tree2;
createBT(Tree2);
if(similar(Tree,Tree2))
cout<<"两棵树相似"<<endl;
else
cout<<"两棵树不相似"<<endl;
return 0;
}
算法测试结果
Attention:请注意输入格式, 本文以先序输入来创建二叉树,具体实现请看createBT()函数。
分析与总结
- 遍历算法由于均需要打印所有的节点,故它们的时间复杂度都是O(n)。相比于非递归算法借助栈的方式,递归实现的遍历操作更简洁,并且对于先、中、后序遍历,只需进行稍微的修改即可。
- 其他操作算法均利用递归实现,代码阅读性较强,易于理解。通过层层递归,算法访问了所有节点,故它们的时间复杂度也都是O(n)。