练习(tree)

一、一棵高度为h的满m叉树有如下性质:根结点所在层次为第1层,第h层上的结点都是叶结点,其余各层上的每个结点都有m棵非空子树,若按层次自顶向下,同一层自左向右,顺序从1开始对全部结点进行编号。

(1)各层的结点个数为多少

答:第一层有m^=1个,第二层有m^1个结点,第三层有m^2个结点.....一般的,第i层有m^i个

(2)编号为i的结点的双亲结点(若存在)的编号为多少

答:在m叉树的情形下,结点i的第一个子女编号为j=(i-1)m+2,反过来结点i的双亲的编号为\left \lfloor (i-1)m+2) \right \rfloor+1,根结点没有双亲,所以 要求i>1

(3)编号为i的结点的第k个孩子结点(若存在)的编号是多少?

答:因为结点i的第1个子女编号为(i-1)m+2,若设该结点结点子女的序号为k=1,2...m,则第k个子女结点的的编号为(i-1)m+k+1(1<=k<=m)

(4)编号为i的结点有右兄弟的条件是什么?其右兄弟结点的编号是多少?

 

二、已知一棵二叉树按顺序存储结构进行存储,设计一个算法,求编号分别为i和j的两个结点的最近的公共祖先结点的值

答:二叉树任意两个结点必然存在最近的公共祖先结点,最坏的情况下是根结点(两个结点分别在根结点的左右分支中),而且最近的公共祖先结点到根结点的全部祖先结点都是公共的。由二叉树顺序存储可知,任一结点i的双亲结点的编号为i/2。求解i和j最近公共祖先ji结点的步骤为:

(1)若i>j,则结点i所在层次大于等于结点j所在层次,结点i的双亲结点为结点i/2,若i/2=j,则结点i/2是源节点i和结点j的最近公共 祖先结点,若i/2≠j,则令i=i/2,即以该结点i的双亲结点为起点,采用递归的方法继续查找

(2)若j>i,则结点j所在层次大于等于i所在层次。结点j的双亲结点为j/2,若j/2=i,则结点j/2是原节点i和结点j的最近公共祖先结点,若j/2≠i,则令j=j/2

重复上述过程,直到找到他们最近的公共祖先结点

ElemType Comm_Ancestor(SqTree T,int i,int j){
    if(T[i]!='#' && T[j]!='#'){//结点存在
        while(i!=j){  //两个编号不同时循环
            if(i>j)
                i=i/2;        //向上找i的祖先
            else
                j=j/2;        //向下找i的祖先
}
    return T[i];
}
}

三、综合

1.若某个非空二叉树的先序序列和后序序列正好相反,则该二叉树的形态是什么?

答:先序为根左右,后序为左右根。前序和后序相反。相反则,左根  和根左  ,根右和右根正好相反。

所以左子树或右子树都为空,满足先序序列和后序序列正好相反。每层只有一个结点。

2.若某非空二叉树的先序序列和后序序列正好相同,则该二叉树的形态是什么?

答:先序:根左右,后序:左右根。相同则有,该树的左右子树都为空,只有一个根结点

3.编写后序遍历二叉树的非递归算法

算法思想:后序非递归遍历二叉树的顺序,先访问左子树,再访问右子树,最后访问根结点。当用堆栈来存储结点时,必须分清返回根结点时是从左子树返回的还是右子树返回。所以,使用辅助指针r,其指向最近访问过的结点。也在结点中增加一个标志域,记录是否已被访问。

void  PostOrder(BiTree T){
    InitStack(S);
    p=T;
    r=NULL;
    while(p||!IsEmpty(S)){
        if(p){                    //走到最左边
            push(S,p);
            p=p->lchild;
            }                    //向右
    else{
        GetTop(S,p);    //取栈顶结点
        if(p->rchild && p->rchild!=r){  //若右子树存在,且未被访问过
            p==->rchild;        //转向右
            push(S,p);        //压入栈
            p=p->lchild;        //再走到最后
    }
    else{                    //否则,弹出结点并访问
        pop(S,p);            //将结点弹出
        visit(p->data);    //访问该结点
        r=p;                //记录最近访问过的结点
        p=NULL;            //访问完后,重置p指针
        }
    }//else
    }//while
}

访问一个结点*p时,栈中结点恰好是*p结点的所有祖先。从栈底到顶结点再加上*p结点,刚好构成从根结点到*p结点的一条路径。。

4.试给出二叉树的自下而上,从右到左的层次遍历算法

算法思想:利用常规的层次遍历算法,出队的同时将各结点指针入栈,在所有结点入栈再从栈顶开始依次访问即为所求算法

  • 把根结点入队
  • 把一个元素出队,遍历这个元素
  • 依次把这个元素的右孩子、左孩子入队
  • 若队列不空,则跳到第2步,否则结束
void InvertLevel(BiTree bt){
    Stack s;
    Queue Q;
    if(bt!=NULL){
        InitStack(s);    //栈初始化,栈中存放二叉树结点的指针
        InitQueue(Q);    //队列初始化,队列中存放二叉树结点的指针
        EnQueue(Q,bt);
        while(IsEmpty(Q)==false){    //从上而下层次遍历
            DeQueue(Q,p);
            Push(s,p);        //出队,入栈
            if(p->child)
                EnQueue(Q,p->lchild); //若左子树不空,则入队列
            if(p->child)
                EnQueue(Q,p->rchild);    //若右子树不空,则入队列
    }
    while(IdEmpty(s)==false){
        Pop(s,p);
        visit(p->data);
}                        //自下而上,从右到左的层次遍历
}//if结束
}

5.假设二叉树采用二叉链表存储结构,设计一个非递归算法求二叉树的高度

采用层次遍历的算法,设置变量level记录当前结点所在层数,设置变量last指向当前层的最右结点,每次层次遍历出队时与last指针比较,若两者相等,则层数加1,并让last指向下一层最右结点,直到遍历完成,level的值即为二叉树的高度。

int Btdepth(BiTree T){
    if(!T)
        return 0;      //树空高度为0
    int front=-1,rear=-1;
    int last=0,level=0;    //last指向下一层第一个结点的位置
    BiTree Q[MaxSize];    //设置队列Q,元素是二叉树结点指针且容量足够
    Q[++rear]=T;        //将根结点入队
    BiTree p;
    while(front<rear){        //队不空,则循环
        p=Q[++front];        //队列元素出队,即正在访问的结点
    if(p->lchild)
        Q[++rear]=p->lchild;    //左孩子入队
    if(p->rchild)
        Q[++rear]=p->rchild;    //右孩子入队
    if(front==last){            //处理该层的最右结点
        level++;                //层数增1
        last=rear;            //last指向下层
    }        
}
    return level;
}

6.设一棵二叉树中各结点的值互不相同,其先序遍历和中序遍历分别存于两个一维数组A[1,...n]和B[1,...n]中,试编写算法建立该二叉树的二叉链表

由先序序列和中序序列可以唯一确定一棵二叉树。算法步骤:

(1)根据先序序列确定树的根结点

(2)根据根结点在中序序列中划分出二叉树的左右子树包含哪些结点,然后根据左、右子树结点在先序序列中的次序确定子树的根结点,即返回步骤1

如此重复,直到每棵子树仅有一个结点(该子树的根结点)为止,如下图所示

 

BiTree PreInCreat(ElemType A[],ElemType B[],int l1,int h1,int l2,int h2){
//l1,h1为先序的第一个和最后一个结点下标,l2,h2为中序的第一个和最后一个结点下标
//初始调用时,l1=l2=1,h1=h2=n
    root=(BiTNode*)malloc(sizeof(BiTNode)); //建根结点
    root->data=A[l1];                        //根结点
    for(i=12;B[i]!=root->data;i++);            //根结点在中序序列中的划分
    llen=i-12;                            //左子树长度
    rlen=h2-i;                            //右子树长度
    if(llen)                                //递归建立左子树
        root->lchild=PreInCreat(A,B,l1+1,l1+llen,l2,l2+llen-1);
    else        //左子树为空
        root->lchild=NULL;
    if(rlen)            //递归建立右子树
        root->rchild=PreInCreat(A,B,h1-rlen+1,h1,h2-rlen+1,h2);
    else                //右子树为空
        root->rchild=NULL;
    return root;        //返回根结点指针
}

7.二叉树按二叉链表形式存储,写一个判别给定二叉树是否完全二叉树的算法

解:根据完全二叉树定义,具有n个结点的完全二叉树与满二叉树中编号1~n的结点一一对应。

采用层次遍历算法,将所有结点加入队列(包括空结点)。遇到空结点时,查看其后是否右非空结点。若有,则二叉树不是完全二叉树。

bool IsComplete(BiTree T){
    InitQueue(Q);
    if(!T)
        return 1;    //空树为满二叉树
    EnQueue(Q,T);
    while(!IsEpty(Q)){
        DeQueue(Q,p);
        if(p){        //结点非空,将其左、右子树入队
        DeQueue(Q,p->lchild);
        EnQueue(Q,p->rchild);
    }
    else              //结点为空,检查其后是否有非空结点
        while(!IsEmpty(Q)){
            DeQueue(Q,p);
            if(p)     //结点非空,则二叉树为非完全二叉树
            return 0;
        }
}
    return 1;
}

8.假设二叉树采用二叉链表存储结构存储,试设计一个算法,计算一棵给定二叉树的所有双分支结点个数。

解:计算一棵二叉树b中所有双分支结点个数的递归模型f(b)

f(b)=0                          //若b=NULL

f(b)=f(b->lchild)+f(b->rchild)  //若*b为双分支结点

f(b)=f(b->lchild)+f(b->rchild)   //其他情况(*b为单分支结点或叶子结点)

int DsonNodes(BiTree b){
    if(b==NULL)  return 0;
    else if(b->lchild !=NULL && b->rchild!=NULL)  //双分支结点
        return DsonNodes(b->lchild)+DsonNodes(b->rchild)+1;
    else
        return DsonNodes(b->lchild)+DsonNodes(b->rchild);
}

9.设树B是一棵采用链式结构存储的二叉树,编写一个把树B中所有结点的左、右子树进行交换的函数

解:采用递归算法实现交换二叉树的左、右子树,首先交换b结点的左孩子的左、右子树,,然后交换b结点 的右孩子的左、右子树,最后交换b结点的左、右孩子,当结点为空时递归结束(后序遍历的思想)

void swap(BiTree b){
    if(b){
        swap(b->lchild); //递归交换左子树
        swap(b->rchild); //递归交换右子树
        temp=b->lchild;  //交换左、右孩子结点
        b->lchild=b->rchild;
        b->rchild=temp;
}
}

10.假设二叉树采用二叉链表存储结构存储,设计一个算法,求先序遍历中第k(1<=k<=二叉树中结点个数)个结点的值

解:设置一个全局变量i记录已访问过的结点的序号,其初值是根结点在先序序列中的序号,即1。当二叉树b为空时返回特殊字符‘#’,

当i==k时,表示找到了满足条件的结点,返回b->data;当i≠k时,递归地在左子树中查找,若找到则返回该值,

否则继续递归地在右子树中查找,并返回其结果。

int i=1;            //遍历序号的全局变量
ElemType PreNode(BiTree b,int k){
    if(b==NULL)        //空结点,则返回特殊字符
        return '#';
    if(i==k)            //相等,则当前结点即为第k个结点
        return b->data;
    i++;                //下一个结点
    ch=PreNode(b->lchild,k);    //左子树递归寻找
    if(ch!='#')            //在左子树中,则返回该值
        return ch;
    ch=PreNode(b->rchild,k);    //在右子树中递归寻找
        return ch;
}

 

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值