【数据结构】二叉树

通信工程的小伙伴请看文章使用说明

实验内容

实现功能(选做代码未贴出,需要请私信)
二叉树的二叉链表的创建
二叉树的前、中、后序遍历的递归算法
二叉树的前、中、后序遍历的非递归算法
层序遍历
求叶子结点数目
求数的深度
判断两棵树是否相似和左右子树互换的操作
求二叉树中位于后序序列第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()函数。

在这里插入图片描述

分析与总结

  1. 遍历算法由于均需要打印所有的节点,故它们的时间复杂度都是O(n)。相比于非递归算法借助栈的方式,递归实现的遍历操作更简洁,并且对于先、中、后序遍历,只需进行稍微的修改即可。
  2. 其他操作算法均利用递归实现,代码阅读性较强,易于理解。通过层层递归,算法访问了所有节点,故它们的时间复杂度也都是O(n)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值