《数据结构》复习之二叉树

  • 二叉树的性质

    • 1满二叉树和完全二叉树
    • 2二叉树的主要性质
  • 二叉树的数据结构
  • 二叉树的算法
  • 补充
  • 总结

1.二叉树的性质

1.1满二叉树和完全二叉树

  在一棵二叉树中,如果所有的分支节点都有左孩子和右孩子,并且叶子节点都集中在二叉树的最下一层,则这样的二叉树被称为满二叉树

  如果一棵深度为k有n个节点的二叉树进行编号后,各结点的编号与深度为k的满二叉树中相同位置山的结点的编号军相投,那么这棵二叉树就是一颗完全二叉树

1.2二叉树的主要性质

  1. 总分支数=总结点数-1(这条结论对任何树都适用,不止是二叉树)

    证明:在二叉树中除根节点之外,每一个结点都有唯一的一个分支指向它,由此可证。

  2. 非空二叉树上叶子节点数等于双分支节点数+1

    证明:由上一条性质可证。设二叉树叶子节点数为n0,单分支节点数为n1,双分支节点数为n2,则总结点数为n0+n1+n2。总分支数为2*n2+n1。由上一条性质可得,n0+n1+n2-1=2*n2+n1。化简得:n0=n2+1。(注意,这种证明方法常常被用到

  3. 二叉树的第i层上最多有2i-1(i>=1)个节点。

    证明:等比数列。

  4. 高度为k的二叉树最多有2k-1(K>=1)个节点。换句话说满二叉树中前k层的结点个数为2k-1。

    证明:等比数列求和问题。

  5. 有n个阶段的完全二叉树,对各节点从上到下,从左到右依次编号(编号范围1~n),则节点之间有如下关系。

    若i为某节点a的编号,则:

    如果i!=1,则双亲节点的编号为i/2向下取整。

    如果2i<=n,则左孩子的编号为2i;如果2i>n,则a无左孩子。

    如果2i+1<=n,则右孩子的编号为2i+1;如果2i+1>n,则a无右孩子。

    这些性质中最常用的还是1-4条性质。

2.二叉树的数据结构

  二叉树也有顺序存储结构和链式存储结构。顺序存储结构是用数组存储,下标遵循上面第5条性质,注意下标从1开始。链式存储结构是最常用的存储二叉树的结构,如下图所示。

  

  其中data表示节点数据域,用与存储对应的数据元素;lchild和rchild分别表示左指针域和右指针域,分别用于存储左孩子结点和右孩子结点的位置。

  定义如下:

typedef struct BTNode
{
    char data;
    struct BTNode *lchild;
    struct BTNode *rchild;
}BTNode

3.二叉树的算法

  二叉树的算法主要是遍历算法,包括深度遍历(先序遍历,中序遍历,后序遍历)和广度遍历(层次遍历)。这也是解大多数二叉树题目的关键。

  三种遍历(先序遍历,中序遍历,后序遍历)代码如下 

void PreOrderTraverse(BiTree T) //先序遍历
{
    if(T!=NULL)
    {
        cout<<T->data<<" ";
        PreOrderTraverse(T->lchild);
        PreOrderTraverse(T->rchild);
    }
}

void InOrderTraverse(BiTree T)  //中序遍历
{
    if(T!=NULL)
    {
        InOrderTraverse(T->lchild);
        cout<<T->data<<" ";
        InOrderTraverse(T->rchild);
    }
}

void PostOrderTraverse(BiTree T)   //后序遍历
{
    if(T!=NULL)
    {
        PostOrderTraverse(T->lchild);
        PostOrderTraverse(T->rchild);
        cout<<T->data<<" ";
    }
}

  层次遍历的伪代码如下

  

根节点入队
while(队不空)
{
    节点出队,并访问
    if(左子树不为空)
        左子树入队
    if(右子树不为空)
        右子树入队
}

  c++代码如下:

  

void LevelOrderTraverse()
{
    int front=0,rear=0;   //定义循环队列
    BiTNode *que[Maxsize];
    front=rear;
    BiTNode *q;
    if(T!=NULL)          //如果传过来的树不为空
    {
        rear=(rear+1)%Maxsize;
        que[rear]=T;                //根节点入队
        while (front!=rear)    //队列不为空
        {
            front=(front+1)%Maxsize;
            q=que[front];        //队头出队
            cout<<q->data<<" ";    //访问队头
            if (q->lchild!=NULL)       //如果左子树不空,则左子根入队
            {
                rear=(rear+1)%Maxsize;
                que[rear]=q->lchild;
            }
            if (q->rchild!=NULL)       //如果右子树不空,则右子根入队
            {
                rear=(rear+1)%Maxsize;
                que[rear]=q->rchild;
            }
        }
    }
}

  这4个算法(其实只算两个)就像模板一样,大多数二叉树题目只要会套这个模板就能解决,因此它非常重要。

4.补充

  下面的程序是我大二数据结构课设时写的,现在看起来有许多不足和幼稚的地方,但仍然具有一定的参考价值,将它放在这里。

  


#define Maxsize 100
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
#include<fstream>
#include<string>
#include<iostream>
using namespace std;

typedef char TElemType;

typedef struct BiTNode
{
    TElemType data;
    int flag;
    int layer;
    struct BiTNode *lchild,*rchild;
}BiTNode,* BiTree;

#include "SqStack.h"

class BinaryTreeOperator   //  二叉树操作类
{
public:
    BiTree T;
    BinaryTreeOperator()
    {
        InitBiTree();
    }
    ~BinaryTreeOperator()
    {
        DestroyBitree(T);
    }
    void InitBiTree();   //初始化函数
    BiTree CreatBitree(char *str1,char*str2,int i,int j,int k,int l);  //根据先序和后序遍历结果建立二叉树
    void DestroyBitree(BiTree T);  //销毁二叉树函数
    void PreOrderTraverse(BiTree T);   //递归先序遍历
    void InOrderTraverse(BiTree T);   //递归中序遍历
    void PostOrderTraverse(BiTree T);    //递归后序遍历
    void NOPreOrder();   //非递归先序遍历
    void NOInOrder();     //非递归中序遍历
    void NOPostOrder();    //非递归后序遍历
    void LevelOrderTraverse();  //层次遍历
    void display(BiTree T);    //按二叉树形态遍历输出
    int BiTreeDepth(BiTree T);    //求二叉树的深度
    void computelayer();   //标记二叉树的层数
    int lush();
    void CountLeaf(BiTree T,int &num);   //数叶子
    void Exchange(BiTree T);    //交换左右子树
    void JudgeTree();    //判断一棵是否为完全二叉树
};

BiTree BinaryTreeOperator::CreatBitree(char *str1,char*str2,int i,int j,int k,int l)  //根据先序和后序遍历结果建立二叉树
{
    int n;
    BiTNode *p;
    p = new BiTNode;
    p->data=str2[l];
    n=i;
    for (;str1[n]!=str2[l];n++);
    if (n==i)
    {
        p->lchild=NULL;
    }
    else
    {
        p->lchild=CreatBitree(str1,str2,i,n-1,k,k+n-i-1);
    }
    if (n==j)
    {
        p->rchild=NULL;
    }
    else
    {
        p->rchild=CreatBitree(str1,str2,n+1,j,k+n-i,l-1);
    }
    return p;
}

void BinaryTreeOperator::InitBiTree() //初始化二叉树
{
    T = new BiTNode;
    if (!T)
    {
        cout<<"初始化失败!"<<endl;
        exit(0);
    }
    T->data=NULL;
    T->lchild=NULL;
    T->rchild=NULL;
}

void BinaryTreeOperator::DestroyBitree(BiTree T) //销毁二叉树
{
    if(T!=NULL)
    {
        DestroyBitree(T->lchild);
        DestroyBitree(T->rchild);
        delete T;
    }
}

void BinaryTreeOperator::PreOrderTraverse(BiTree T)  //递归先序遍历
{
    if(T!=NULL)
    {
        cout<<T->data<<" ";
        PreOrderTraverse(T->lchild);
        PreOrderTraverse(T->rchild);
    }
}

void BinaryTreeOperator::InOrderTraverse(BiTree T)//递归中序遍历
{
    if(T!=NULL)
    {
        InOrderTraverse(T->lchild);
        cout<<T->data<<" ";
        InOrderTraverse(T->rchild);
    }
}

void BinaryTreeOperator::PostOrderTraverse(BiTree T)//递归后序遍历
{
    if(T!=NULL)
    {
        PostOrderTraverse(T->lchild);
        PostOrderTraverse(T->rchild);
        cout<<T->data<<" ";
    }
}

void BinaryTreeOperator::NOPreOrder()  //非递归先序遍历
{
    if (T!=NULL)
    {
        BiTNode * p;
        SqStack S;
        InitStack(S);
        Push(S,T);
        while (!StackEmpty(S))
        {
            Pop(S,p);
            cout<<p->data<<" ";
            if (p->rchild!=NULL)
            {
                Push(S,p->rchild);
            }
            if (p->lchild!=NULL)
            {
                Push(S,p->lchild);
            }
        }
    }
}

void BinaryTreeOperator::NOInOrder()  //非递归中序遍历
{
    if (T!=NULL)
    {
        BiTNode * p;
        SqStack S;
        InitStack(S);
        Push(S,T);
        while (!StackEmpty(S))
        {
            while (GetTop(S,p)&&p)
            {
                Push(S,p->lchild);
            }
            Pop(S,p);
            if (!StackEmpty(S))
            {
                Pop(S,p);
                cout<<p->data<<" ";
                Push(S,p->rchild);
            }
        }
    }
}

void BinaryTreeOperator::NOPostOrder()  //非递归后序遍历
{
    BiTNode *t;
    BiTNode *p;
    BiTNode *s;
    SqStack Stack;
    InitStack(Stack);
    t=T;
    p=T;
    while(t)     //让其一直往左走,并入栈
    {
        t->flag=0;   //做好标志位,0表示右指数未访问,1表示右指数被访问
        Push(Stack,t);
        t=t->lchild;
    }
    while (!StackEmpty(Stack))
    {
        s=GetTop(Stack);
        if(s->rchild==NULL||s->flag==1)   //如果右指数为空或标志位为1则出栈并输出
        {
            Pop(Stack,p);
            cout<<p->data<<" ";
        }
        else           //并对右子树做相同的操作
        {
            s->flag=1;
            p=s->rchild;
            while (p!=NULL)
            {
                p->flag=0;
                Push(Stack,p);
                p=p->lchild;
            }
        }
    }   //无需管入栈的,因为下一轮循环会处理好
}

void BinaryTreeOperator::LevelOrderTraverse() //层次遍历
{
    int front=0,rear=0;
    BiTNode *que[Maxsize];
    front=rear;
    BiTNode *q;
    if(T!=NULL)          //如果传过来的树不为空
    {
        rear=(rear+1)%Maxsize;
        que[rear]=T;                //根节点入队
        while (front!=rear)    //队列不为空
        {
            front=(front+1)%Maxsize;
            q=que[front];        //队头出队
            cout<<q->data<<" ";    //访问队头
            if (q->lchild!=NULL)       //如果左子树不空,则左子根入队
            {
                rear=(rear+1)%Maxsize;
                que[rear]=q->lchild;
            }
            if (q->rchild!=NULL)       //如果右子树不空,则右子根入队
            {
                rear=(rear+1)%Maxsize;
                que[rear]=q->rchild;
            }
        }
    }
}

void BinaryTreeOperator::display(BiTree T)  //利用层数,横向打印
{
    int i;
    if (T==NULL)
    {
        return;
    }
    display(T->rchild);
    for (i=1;i<T->layer;i++)
    {
        cout<<"    ";
    }
    cout<<T->data<<endl;
    display(T->lchild);
}

int BinaryTreeOperator::BiTreeDepth(BiTree T) //计算二叉树深度
{
    int L,R;
    if(T==NULL)
        return 0;
    else
    {
        L=BiTreeDepth(T->lchild);
        R=BiTreeDepth(T->rchild);
        return (L>R?L:R)+1;
    }
}

void BinaryTreeOperator::computelayer()  //计算层数
{
    int front=0,rear=0;
    BiTNode *que[Maxsize];
    front=rear;
    BiTNode *q;
    if(T!=NULL)          //如果传过来的树不为空
    {
        T->layer=1;
        rear=(rear+1)%Maxsize;
        que[rear]=T;                //根节点入队
        while (front!=rear)    //队列不为空
        {
            front=(front+1)%Maxsize;
            q=que[front];        //队头出队
            if (q->lchild!=NULL)       //如果左子树不空,则左子根入队
            {
                rear=(rear+1)%Maxsize;
                que[rear]=q->lchild;
                que[rear]->layer=q->layer+1;    //计算一下层数
            }
            if (q->rchild!=NULL)       //如果右子树不空,则右子根入队
            {
                rear=(rear+1)%Maxsize;
                que[rear]=q->rchild;
                que[rear]->layer=q->layer+1;  //计算一下层数
            }
        }
    }
}

void BinaryTreeOperator::CountLeaf(BiTree T,int &num) //数叶子节点个数
{
    if(T!=NULL)
    {
        if (T->lchild==NULL&&T->lchild==NULL)
        {
            num++;
        }
        CountLeaf(T->lchild,num);
        CountLeaf(T->rchild,num);
    }
}

void BinaryTreeOperator::Exchange(BiTree T) //交换左右子树
{
    if(T!=NULL)
    {
        BiTNode * p;
        p=T->lchild;
        T->lchild=T->rchild;
        T->rchild=p;
        Exchange(T->lchild);
        Exchange(T->rchild);
    }
}

void BinaryTreeOperator::JudgeTree()  //判断是否是完全二叉树
{
    int front=0,rear=0;
    BiTNode *que[Maxsize];
    front=rear;
    BiTNode *q;
    int flag=0;
    if(T!=NULL)          //如果传过来的树不为空
    {
        rear=(rear+1)%Maxsize;
        que[rear]=T;                //根节点入队
        while (front!=rear)    //队列不为空
        {
            front=(front+1)%Maxsize;
            q=que[front];        //队头出队
            if (q==NULL)
            {
                while (front!=rear)
                {
                    front=(front+1)%Maxsize;  //根据完全二叉树的性质,层次遍历中若出现空节点后面出现其他节点则不是完全二叉树
                    if (que[front]!=NULL)
                    {
                        flag=1;
                    }
                }
            }
            else
            {
                rear=(rear+1)%Maxsize;
                que[rear]=q->lchild;
                rear=(rear+1)%Maxsize;
                que[rear]=q->rchild;
            }
        }
    }
    if (flag==1)
    {
        cout<<"不是完全二叉树!"<<endl;
    }
    else
    {
        cout<<"是完全二叉树!"<<endl;
    }
}

int BinaryTreeOperator::lush()  //计算繁茂度
{
    computelayer();   //将层数标记好
    int C[10]={0};
    int max;
    int front=0,rear=0;
    BiTNode *que[Maxsize];
    front=rear;
    BiTNode *q;
    if(T!=NULL)          //如果传过来的树不为空
    {
        rear=(rear+1)%Maxsize;
        que[rear]=T;                //根节点入队
        while (front!=rear)    //队列不为空
        {
            front=(front+1)%Maxsize;
            q=que[front];        //队头出队
            C[q->layer]++;     //入队的时候对层数进行累加
            if (q->lchild!=NULL)       //如果左子树不空,则左子根入队
            {
                rear=(rear+1)%Maxsize;
                que[rear]=q->lchild;
            }
            if (q->rchild!=NULL)       //如果右子树不空,则右子根入队
            {
                rear=(rear+1)%Maxsize;
                que[rear]=q->rchild;
            }
        }
    }
    max=C[1];
    for (int i=2;i<=q->layer;i++)
    {
        if (C[i]>max)
        {
            max=C[i];
        }
    }
    return max*BiTreeDepth(T);
}

int main()
{
    int i=0,j=20,k=0,l=20;
    int choice;
    bool loop=true;
    BinaryTreeOperator Tree;
    char str1[81];
    char str2[81];
    int num=0;
    fstream Bitreef("Bitree.txt",ios::in);
    if (!Bitreef)
    {
        cout<<"打开Bitree.txt文件失败"<<endl;
        exit(0);
    }
    Bitreef.getline(str1,81);
    Bitreef.getline(str2,81);
    while(loop)
    {
        cout<<"     ***************二叉树应用****************\n";
        cout<<"     *   1.建立二叉树         *\n";
        cout<<"     *   2.递归先序遍历            *\n";
        cout<<"     *   3.递归中序遍历            *\n";
        cout<<"     *   4.递归后序遍历            *\n";
        cout<<"     *   5.非递归先序遍历       *\n";
        cout<<"     *   6.非递归中序遍历       *\n";
        cout<<"     *   7.非递归后序遍历       *\n";
        cout<<"     *   8.层次遍历              *\n";
        cout<<"     *   9.按形态输出         *\n";
        cout<<"     *   10.二叉树的深度       *\n";
        cout<<"     *   11.二叉树的繁茂度      *\n";
        cout<<"     *   12.叶子数目             *\n";
        cout<<"     *   13.交换左右子树           *\n";
        cout<<"     *   14.判断是否为完全二叉树         *\n";
        cout<<"     *   15.退出程序             *\n";
        cout<<"     *****************************************\n";
        cin>>choice;

        switch(choice)
        {
        case 1:
            Tree.T=Tree.CreatBitree(str1,str2,i,j,k,l);
            break;
        case 2:
            Tree.PreOrderTraverse(Tree.T);
            cout<<endl;
            break;
        case 3:
            Tree.InOrderTraverse(Tree.T);
            cout<<endl;
            break;
        case 4:
            Tree.PostOrderTraverse(Tree.T);
            cout<<endl;
            break;
        case 5:
            Tree.NOPreOrder();
            cout<<endl;
            break;
        case 6:
            Tree.NOInOrder();
            cout<<endl;
            break;
        case 7:
            Tree.NOPostOrder();
            cout<<endl;
            break;
        case 8:
            Tree.LevelOrderTraverse();
            cout<<endl;
            break;
        case 9:
            Tree.computelayer();
            Tree.display(Tree.T);
            break;
        case 10:
            cout<<"二叉树的深度是:"<<Tree.BiTreeDepth(Tree.T)<<endl;
            break;
        case 11:
            cout<<"二叉树的繁茂度是:"<<Tree.lush()<<endl;
            break;
        case 12:
            num=0;
            Tree.CountLeaf(Tree.T,num);
            cout<<"二叉树的叶子数是:"<<num<<endl;
            break;
        case 13:
            Tree.Exchange(Tree.T);
            break;
        case 14:
            Tree.JudgeTree();
            break;
        case 15:
            loop=false;
            break;
        }
    }
    return 0;
}

5.总结

  二叉树的题目,大多通过深搜和广搜来解决。能理解其中的思想才是关键。比如在深搜的时候,要理解什么时候往下走(递归开始进入左右子树时),什么时候往上走(递归左右子树完成后)。能动态的理解整个搜索的过程(对于二叉树图能画出遍历过程)对解题将会有很大的帮助。

时间: 2024-10-11 13:28:14

《数据结构》复习之二叉树的相关文章

数据结构复习之二叉树的非递归先序,中序,后序遍历

#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<stack> using namespace std; struct Tree{ int x; Tree *lchild, *rchild; Tree(){ lchild = rchild = NULL; } }; typedef Tree* pT; void buildT(pT &

SDUT 3343 数据结构实验之二叉树四:还原二叉树

数据结构实验之二叉树四:还原二叉树 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 给定一棵二叉树的先序遍历序列和中序遍历序列,要求计算该二叉树的高度. Input 输入数据有多组,每组数据第一行输入1个正整数N(1 <= N <= 50)为树中结点总数,随后2行先后给出先序和中序遍历序列,均是长度为N的不包含重复英文字母(区分大小写)的字符串. Output 输出一个整数,即该二叉树的

【数据结构】之二叉树的java实现

二叉树的定义: 二叉树是树形结构的一个重要类型.许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要. 二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的.分别称作这个根的左子树和右子树的二叉树组成. 这个定义是递归的.由于左.右子树也是二叉树, 因此子树也可为空树.下图中展现了五种不同基本形态的二叉树. 其中 (a) 为空树, (b

SDUT 3346 数据结构实验之二叉树七:叶子问题

数据结构实验之二叉树七:叶子问题 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 已知一个按先序输入的字符序列,如abd,,eg,,,cf,,,(其中,表示空结点).请建立该二叉树并按从上到下从左到右的顺序输出该二叉树的所有叶子结点. Input 输入数据有多行,每一行是一个长度小于50个字符的字符串. Output 按从上到下从左到右的顺序输出二叉树的叶子结点. Example Inpu

SDUT 3342 数据结构实验之二叉树三:统计叶子数

数据结构实验之二叉树三:统计叶子数 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 已知二叉树的一个按先序遍历输入的字符序列,如abc,,de,g,,f,,, (其中,表示空结点).请建立二叉树并求二叉树的叶子结点个数. Input 连续输入多组数据,每组数据输入一个长度小于50个字符的字符串. Output 输出二叉树的叶子结点个数. Example Input abc,,de,g,,f

SDUT 3340 数据结构实验之二叉树一:树的同构

数据结构实验之二叉树一:树的同构 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. Input 输入数据包含

javascript实现数据结构:线索二叉树

遍历二叉树是按一定的规则将树中的结点排列成一个线性序列,即是对非线性结构的线性化操作.如何找到遍历过程中动态得到的每个结点的直接前驱和直接后继(第一个和最后一个除外)?如何保存这些信息? 设一棵二叉树有n个结点,则有n-1条边(指针连线) , 而n个结点共有2n个指针域(Lchild和Rchild) ,显然有n+1个空闲指针域未用.则可以利用这些空闲的指针域来存放结点的直接前驱和直接后继信息. 对结点的指针域做如下规定: 1.若结点有左子树,则其leftChild域指示其左孩子,否则令leftC

数据结构快速回顾——二叉树 解幂子集问题

回溯法是设计递归的一种常用方法,它的求解过程实质上就是一个先序遍历一棵"状态树"的过程,只是这棵树不是遍历前预先建立的而是隐含在遍历过程中的. 下面举一个例子:求含n个元素的集的幂集:集合A={ {1,2,3}, {1,2}, {1,3}, {1}, {2,3},{2},{3},{}}; //{}表示空集合从集合A的每一个元素的角度看,它只有两种状态:或者是属于幂集的元素集,或不属于幂集元素集,则求幂集的过程就可以看成是依次对集合A中的元素进行"取","舍

数据结构快速回顾——二叉树

二叉树(Binary Tree)是个有限元素的集合,该集合或者为空.或者由一个称为根(root)的元素及两个不相交的.被分别称为左子树和右子树的二叉树组成.当集合为空时,称该二叉树为空二叉树.在二叉树中,一个元素也称作一个结点. 基本概念: (1)结点的度.结点所拥有的子树的个数称为该结点的度. (2)叶结点.度为0的结点称为叶结点,或者称为终端结点. (3)分枝结点.度不为0的结点称为分支结点,或者称为非终端结点.一棵树的结点除叶结点外,其余的都是分支结点. (4)左孩子.右孩子.双亲.树中一