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

来源:互联网 发布:flashfxp 软件下载 编辑:程序博客网 时间:2024/05/11 22:38

  • 二叉树的性质
    • 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.总结

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

1 0
原创粉丝点击