二叉树遍历变形_legend



二叉树遍历变形:

(0)二叉树的层次遍历分层打印

(1)输出二叉树中所有的叶子节点

(2)释放二叉树中的所有节点

(3)基于递归的二叉树算法设计

(4)递归法求二叉树中的所有叶子节点

(5)查找值为x的节点,找到返回其指针

(6)二叉树的 高度

(7)求先序遍历中的第k个节点的值

(8)二叉树的叶子节点个数

(9)二叉树中的单分支节点个数

(10)双分支节点个数

(11)二叉树中值为k的节点个数

(12)判断一棵二叉树是否为满二叉树

(13)把二叉树b复制到二叉树t中

(14)把二叉树b交换左右子树后存到二叉树t中

(15)交换左右子树

(16)判断两棵树是否相似

(17)将二叉树的顺序存储结构转换为链式存储结构

(18)将二叉树的链式结构转换为顺序结构

(19)求二叉树中节点值为x的节点的层数

(20)求二叉树中距离p最近的叶子节点

(21)求二叉树的宽度(即节点个数最多的一层的节点个数)

(22)二叉树中指定某一层的叶子节点个数

(23)输出从每一个叶子节点到根节点的逆路径:(即从根节点到叶子节点的路径)

(24)输出二叉树中最长的一条路径

(25)输出值为x的节点的所有祖先

(26)输出从根节点到节点值为x的节点的路径

(27)求节点值为x和节点值为y的两个节点的最近共同祖先

(28)

-----------------------------------------

(0)二叉树的层次遍历分层打印

给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。

比如:

输出:

1
2 3
4 5 6
7 8

原始实现:

void PrintBFS(Node* root) {
    queue<Node*> Q;
    Q.push(root);
    do {
        Node *node = Q.front();
        Q.pop();
        cout << node->data << " ";
        if (node->pLeft)
            Q.push(node->pLeft);
        if (node->pRight)
            Q.push(node->pRight);
    }
    while (!Q.empty());
}

说明:难点在于如何换行,即如何每行输出;

 

(0.1)方法一:

利用了两个队列,队列A储存本层的节点,队列B储存下层的节点。

元素始终从队列A中出队,遍历本层的节点,把其子代节点排入队列B。

本层遍历完毕后,就可换行,并交换两个队列。

ps:队列A/B中只会存储一个行中的元素。一个队列中不会同一时间存在2个行中的元素。所以队列A出队完,即可打换行;

void PrintNodeByLevel(Node* root) {
    deque<Node*> Q1, Q2;
    Q1.push_back(root);  //根节点进入队列1;
    do {
        do {

            /*

                    一个队列Q1 会记录一层中的所有节点,不会有多个层中的节点同时出现在一个队列中; 

                   所以,将Q1中所有的元素出队列,并访问,这样一层就访问完了,同时将该层的下一层的元素存入到队列Q2中;

           */
            Node* node = Q1.front();  // 元素始终从队列1中出队;
            Q1.pop_front();
            cout << node->data << " ";
            if (node->pLeft)
                Q2.push_back(node->pLeft);
            if (node->pRight)
                Q2.push_back(node->pRight);
        } while (!Q1.empty());
        cout << endl;  // Q1队列为空是换行;
        Q1.swap(Q2);  /*此中的swap操作其实是将队列2所有元素出队,加入到队列1中*/
    } while(!Q1.empty());
}

 

(0.2)方法二:

把一个结束标识放进队列里。

当发现空指针(结束信号)时,要检查队列内是否还有节点,如果没有的话还插入新的结束信号,则会做成死循环。

void PrintNodeByLevel(Node* root) {
    queue<Node*> Q;
    Q.push(root); //根节点进队列;
    Q.push(0); //第一层的结束标识;对于每一行来说,该行节点在前,结束标识在后。
    do {
        Node* node = Q.front();
        Q.pop();
        if (node) { //节点非空,说明是正常的节点;
            cout << node->data << " ";
            if (node->pLeft)
                Q.push(node->pLeft);
            if (node->pRight)
                Q.push(node->pRight);
        }
        else if (!Q.empty()) {

            /*遇到上一个行的结束标识,说明本行的节点都已经入队了。所以需要在本行加结束标识;*/
            Q.push(0);
            cout << endl;
        }
    } while (!Q.empty());
}

-------------
(1)输出二叉树中所有的叶子节点:

void printLeaves(BTNode* b){
 if(b){

   if(b->lchild==NULL && b->rchild==NULL){
   visit(b->data);
   }

   printLeaves(b->lchild);
   printLeaves(b->rchild);

 }

}

注:基于先序遍历

-----
(2)释放二叉树中的所有节点:
void deleteBT(BTNode* b){
 if(b){
  deleteBT(b->lchild);
  deleteBT(b->rchild);
  free(b);
 }
}
注:基于后序遍历

------

(3)基于递归的二叉树算法设计:

二叉树是递归的数据结构,所以一棵非空的二叉树的 左右子树也是二叉树。
1)设原问题为f(b),较小问题为f(b->lchild)和f(b->rchild);
2)假设f(b->lchild),f(b->rchild)是可求的,由此导出f(b);
3)b为空或只有一个节点(叶子节点)时,递归结束。

-------

(4)递归法求二叉树中的所有叶子节点:

1)递归模型如下:
当b=NULL,    f(b)=不做任何事;
当b=叶子节点, f(b)=输出b->data的值;
其他情况 ,    f(b)=f(b->lchild);f(b->rchild);

void printLeaves(BTNode* b){
 
 if(b){
  if(b->lchild==NULL && b->rchild==NULL)
  visit(b->data);

  else{
  printLeaves(b->lchild);
  printLeaves(b->rchild);
  }

 }
}

--------

(5)查找值为x的节点,找到返回其指针,否则NULL;
1)递归模型:
当b=NULL;  f(b,x)=NULL;
当b->data=x;  f(b,x)=b;
其他情况;  f(b,x)=f(b->lchild,x)或者f(b->rchild,x);

BTNode* findNode(BTNode* b, DataType x){
 BTNode * pnode;
 if(b==NULL) return NULL;
 else if(b->data==x) return b;

 else{
 pnode=findNode(b->lchild,x);
  if(pnode) return pnode;
  else return findNode(b->rchild,x);

 }

}

注:基于先序。

-------

(6)二叉树的 高度:

1)递归模型:
当b=NULL;  f(b)=0;
当b=叶子;  f(b)=1;
其他情况;  f(b)=Max( f(b->lchild), f(b->rchild) )+1;

其实:b=叶子这种情况可以省略,已经包含在其他情况中。

int BTDepth(BTNodex* b){

 int lchildDepth,rchildDepth;

 if(b==NULL)
 return 0;

 else {
 lchildDepth=BTDepth(b->lchild);
 rchildDepth=BTDepth(b->rchild);
 return (lchildDepth>rchildDepth)?(lchildDepth+1):(rchildDepth+1);
 }

}

注:基于后序

--------

(7)求先序遍历中的第k个节点的值:
1)分析:用一个全局变量n记录访问过的节点个数,当二叉树为空时返回空格,当
n=k时,返回b->data,当n=k时,在左子树查找,若找到则返回该值,否则在右子树查找。
2)递归模型:
当b=NULL;  f(b,k)=' ';返回空格
当n==k时;       f(b,k)=b->data;
当n!=k时;      f(b,k)=f(b->lchild,k)或者f(f->rchild,k);
3)代码:
 int n=1;//初始时n=1,对应第一个节点,根节点。
 DataType preNode_k(BTNode* b, int k){
  if(b==NULL) return ' ';
  if(n==k) return (b->data);

  n++;
  DataType tempData;
  tempData=preNode_k(b->lchild,k);
  if(tempData!=' ') return tempData;
  else return preNode_k(b->rchild,k);

 }

 注:基于先序。

---------

(8)二叉树的叶子节点个数:
1)递归模型:
若b=NULL;  f(b)=0;
若b为叶子;  f(b)=1;
其他情况;  f(b)=f(b->lchild)+f(b->rchild);
2)代码:
int leavfNodes(BTNode* b){
 int num1,num2;
 if(b==NULL) return 0;
 else if(b->lchild==NULL && b->rchild==NULL) return 1;
 else {
  num1=leavfNodes(b->lchild);
  num2=leavfNodes(b->rchild);
      return (num1+num2);
 }
}
注:基于后序。

-----------

(9)二叉树中的单分支节点个数:
1)递归模型:
当b=NULL;  f(b)=0;
当b为单分支节点; f(b)=f(b->lchild)+f(b->rchild)+1;
当b为叶子或双分支;f(b)=f(b->lchild)+f(b->rchild);

int simpleSonNode(BTNode* b){
 if(b==NULL) return 0;

 else if( (b->lchild==NULL && b->rchild!=NULL) || (b->lchild!=NULL && b->rchild==NULL) )
 //b为单分支
 return simpleSonNode(b->lchild)+simpleSonNode(b->rchild)+1;

 else return simpleSonNode(b->lchild)+simpleSonNode(b->rchild);
}

注:基于后序。

-----------

(10)双分支节点个数:
1)递归模型:
当b=NULL;   f(b)=0;
当b为双分支;      f(b)=f(b->lchild)+f(b->rchild)+1;
其他(b为叶子或单分支) f(b)=f(b->lchild)+f(b->rchild);

int doubleSonNode(BTNode * b){
 
 if (b==NULL) return 0;
 else if(b->lchild && b->rchild)
  return doubleSonNode(b->lchild)+doubleSonNode(b->rchild)+1;

  else  return doubleSonNode(b->lchild)+doubleSonNode(b->rchild);
}

注:基于后序;

-------------

(11)二叉树中值为k的节点个数:
1)递归模型:
当b=NULL;  f(b,k)=0;
当b->data=k;  f(b,k)=1+f(b->lchild)+f(b->rchild);
当b->data!=k; f(b,k)=f(b->lchild)+f(b->rchild);

int count_k(BTNode* b, DataType k){
 if(b==NULL) return 0;

 else if(b->data==k)
 return 1+count_k(b->lchild,k)+count_k(b->rchild,k);

 else
 return count_k(b->lchild,k)+count_k(b->rchild,k);

}

注:基于先序;

--------------

(12)判断一棵二叉树是否为满二叉树;
1)分析:满二叉树满足n=2^h-1,h为高度。

2)代码:

int height(BTNode* b){
 int lchildDepth,rchildDepth;
 if(b==NULL) return 0;
 else {
 lchildDepth=height(b->lchild);
 rchildDepth=height(b->rchild);
 return (lchildDepth>rchildDepth)?(lchildDepth+1):(rchildDepth+1);
 }
}

int nodeCount(BTNode* b){
 if(b==NULL) return 0;
 else
 return 1+nodeCount(b->lchild)+nodeCount(b->rchild);
}

int isFullBT(BTNode* b){
 int count;
 int h;
 count=nodeCount(b);
 h=height(b);
 if( count==pow(2,n)-1 )
 return 1;
 else return 0;
}

----------

(13)把二叉树b复制到二叉树t中;
1)递归模型:
当b=NULL;  f(b,t)中; t=NULL;
其他情况;  f(b,t)中; 复制根节点b产生t节点,
    f(b->lchild,t->lchild),
    f(b->rchild,t->rchild);

2)代码实现:
void copy(BTNode* b ,BTNode* t){
 if(b==NULL)  t=NULL;
 else {

  t=(BTNode* )malloc(sizeof(BTNode));//复制根节点t
  t->data=b->data;
  copy(b—>lchild,t->lchild);
  copy(b->rchild,t->rchild);
 }

}
注:基于先序。

-------------

(14)把二叉树b交换左右子树后存到二叉树t中:
1)递归模型:
当b=NULL;  f(b,t)中;t=NULL;
其他情况;  f(b,t)中; 复制根节点b产生t节点,
    f(b->lchild,t->rchild),
    f(b->rchild,t->lchild);

2)代码实现:
 
 void swap(BTNode* b, BTNode* t){
  if(b==NULL) t=NULL;
  else {
  t=(BTNode* )malloc(sizeof(BTNode));
  t->data=b->data;

  swap(b->lchild,t->rchild);
  swap(b->rchild,t->lchild);
  }
 }

注:基于先序;

-------------

(15)交换左右子树;

1)递归模型:
当b=NULL;  f(b)中不做任何事;
其他情况;  f(b)中:
    f(b->lchild);
    f(b->rchild);
    将b的左右孩子互换;
2)代码实现:

void swap(BTNode* b){
 BTNode* pnode;
 if(b){
 swap(b->lchild);//左子树交换
 swap(b->rchild);//右子树交换
 pnode=b->lchild;//保存左指针
 b->lchild=b->rchild;//交换b的左右指针
 b->rchild=p;
 }
}
注:基友后序。

-------------

(16)判断两棵树是否相似;
1)分析:
t1,t2相似,指的是t1,t2都是空的树;或者t1,t2的根节点相似,且t1的左子树,右子树分别与t2的左子树,右子树相似。
2)递归模型:
t1,t2均为NULL时;    f(t1,t2)=true;
t1,t2一个为NULL,另一个不是 f(t1,t2)=false;
其他情况                     f(t1,t2)=f(t1->lchild,t2->lchild)&&f(t1->rchild,t2->rchild);

3)代码实现:

int isLike(BTNode* t1,BTNode* t2){
 if(t1==NULL && t2==NULL) return 1;
 else if( (t1==NULL && t2!=NULL) ||(t1!=NULL && t2==NULL) )
 return 0;
 else {
  int like1,like2; 
  like1=isLike(t1->lchild,t2->lchild);
  like2=isLike(t1->rchild,t2->rchild);
  return (like2&&like1);
 }
}
注:基于基于后序。

------------

(17)将二叉树的顺序存储结构转换为链式存储结构:
1)分析:
二叉树的顺序存储结构为BTNode* seTree[Maxsize],
f(seTree,i)来得到由seTree[i]创建的节点指针,显然f(seTree,0)得到的是根节点指针,
2)递归模型:
当i>=MaxSize;    f(seTree,i)=NULL;
当seTree[i]='#'即i号为空   f(seTree,i)=NULL;
其他情况;                    f(seTree,i)=b,创建根节点b(值为seTree[i])
       b->lchild=f(seTree,2*i+1);
       b->rchild=f(seTree,2*i+2);

3)代码:
BTNode* seTree_to_linkTree(BTNode* seTree,int i){
 BTNode* b;
 if(i>=Maxsize) return NULL;
 else if(seTree[i]=='#')//i号节点为空时
 return NULL;

 else {
 b=(BTNode*) malloc(sizeof(BTNode));
 b->data=seTree[i];

 b->lchild=seTree_to_linkTree(seTree,2*i+1);
 b->rchlld=seTree_to_linkTree(seTree,2*i+2);
 return b;
 }
}

或者:
 void seTree_to_linkTree(BTNode* &b ,BTNode* seTree,int i ){
  if(i>=Maxsize) b=NULL;
  else if(seTree[i]='#')
  b=NULL;

  b=(BTNode*)malloc(sizeof(BTNode));//创建根节点
  b->data=seTree[i];
  seTree_to_linkTree(b->lchild,seTree,2*i+1);//创建左子树
  seTree_to_linkTree(b->rchild,seTree,2*i+2);//创建右子树
 }
 注:基于先序。

---------------

 (18)将二叉树的链式结构转换为顺序结构:
 1)分析:二叉树的顺序存储结构a,(初始时所有元素的值为‘#’)
 2)递归结构:
 当b=NULL;              f(a,b,i)不做任何事;
 其他情况;               f(a,b,i)中 a[i]=b->data;
       f(a,b->lchild,2*i+1);
       f(a,b->rchild,2*i+2);
3)代码实现:

void linkTree_to_SeTree(SqTree a ,BTNode* b,int i){
 if(b){
 a[i]=b->data;
 linkTree_to_seTree(a,b->lchild,2*i+1);
 linkTree_to_seTree(a,b->rchild,2*i+2);
 }
}

注:基于先序、

------------

(19)求二叉树中节点值为x的节点的层数:
1)分析:
设f(b,x,h)等于data值为x的节点的层次,当找不到data值为x的节点时返回0;其中,h表示b所指节点的层次,初始时调用方式为f(b,x,1),即从根节点开始查找。
2)递归模型:
当b=NULL;                f(b,x,h)=0;
当b->data==x;             f(b,x,h)=h;
其他情况;                 f(b,x,h)=f(b->lchild,x,h+1)或f(b->rchild,x,h+1);
3)代码实现:
int level(BTNode* b, DataType x ,int h){
 //h表示b所在层次。
 if(b==NULL){
 return 0;
 }
 else if(b->data==x){
 return h;
 }
 else {
  int level_left=level(b->lchild,x,h+1);
  //b所在层次为h,所以b的左孩子所在的层次为h+1;
  if(level_left)
  return level_left;
  else return level(b->rchild,x,h+1);
 }
}

注:基于先序;
或者:
1)分析:
基于层次遍历,在队列中设置一个数组来保存进队节点所在的层次,在出队的 时候若当前节点
的data为x,就返回对应的层次,否则将不空的左孩子和不空的右孩子入队列。
2)代码实现:
int level(BTNode* b, DataType x){
 
 struct {
 int front,rear;
 BTNode* dataArray[Maxsize];
 int level[Maxsize];//保存各个节点的层次
 }Qu;//定义结构体变量Qu;

 Qu.front=Qu.rear=0;//队头队尾初始化
 Qu.rear=(Qu.rear+1)%Maxsize;//根节点入队。
 Qu.dataArry[Qu.rear]=b;
 Qu.level[Qu.rear]=1;//根节点对应的层次为1;

 while(Qu.rear!=Qu.front){
  Qu.front=(Qu.front+1)%Maxsize;//出队列
  BTNode* p=Qu.dataArray[Qu.front];
  int h=Qu.level[Qu.front];

  if(p->data==x)//关键码
  return h;

  if(p->lchild!=NULL){//p的左孩子进队
   Qu.rear=(Qu.rear+1)%Maxsize;
   Qu.dataArray[Qu.rear]=p->lchild;
   Qu.level[Qu.rear]=h+1;
  }

  if(p->rchild!=NULL){//p的右孩子进队
   Qu.rear=(Qu.rear+1)%Maxsize;
   Qu.dataArray[Qu.rear]=p->rchild;
   Qu.level[Qu.rear]=h+1;
  }

 }
return 0;      //如果没有找到,则返回0
}

----------

(20)求二叉树中距离p最近的叶子节点;
1)分析:
采用层次遍历,从p节点开始遇到的第一个叶子节点就是所求。
2)代码如下:
DataType findLeave(BTNode* b , BTNode* p){
 
 BTNode* Qu[Maxsize];
 BTNode* pnode;
 int front=0;
 int rear=0;

 rear=(rear+1)%Maxsize;//p节点进队
 Qu[rear]=p;

 while(front!=rear){
  front=(front+1)%Maxsize;
  pnode=Qu[front];

  if(pnode->lchild==NULL && pnode->rchild==NULL){
   return pnode->data;
  }

  if(NULL!=pnode->lchild){
  rear=(rear+1)%Maxsize;
  Qu[rear]=pnode->lchild;
  }

  if(NULL!=pnode->rchild){
   rear=(rear+1)%Maxsize;
   Qu[rear]=pnode->rchild; 
  }

 }

}

-------

(21)求二叉树的宽度(即节点个数最多的一层的节点个数);
1)分析:
采用层次遍历,将各个节点和对应的层次放到一个队列中,通过扫描二叉树得到各个节点的层次。
2)代码实现:
int BTWidth(BTNode* b){

 struct{
  BTNode* pnodeArray[Maxsize];
  int nodeLevel[Maxsize];
  int front;
  int rear;
 }Qu;//定义结构体变量Qu;

 Qu.front=Qu.rear=-1;//初始化front,rear;
 Qu.rear++;
 Qu.pnodeArray[Qu.rear]=b;
 Qu.nodeLevel[Qu.rear]=1;
 //根节点对应的下标是0,对应的层次是1;

 BTNode* pnode;
 int node_level;
 while(Qu.rear!=Qu.front){
  Qu.front++;//出队
  pnode=Qu.pnodeArray[Qu.front];
  node_level=Qu.nodeLevel[Qu.front];

  if(NULL!=pnode->lchild){//左孩子进队
   Qu.rear++;
   Qu.pnodeArray[Qu.rear]=pnode->lchild;
   Qu.nodeLevel[Qu.rear]=node_level+1;
  }

  if(NULL!=pnode->rchild){//右孩子进队
   Qu.rear++;
   Qu.pnodeArray[Qu.rear]=pnode->rchild;
   Qu.nodeLevel[Qu.rear]=node_level+1;
  }

 }

 printf("输出各个节点的层次\n");
 for(int i=0;i<=Qu.rear;i++){
 //根节点对应的是rear=0;所以rear+1为节点个数
 printf("%c, %d \n",Qu.pnodeArray[i].data, Qu.nodeLevel[i]);
 }

 int MaxWidth=0;
 int tempWidth;
 int tempLevel=1;//从根节点开始,根节点的层次为1;
 int i=0;//根节点对应的下标为0
  while(i<=Qu.rear){
   tempWidth=0;

    while(i<=Qu.rear && Qu.nodeLevel[i]==tempLevel){
    tempWidth++;
    i++;
    }

    tempLevel=Qu.nodeLevel[i];
    if(tempWidth>MaxWidth)
    MaxWidth=tempWidth;

  }

return MaxWidth;
}
注:队列中的节点,出队后仍然需要保留在队列中以便后来求二叉树的宽度,所以设置的 队列不是循环队列,如果用循环队列可能被其他的节点覆盖。

-------
(22)二叉树中指定某一层的叶子节点个数:
1)分析:
基于层次遍历,若某一层的节点如队后,rear指向该层中最右节点,然后用last来保存最右节点(对于第一层的最右节点即根节点,则last=1)。出队时,若front==last,表示这一层处理完毕,让层次level++;并设置last为下一层的最右节点;
因为第一层只有一个节点,所以根节点就是最右节点。对于其他层,上一层最右节点最后入队的孩子就是最右节点。
2)代码实现:
int leaves_Of_LevelK(BTNode* b,int k){
 BTNode* Qu[Maxsize];
 int front,rear;

 int level;//记录层次;
 int last;//逐层记录最右节点的编号。即最右节点是第几个节点,
 int leaveCount;//叶子个数

 front=rear=0;//初始化
 if(b==NULL || k<=1) return 0;
 rear++;     //根节点入队
 Qu[rear]=b;
 last=rear;  //初始时最右节点就是根节点,即第一个节点,编号为1.
 level=1; //根节点对应的层次为1.
 leaveCount=0;

 BTNode* pnode;
 while(rear!=front){

  front=(front+1)%Maxsize;//出队
  pnode=Qu[front];

  if(level==k && pnode->lchild==NULL && pnode->rchild==NULL)
  leaveCount++;

  if(pnode->lchild){//左孩子进队
  rear=(rear+1)%Maxsize;
  Qu[rear]=pnode->lchild;
  }

  if(pnode->rchild){//右孩子进队
  rear=(rear+1)%Maxsize;
  Qu[rear]=pnode->rchild;
  }

  if(front==last){//当前层处理完毕
   level++;
   last=rear;
  }

  if(level>k)
  return leaveCount;
 }
}

 

2)方法二:

使用题目21中的方法,求出每一个节点的层次,然后遍历队列,统计层次为 i 的叶子节点的个数即可。

--------

(23)输出从每一个叶子节点到根节点的逆路径:(即从根节点到叶子节点的路径)

1)分析:采用层次遍历的方法。队列为非环形顺序队列。
队列中的每个元素为节点指针,以及当前节点的父节点的索引的结构体。
2)代码分析:

void allPath(BTNode* b){
 
 struct snode{
  BTNode* node;
  int parentIndex;//保存父节点的索引
 } Qu[Maxsize];

 int front,rear;
 int tempParentIndex;

 front=rear=-1;//初始化
 rear++;//根节点入队
 Qu[rear].node=b;
 Qu[rear].parentIndex=-1;

 BTNode* pnode;
 while(front<rear){
  front++;//出队
  pnode=Qu[front].node;

  if(pnode->lchild==NULL && pnode->rchild==NULL){

   tempParentIndex=front;
   while(Qu[tempParentIndex].parentIndex!=-1){
   //tempParentIndex不是根节点则循环进行。

   visit(Qu[tempParentIndex].node->data);
   tempParentIndex=Qu[tempParentIndex].parentIndex;
   }

   visit(Qu[tempParentIndex].node->data);
  }

  if(pnode->lchild){//左孩子进队
  rear++;
  Qu[rear].node=pnode->lchild;
  Qu[rear].parentIndex=front;//因为pnode对应的编号为front
  }

  if(pnode->rchild){//右孩子进队
  rear++;
  Qu[rear].node=pnode->rchild;
  Qu[rear].parentIndex=front;//因为pnode对应的编号为front
  }
 }

}

或者:
1)采用path数组存放路径,pathlen为数组长度。
2)递归模型:
当b为叶子节点时;    f(b,path,pathlen)中输出path的值;
其他情况;     f(b,path,pathlen)中,
       将b->data的值放入到path中,pathlen++;
       f(b->lchild,path,pathlen);
       f(b->rchild,path,pathlen);
3)代码实现:
/*path存放的都是叶子节点的祖先节点,并没有存放叶子节点*/
void allPath(BTNode* b , DataType []path ,int pathlen){
 if(b){

  if(b->lchild==NULL && b->rchild==NULL){
   visit(b->data);//访问叶子节点。
   for(int i=pathlen-1;i>=0;i--){
   visit(path[i]);
   }
  }

  else{
   path[pathlen]=b->data;//当前节点放入路径中
   pathlen++;

   allPath(b->lchild,path,pathlen);//扫描左子树
   allPath(b->rchild,path,pathlen);//扫描右子树
   pathlen--;  //恢复环境?????

  }

 }

}
---------
(24)输出二叉树中最长的一条路径;
1)分析:
最长的路径是从根节点到某一个叶子节点的路径;
path保存当前的路径,pathlen为路径的长度;
longpath保存最长的路径,longpathlen保存其长度。
当b==NULL,表示当前的扫描已经结束,将pathlen与longpathlen比较。
2)代码实现:

void longPath(BTNode* b , DataType [] path ,int pathlen ,DataType longPath, int longpathlen){
 
 int i;
 if(b==NULL){ //当前路径更长,保存在longpathlen中。 
  if(pathlen>longpathlen){
   for(i=pathlen-1;i>=0;i--)
   longpath[i]=path[i];
   longpathlen=pathlen;
  }
 }

 else {
   path[pathlen]=b->data;
   pathlen++;
   longPath(b->lchild,path,pathlen,longpath,longpathlen);
   longPath(b->rchild,path,pathlen,longpath,longpathlen);
   pathlen--;
 }
}

--------

(25)输出值为x的节点的所有祖先;
1)分析:
f(b,x)表示b节点是否是值为x的节点的祖先,若是则f(b,x)=1;
否则,f(b,x)=0;
2)递归模型:
当b=NULL;                 f(b,x)=0;
当b->data=x;         f(b,x)=1;
当f(b->lchild,x)=1或f(b->rchild,x)=1; f(b,x)=1,并输出b->data;
3)代码实现:

int allAncestor(BTNode* b ,DataType x){
 
 if(b==NULL) return 0;
 else if(b->data==x)  return 1;

 if( allAncestor(b->lchild,x) || allAncestor(b->rchild,x) ){
  visit(b->data); 
  return 1;
 }
}

注:基于后序。

------
(26)输出从根节点到节点值为x的节点的路径;
1)方法一:同上
2)方法二:采用非递归后序遍历,当后序遍历到值为x的节点时,此时栈中的所有节点均是该节点的祖先节点,这些祖先节点变构成了一条从跟节点到该节点的路径。

void ancestorPath(BTNode * b , DataType x){
 
 BTNode* stack[Maxsize];
 int top=-1;
 int flag;
 BTNode* pnode=b;
 BTNode* preVisited;

 if(b){
  do{

   while(pnode){//将pnode的所有左节点进栈。
    top++;
    stack[top]=pnode;
    pnode=pnode->lchild;
   }

   preVisited=NULL;//preVisited为栈顶节点的前一个已经访问过的节点。
   flag=1;

   while(top!=-1 && flag){
    pnode=stack[top];//取栈顶元素
    if(pnode->rchild==preVisited){

    //右孩子不存在或已经访问过,则访问之
     if(pnode->data==x){
     //找到值为x的节点,则栈中都是祖先节点。
     for(int i=0;i<=top;i++)
     visit(stack[i]->data);    
     }
     top--;
     preVisited=pnode;
     
    }

    else{
    pnode=pnode->rchild;
    flag=0;//flag=0表示栈顶节点处理完毕。
    }

   }

  }while(top!=-1);//栈非空
 }
}

------

(27)求节点值为x和节点值为y的两个节点的最近共同祖先;
或求包含x和y两个节点的最小子树。

1)分析:
采用非递归后序遍历,当找到节点值为x的节点时,将栈中的所有的节点值放在ancestor_x数组中,(找到x时,栈中的 节点均是x的祖先);
当找到值为y的节点时,将栈中的所有的节点值放在ancesotr_y数组中;
当两个节点均找到后,通过比较找出它们的公共祖先。

2)代码实现:
int commonAncestor(BTNode* b ,DataType x, DataType y){
 DataType ancestor_x[Maxsize],ancestor_y[Maxsize];
 BTNode* stack[Maxsize];
 BTNode* pnode=b;
 BTNode* preVisited;//当前栈顶节点的前一个被访问的 节点
 int flag,top=-1;
 bool findx=false, findy=false;

 if(NULL!=b){
  do{
   while(pnode!=NULL){//将pnode的所有左孩子放到栈中。
    top++;
    stack[top]=pnode;
    pnode=pnode->lchild;
   }

   flag=1;
   preVisited=NULL;

   while(top!=-1 && 1==flag){
    pnode=stack[top];//访问栈头

    if(pnode->rchild==preVisited){
    //右孩子不存在或右孩子已经访问过
     if(pnode->data==x){
      for(int i=0;i<=top;i++)
      ancestor_x[i]=stack[i]->data;
      findx=true;
     }
     else if(pnode->data==y){
      for(int i=0;i<=top;i++)
      ancestor_y[i]=stack[i]->data;
      findy=true;
     }

     if(finx && findy){
      int i=0;
      while(ancestor_x[i]==ancestor_y[i])
      i++;
      visit(ancestor_x[i-1]);//公共最近祖先
      return 1;
     }

     top--;
     preVisited=pnode;
    }

    else{
     pnode=pnode->rchild;
     flag=0;//退出当前循环。
    }

   }

  }while(top!=-1);
 }

return 0;//没有找到共同祖先。
}

--------

(28)算术表达式以中序二叉树形式存储,求表达式的值;
1)分析:根节点用于存储运算符,若能分别求出左子树和右子树的表达式的值,最后就可以计算出结果。
2)代码实现:

float expressionValue(BTNode* b){
 float lvalue,rvalue,finalvalue;

 if(b!=NULL){
  if(b->data!='+' && b->data!='-' && b->data!='*' && b->data!='/')
   return (b->data-'0');
  else {
   lvalue=expressionValue(b->lchild);
   rvalue=expressionValue(b->rchild);
   switch(b->data){
    case '+':  finalvalue=lvalue+rvalue;break;
    case '-': finalvalue=lvalue1rvalue;break;
    case '*': finalvalue=lvalue*rvalue;break;
    case '/':   if(rvalue!=0)
       finalvalue=lvalue/rvalue;
       else exit(0);break;
   }

  }

 }

 return finalvalue;
}

--------

(29)中序输出上题中的表达式,并加上括号;
1)分析:
中序遍历二叉树,当根节点和左孩子(或右孩子)都是运算符时,比较他们的优先级,当根节点的优先级高时,需要加上括号。

2)代码实现:
/*
比较运算符的优先级
op1>op2 返回1;
op1=op2 返回0;
op1<op2 返回-1;
*/
int precedency(char op1, char op2){
 if(op1!='+' && op1!='-' && op1!='*' && op1!='/')
 return -1;//数据的优先级是相同的
 if(op2!='+' && op2!='-' && op2!='*' && op2!='/')
 return -1;
 switch(op1){
  case '+':
  case '-': if('+'== op2 || '-'==op2)
     return 0;
     else return -1; ;break;
  case '*':
  case '/': if('+'== op2 || '-'==op2)
     return 1;
     else return 0; ;break;
 }
}

void inorderExpre(BTNode* b){
 int precede;//记录优先级
 if(b!=NULL){
  if(b->lchild){
   precede=precedency(b->data,b->lchild->data);
   if(1==precede)

   printf("(");//给左孩子加括号。

   inorderExpre(b->lchild);
   if(1==precede)

   printf(")");
  }

  visit(b->data);//输出根节点。

  if(b->rchild){

   precede=precedency(b->data,b->rchild->data);
   if(1==precede)

   printf("(");//给左孩子加括号。

   inorderExpre(b->rchild);
   if(1==precede)

   printf(")");

  }


 } 

}

--------

(30)将上题中的表达式按照后序百丽得到后序表达式,然后求值。
1)分析:
1.先后序遍历得到后缀表达式;
2.后缀表达式求值:从左到右扫描后缀表达式,遇到数字就进栈,遇到操作符就出栈两个操作数,进行相应的运算,然后把结果入栈,知道后缀表达式结束,栈中只有一个元素,就是最终的结果。
2)代码实现:
 char postExp[Maxsize];
 int n=0;

 void postExpre(BTNode* b){
  if(b){
   postExpre(b->lchild);
   postExpre(b->rchild);
   postExp[n++]=b->data;
  }
 }

 float computeValue(){
  float stack[Maxsize];
  int top=-1;
  float oper1,oper2,oper;//操作数1, 操作数2,运算结果。
  char ch;//ch保存后缀表达式的一个字符。
  int i=0;
  while(i<n){//扫描后缀表达式
   ch=postExp[i++];
   switch(ch){
    case '+':
    oper1=stack[top];top--;//操作数1出栈
    oper2=stack[top];top--;//操作数2出栈
    oper=oper1+oper2;
    top++;//操作结果进栈
    stack[top]=oper;
    break;

    case '-':
    oper1=stack[top];top--;//操作数1出栈
    oper2=stack[top];top--;//操作数2出栈
    oper=oper2-oper1;
    top++;//操作结果进栈
    stack[top]=oper;
    break;

    case '*':
    oper1=stack[top];top--;//操作数1出栈
    oper2=stack[top];top--;//操作数2出栈
    oper=oper2*oper1;
    top++;//操作结果进栈
    stack[top]=oper;
    break;

    case '/':
    oper1=stack[top];top--;//操作数1出栈
    oper2=stack[top];top--;//操作数2出栈
    if (0==oper1)
    exit(0);

    oper=oper2/oper1;
    top++;//操作结果进栈
    stack[top]=oper;
    break;

    default: //操作数进栈
    top++;
    stack[top]=ch-'0';break;

   }

  }
  return stack[0];
 }

 --------
 (31)给二叉树的存储结构中的每一个节点加上一个parent指针,设计一个算法,给这个指针赋值,并输出所有节点到根节点的 逆路径。

 1)分析:采用先序递归。
 /*
根节点的父节点为NULL,所以调动时,传递给b为根节点,p的值为NULL
 */
 void  TriBTree(BTNode* b , BTNode* p){
  if(b){
   b->parent=p;
   TriBTree(b->lchild,b);
   TriBTree(b->rchild,b);
  }
 }

 void allPath(BTNode* b){
  BTNode *pnode;
  if(b){
   pnode=b;
   printf("当前节点%c到根节点 的逆路径\n",pnode->data);

   while(pnode){//输出当前节点pnode到根节点的路径
   visit(pnode->data);
   pnode=pnode->parent;
   }
   allPath(b->lchild);//扫描左子树
   allPath(b->rchild);//扫描右子树
  }

 }

 ------

 (32)判读一棵树是否为完全二叉树;
 1)分析:
 采用层次遍历的方法,从上到下,从左到右的层次遍历,需要满足下面两个条件:
 1.某个节点没有左孩子,一定无右孩子。
 2.某个节点缺少右孩子,则按上到下,从左到右的顺序的下一个节点一定无孩子。

 2)代码实现:
 /*
complete表示二叉树为完全二叉树,初值为1 ,一定不满足上面两个条件则complete=0;
doubleSon表示所有节点均有两个孩子,初值为 1,一旦某个节点只有一个孩子,则置0.
 */
 int completeBTree(BTNode* b){
 BTNode * Queue[Maxsize];
 BTNode * pnode;
 int complete=1,doubleSon=1;
 int front=rear=0;

 if(b!=NULL){
  rear++;//根节点入队
  Queue[rear]=b;

  while(front!=rear){
   front++;
   pnode=Queue[front];

   if(NULL==pnode->lchild){//pnode左孩子为空
    doubleSon=0;
    if(pnode->rchild!=NULL)
    complete=0;
   }
   else{
    if(1==doubleSon){//pnode的前面没有缺少孩子的节点。
     rear++;//左孩子进队
     Queue[rear]=pnode->lchild;

     if(NULL==pnode->rchild)//右孩子为空
     doubleSon=0;
     else{//右孩子非空
      rear++;//右孩子进队
      Queue[rear]=pnode->rchild;
     }

    }
    else //doubleSon=0;说明已经有节点缺少孩子了。
    complete=0;
    //当前的pnode节点为缺少孩子节点的后继节点,但pnode有左孩子。违反2
   }

  }
  return complete;
 }
 else return 1;//默认空二叉树为完全二叉树
 }

--------

(33)已知二叉树存储在数组A[n]中,其中每个节点存储在数组的一个元素中,每个元素有3个字段,分别为data,ltag,rtag,其中data存储节点的值,ltag,rtag分别为左右孩子的下标,当没有左右孩子时,对应的下标为-1,根据如此的数组构造一个二叉链树。
1)举例如下图:

2)代码实现:
/*
数组中的元素时按照先序遍历存储的。
STree为结构体类型,包含三个字段,data,ltag,rtag。
i表示下标。
*/
BTNode* createTree(STree * A, int i){
 BTNode* b;
 b=(BTNode*) malloc(sizeof(BTNode));//根据A[i]中的data创建一个新节点。
 b->data=A[i].data;

 if(A[i].ltag!=-1){

  b->lchild=createTree(A,A[i].ltag);
 }
 else b->lchild=NULL;

 if(A[i].rtag!=-1){
  b->rchild=createTree(A,A[i].rtag)
 }
 else b->rchild=NULL;

retrun b;
}

------
(34)已知二叉树按照先序次序依次存储在数组A[n]中,每个节点存储在数组的一个元素中,每个元素有三个字段,分别为data,ltag,rtag,data表示节点的值,ltag,rtag表示是否有左右孩子,若有左右孩子,则对应的 标志为1,否则为0。
构造一个链式二叉树。

1)举例如图所示:

2)分析:
设置一个全局变量i(初始值为0)扫描数组A,采用先序遍历方式遍历数组A,先建立根节点b,如果左孩子,则下标i+1,则递归构造左子树;若有右孩子,则数组下标i+1,递归构造右子树;
注意:i为全局变量,例如i=0时,A[0].ltag=1,需要构造左子树,当左子树构造完毕i=5,此时无法判断A[0].rtag,
所以将rtag,ltag作为参数。

3)代码实现:
/*
STree是结构体数据类型,包含data,ltag,rtag三个字段。
i为全局变量,
所以根据数组A,以及下标i,来创建一个节点,ltag,rtag来判断是否有左右子树。
*/

//创建一个节点需要有数组名A,元素下标i,以及该节点的ltag,rtag来判断是否有左右孩子。
int i=0;
BTNode* createTree(STree A[], int ltag ,int rtag){
 BTNode* b;
 b=(BTNode*) malloc(sizeof(BTNode));
 b->data=A[i].data;
 i++;

 if(1==ltag) //左子树递归构造左子树
 b->lchild=createTree(A,A[i].ltag, A[i].rtag);
 else
 b->lchild=NULL;

 if(1==rtag)
 b->rchild=createTree(A,A[i].ltag,A[i].rtag);
 else b->rchild=NULL;

 return b;
}

-------


 (35)已知一棵完全二叉树存放在一个数组T[length]中,每个元素为各个节点的值。建立二叉树链式表示。
 1)分析:
 数组是从0号位置开始,所以节点i的左孩子下标为2*i+1,右孩子为2*i+2;
 2)注意:
 使用了引用型参数ptr,目的是把新建立的根节点带回上一层(即实参一层)。
 如:如果原来是空树,函数将root(实参,=NULL)通过ptr(作为root的别名)传入,函数建立新节点,该节点的地址直接放入root,成为该树的根节点。如果原来是非空树,ptr成为上一层ptr的lchild或者rchild的别名,新节点的地址将直接传入上一层ptr的lchild或rchild中,实现自动链接。
 3)代码实现:
 /*
根据T[i]来创建一个新节点ptr;
 */
 void createTree(DataType T[], int length ,int i , BTNode* & ptr){
  if(i>=length) ptr=NULL;
  else {
   ptr=(BTNode*) malloc(sizeof(BTNode));//建立根节点。
   ptr->data=T[i];
   createTree(T,length , 2*i+1,ptr->lchild);//递归建立左子树
   createTree(T,length , 2*i+2,ptr->rchild);//递归建立右子树
  }
 }
注:初次调用时,i=0;ptr用root(=NULL)作为实参代入。

------

(36)将一个用二叉链表示的 完全二叉树用二叉树的顺序表示;
/*
将t节点的data保存在A[i]中,
则t->lchild保存在A[2*i+1];
t->rchild保存在A[2*i+2]中;
*/
void linkList_to_Array(BTNode* t ,DataType []A, int length ,int i){
  if(NULL==t) return ;
  if(i>=n) {
  printf("内存不足");
  exit(0);
  }

  A[i]=t;
  linkList_to_Array(t->lchild, A,length ,2*i+1);
  linkList_to_Array(t->lchild, A,length ,2*i+1);
}
注:基于先序
-------
(37)求出二叉树t中指定节点p的父节点;
注:基于先序;

BTNode* getParent(BTNode* t, BTNode* p){
 BTNode* parent;
 if(NULL==t) return NULL;
 if(t->lchild==p || t->rchild == p)
  return t;
 parent=getParent(t->lchild,p);
 if(parent!=NULL) return parent;
 else return getParent(t->rchild,p);
}

---

(38)求二叉树中各节点的元素的最大值;
注:最大值使用引用型参数

void maxValue(BTNode* t , DataType & max ){
 if(t!=NULL){
  if(t->data>max) max=t->data;
  maxValue(t->lchild,max);
  maxValue(t->rchild,max);
 }
}

------
(39)给定一棵二叉树的先序序列为pre[s1,t1]和中序序列in[s2,t2],构造链式二叉树。
1)分析:
若s1<t1,则以pre[s1]建立二叉树的 根节点,然后在in[s2,t2]中搜索,寻找in[i]==pre[s1]的位置i,从而把中序序列分为in[s2,i-1]和in[i+1,t2];然后再以pre[s1+1,s1+(i-s2)] 和 in[s2,i-1]来构造左子树,以pre[s1+i-s2+1,t1]和in[i+1,t2]
递归构造跟的右子树。

2)代码实现:

void createBTree(BTNode* & t ,DataType pre[], DataType in[], int s1,int t1,int s2, int t2){
 int i;
 if(s1<=t1){
  t=(BTNode*) malloc(sizeof(BTNode));//创建根节点。
  t->data=pre[s1];
  t->lchild=t->rchild=NULL;

  for(i=s2;i<=t2;i++)
  if(in[i]==pre[s1]) break;//在中序in[]中找到根节点的位置i。

  createBTree(t->lchild,pre ,in,s1+1,s1+i-s2,s2,i-1);
  //(s1+i-s2)-(s1+1)==(i-1)-s2;
  createBTree(t->rchild,pre ,in,s1+i-s2+1,t1,i+1,t2);
  //t1-(s1+i-s2+1)==t2-(i+1);
 }
}

-------

(40)给定二叉树的后序遍历post[s1,t1]和中序 序列in[s2,t2],构造二叉树;
1)分析:
与上题类似,只不过
1.后序序列中二叉树根节点的位置在后序序列的最后的t1位置;
2.递归构造二叉树是先后后左;
3.子序列的边界不同;

2)代码实现:
void createBTree(BTNode* & t, DataType post[],DataType in[], int s1, int t1, int s2 , int t2){
 
 if(s1<=t1){
  t=(BTNode*) malloc(sizeof(BTNode));
  t->data=post[t1];
  t->lchild=t->rchild=NULL;

  int i;
  for(i=s2;i<=t2;i++)//在中序in[]中找到根节点的下标i
  if(in[i]==post[t1]) break;

  createBTree(t->rchild,post,in,s1+i-s2,t1-1,i+1,t2);//创建右子树
  createBTree(t->lchild,post,in,s1,s1+i-s2-1,s2,i-1);//创建左子树
 }
}

-------

(41)求二叉树的前序遍历中的第k个节点;
1)分析:
在前序遍历中加入引用型参数计算器count;
2)代码实现:

BTNode * pre_search(BTNode* t ,int & count, int k){
 if(t!=NULL){
  count++;
  if(count==k) return t;
  BTNode* pnode;

  pnode=pre_search(t->lchild,count,k);
  if(pnode) return pnode;
  else return pre_search(t->rchild,count,k);

 }
return NULL;
}
注:调用时count的初始值为0;

------

(42)利用二叉树的前序遍历求任意两个指定节点i和j的路径以及长度;
1)范例:
设I和J是树的两个节点,设从根节点A到节点I的路径是ABCDEI,从根节点到节点J的路径是ABPQRJ,则从节点I到节点J的路径是IEDCBPQRJ,长度为8.


2)思路:可以求出两个节点到根节点的路径,然后由此得到节点 i 和节点 j 的最近公共祖先节点 ancestor ,然后  节点 i 到节点 j 的最短路径 为 :节点 i  到 祖先节点 ancesotr , 然后祖先节点 ancesotr  到节点 j  ;

---

(43)已知一棵满二叉树的前序序列,求后序序列;
1)分析:
f(pre,s1,t1, post,s2,t2);满二叉树中任何一个节点的 左右子树均有相同数目的节点。
2)递归模型:
当 s1>t1时,   f(pre,s1,t1,post,s2,t2)中不做任何事;
当 s1<=t1时,  f(pre,s1,t1,post,s2,t2)中,
     post[t2]=pre[s1];
     取中间位置half=(t1-s1)/2;
     将pre[s1+1,s1+half]左子树转化为post[s2,s2+half-1];
     即:f(pre,s1+1,s1+half,post ,s2,s2+half-1);
     将pre[s1+half+1,t1]右子树转化为post[s2+half,t2-1];
     即:f(pre,s1+half+1,t1,post, s2+half,t2-1);
3)代码实现:
void pre_to_post(DataType pre[],int s1,int t1, DataType post[] ,int s2,int t2){
 int half;

 if(s1<=t1){
  post[t2]=pre[s1];
  half=(t1-s1)/2;
  pre_to_post(pre,s1+1,s1+half,post,s2,s2+half-1);//转换左子树
  pre_to_post(pre,s2+half+1,t1,post,s2+half,t2-1);//转换右子树
 }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值