8606 二叉树的构建及遍历操作

Description

 构造二叉链表表示的二叉树:按先序次序输入二叉树中结点的值(一个字符),'#'字符表示空树,构造二叉链表表示的二叉树T;再输出三种遍历序列。本题只给出部分代码,请补全内容。

#include "stdio.h"
#include "malloc.h"
#define TRUE 1
#define FALSE 0
#define OK  1
#define ERROR  0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int  Status;

typedef char  ElemType;
typedef struct BiTNode{
  ElemType data;
  struct BiTNode *lchild,*rchild;//左右孩子指针
} BiTNode,*BiTree;

Status CreateBiTree(BiTree &T) {  // 算法6.4
  // 按先序次序输入二叉树中结点的值(一个字符),’#’字符表示空树,
  // 构造二叉链表表示的二叉树T。
  char ch;
  scanf("%c",&ch);
  if (ch=='#') T = NULL;
  else {
    if (!(T = (BiTNode *)malloc(sizeof(BiTNode)))) return ERROR;
    ________________________ // 生成根结点
     _______________________   // 构造左子树
    _________________________  // 构造右子树
  }
  return OK;
} // CreateBiTree

 

Status PreOrderTraverse( BiTree T) {
   // 前序遍历二叉树T的递归算法
   //补全代码,可用多个语句

} // PreOrderTraverse

Status InOrderTraverse( BiTree T) {
     // 中序遍历二叉树T的递归算法
    //补全代码,可用多个语句
   

} // InOrderTraverse

Status PostOrderTraverse( BiTree T) {
     // 后序遍历二叉树T的递归算法
     //补全代码,可用多个语句

} // PostOrderTraverse

int main()   //主函数
{
                      //补充代码
}//main

输入格式

第一行:输入一棵二叉树的先序遍历序列

输出格式

第一行:二叉树的先序遍历序列
第二行:二叉树的中序遍历序列
第三行:二叉树的后序遍历序列

输入样例

AB##C##

输出样例

ABC
BAC
BCA

#include "stdio.h"
#include "malloc.h"
#define TRUE 1
#define FALSE 0
#define OK  1
#define ERROR  0
#define INFEASIBLE -1
#define OVERFLOW -2
#include <iostream>
using namespace std;
typedef int  Status;

typedef char  ElemType;
typedef struct BiTNode{
  ElemType data;
  struct BiTNode *lchild,*rchild;//左右孩子指针
} BiTNode,*BiTree;

Status CreateBiTree(BiTree &T) {  // 算法6.4
  // 按先序次序输入二叉树中结点的值(一个字符),’#’字符表示空树,
  // 构造二叉链表表示的二叉树T。
  char ch;
  scanf("%c",&ch);
  if (ch=='#') T = NULL;
  else {
    if (!(T = (BiTNode *)malloc(sizeof(BiTNode)))) return ERROR;
    T->data=ch; // 生成根结点
    CreateBiTree(T->lchild);// 构造左子树
    CreateBiTree(T->rchild); // 构造右子树
  }
  return OK;
} // CreateBiTree

Status PreOrderTraverse( BiTree T) {
   if(T==NULL) return ERROR;
   else{
   cout<<T->data;
   PreOrderTraverse(T->lchild);
   PreOrderTraverse(T->rchild);}
} // PreOrderTraverse

Status InOrderTraverse( BiTree T) {
    if(T==NULL) return ERROR;
    else{
   InOrderTraverse(T->lchild);
   cout<<T->data;
   InOrderTraverse(T->rchild);}
} // InOrderTraverse

Status PostOrderTraverse( BiTree T) {
   if(T==NULL) return ERROR;
   else{
   PostOrderTraverse(T->lchild);
   PostOrderTraverse(T->rchild);
   cout<<T->data;}
} // PostOrderTraverse



int main()   //主函数
{
    BiTree T;
    CreateBiTree(T);
    PreOrderTraverse(T);
    cout<<endl;
    InOrderTraverse(T);
    cout<<endl;
    PostOrderTraverse(T);
 }//main

解析:这道题是很基础的二叉树的基本操作,本质就是利用递归进行遍历,创建二叉树时就先创建根节点,将数据存入,然后递归创建左子树和右子树;前中后序遍历则按照根节点,左子树,右子树不同的顺序进行递归遍历,这道题没什么说的

17121 求二叉树各种节点数

Description

构造二叉链表表示的二叉树:按先序次序输入二叉树中结点的值(一个字符),'#'字符表示空树,构造二叉链表表示的二叉树T,并求此二叉树中度为2的节点总数,度为1的节点总数,度为0的节点总数

#include "stdio.h"
#include "malloc.h"
#define TRUE 1
#define FALSE 0
#define OK  1
#define ERROR  0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int  Status;

typedef char  ElemType;
typedef struct BiTNode{
  ElemType data;
  struct BiTNode *lchild,*rchild;//左右孩子指针
} BiTNode,*BiTree;

Status CreateBiTree(BiTree &T) {  // 算法6.4
  // 按先序次序输入二叉树中结点的值(一个字符),’#’字符表示空树,
  // 构造二叉链表表示的二叉树T。
  char ch;
  scanf("%c",&ch);
  if (ch=='#') T = NULL;
  else {
    if (!(T = (BiTNode *)malloc(sizeof(BiTNode)))) return ERROR;
    ________________________ // 生成根结点
     _______________________   // 构造左子树
    _________________________  // 构造右子树
  }
  return OK;
} // CreateBiTree

int main()   //主函数
{
                      //补充代码
}//main

输入格式

第一行输入先序次序二叉树中结点

输出格式

第一行输出度为2的节点数
第二行输出度为1的节点数
第三行输出度为0的节点数

输入样例

ABC###D##

输出样例

1
1
2

#include "stdio.h"
#include "malloc.h"
#define TRUE 1
#define FALSE 0
#define OK  1
#define ERROR  0
#define INFEASIBLE -1
#define OVERFLOW -2
#include<iostream>
using namespace std;
typedef int  Status;

typedef char  ElemType;
typedef struct BiTNode{
  ElemType data;
  struct BiTNode *lchild,*rchild;//左右孩子指针
} BiTNode,*BiTree;

Status CreateBiTree(BiTree &T) {  // 算法6.4
  // 按先序次序输入二叉树中结点的值(一个字符),’#’字符表示空树,
  // 构造二叉链表表示的二叉树T。
  char ch;
  scanf("%c",&ch);
  if (ch=='#') T = NULL;
  else {
    if (!(T = (BiTNode *)malloc(sizeof(BiTNode)))) return ERROR;
    T->data=ch; // 生成根结点
    CreateBiTree(T->lchild);  // 构造左子树
    CreateBiTree(T->rchild);  // 构造右子树
  }
  return OK;
} // CreateBiTree
int er(BiTree T)
{
    if(T==NULL) return ERROR;
    int ct=0;
    if(T->lchild!=NULL&&T->rchild!=NULL)
    {
        ct=1;
    }
    return ct+er(T->lchild)+er(T->rchild);
}
int yi(BiTree T)
{
    if(T==NULL) return ERROR;
    int ct=0;
    if((T->lchild!=NULL&&T->rchild==NULL)||(T->lchild==NULL&&T->rchild!=NULL))
    {
        ct= 1;
    }
    return ct+yi(T->lchild)+yi(T->rchild);
}
int o(BiTree T)
{
    if(T==NULL) return ERROR;
    if(T->lchild==NULL&&T->rchild==NULL)
    {
        return 1;
    }
    return o(T->lchild)+o(T->rchild);
}
int main()   //主函数
{
    BiTree T;
    CreateBiTree(T);
    cout<<er(T)<<endl;
    cout<<yi(T)<<endl;
    cout<<o(T)<<endl;
 }//main

解析:这题思路很简单,就是通过遍历将符合度数条件的一个个数出来,比如数节点度为1的函数int yi(BiTree T)中,如果树为空就return error,而度为1的条件是左孩子为空或者右孩子但是不能同时为空,遇到符合条件的就更新数量

18924 二叉树的宽度

Description

二叉树的宽度指的是具有节点数目最多的那一层的节点个数。
          1
         / \
        2   3
       /    
      4    
答案为2, 第二层节点数最多,为2个节点。

输入格式

共n行。
第一行一个整数n,表示有n个结点,编号为1至n,结点1为树根。(1<=n<=50)
第二行至第n行,每行有两个整数x和y,表示在二叉树中x为y的父节点。x第一次出现时y为左孩子

输出格式

输出二叉树的宽度。

输入样例

5
1 2
1 3
2 4
2 5

输出样例

2

#include <iostream>
#include <queue>
using namespace std;

int main()
{
    int n;
    cin>>n;
    int hai[105][3]={ };
    int width=1;
    int x,y;
    for(int i=1;i<n;i++)
    {
        cin>>x>>y;
        if(!hai[x][1])
        {
            hai[x][1]=y;
        }
        else
        {
            hai[x][2]=y;
        }
    }
    queue<int> man;
    man.push(1);
    while(man.size())
    {
        int len=man.size();
        width=max(width,len);
        for(int i=0;i<len;i++)
        {
            int temp=man.front();
            man.pop();
            if(hai[temp][1])
            {
                man.push(hai[temp][1]);
            }
            if(hai[temp][2])
            {
                man.push(hai[temp][2]);
            }
        }
    }
    cout<<width;
    return 0;
}

解析:这道题标程的思路就很好,将每一个节点的孩子节点通过另外一个数组存起来,方便处理。重点说说bfs的部分:

while(man.size())
    {
        int len=man.size();
        width=max(width,len);
        for(int i=0;i<len;i++)
        {
            int temp=man.front();
            man.pop();
            if(hai[temp][1])
            {
                man.push(hai[temp][1]);
            }
            if(hai[temp][2])
            {
                man.push(hai[temp][2]);
            }
        }
    }

先看内层循环,这一部分代码就是将各节点替换为各结点的孩子节点,而每一次更新队列,就会重新更新宽度,保证其为最大宽度,而len则为记录上一次内层循环更新后的队列的长度(即为上一层的宽度),len变量的存在既方便我们更新宽度,又方便我们更新队列,而外层循环的条件man.size()则巧妙地保证了能够在遍历完每一层树之后结束循环。

18724 二叉树的遍历运算

Description

二叉树的三种遍历都可以通过递归实现。
如果我们知道一棵二叉树的先序和中序序列,可以用递归的方法求后序遍历序列。

输入格式

两行,第一行一个字符串,表示树的先序遍历,第二行一个字符串,表示树的中序遍历。
树的结点一律用小写字母表示,且字符串长度不超过30。

输出格式

一个字符串,树的后序序列。

输入样例

abcde
bcade

输出样例

cbeda

#include <iostream>
#include <string>
using namespace std;
string pre,mid;
void post(int root,int start,int end)
{
    if(start>end) return;
    int i=start;
    while(i<end&&mid[i]!=pre[root])
    {
        i++;
    }
    post(root+1,start,i-1);
    post(root+i-start+1,i+1,end);
    cout<<mid[i];
}
int main()
{
    cin>>pre>>mid;
    int len=pre.length();
    post(0,0,len-1);
    return 0;
}

解析:这道题需要了解三种遍历的顺序的特点:

​​        先序​​:第一个元素是当前子树的根节点。

​​        中序​​:根节点将序列分为左子树和右子树。

所以思路就明确了:在先序中找到当前根节点(pre[root]),然后在中序中定位该根节点的位置i。中序中i左边的元素属于左子树,右边的属于右子树。对左子树和右子树分别递归调用,最后输出根节点,形成后序遍历(左→右→根)。

所以不断地找到根节点是这道题的关键。

仔细看一下post函数:

void post(int root,int start,int end)
{
    if(start>end) return;
    int i=start;
    while(i<end&&mid[i]!=pre[root])
    {
        i++;
    }
    post(root+1,start,i-1);
    post(root+i-start+1,i+1,end);
    cout<<mid[i];
}

参数root:前序遍历的根节点位置,start/end:子树在中序的位置

递归出口:start>end

变量i是标记根节点在中序遍历的位置。

post(root+1,start,i-1);则为遍历左子树,root+1:在先序中,左子树的根节点紧跟在当前根节点之后;中序范围为start到i - 1。

post(root+i-start+1,i+1,end);则为遍历右子树,根节点位置为root + i - start + 1(跳过左子树长度),中序范围为i + 1到end。

问:为什么这样就能保证后序输出呢?

答:后序的本质:

后序遍历的顺序是:​​左子树 → 右子树 → 根节点​​。
递归代码的关键在于:​​先处理左子树和右子树,最后处理根节点​​。

递归的调用栈决定了节点的处理顺序。代码中总是​​先深入左子树,再深入右子树,最后处理根节点​​。

  1. ​​左子树优先处理​​:递归会一直向左深入,直到左子树为空。
  2. ​​右子树次之处理​​:左子树处理完毕后,递归处理右子树。
  3. ​​根节点最后处理​​:左右子树都处理完毕后,才会将根节点加入结果。

做完这题建议去看洛谷P1827

https://www.luogu.com.cn/problem/P1827

18923 二叉树的直径

Description

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
          1
         / \
        2   3
       / \    
      4   5  
答案为3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。

输入格式

共n行。
第一行一个整数n,表示有n个结点,编号为1至n。
第二行至第n行,每行有两个整数x和y,表示在二叉树中x为y的父节点。x第一次出现时y为左孩子

输出格式

输出二叉树的直径。

输入样例

5
1 2
1 3
2 4
2 5

输出样例

3

#include <iostream>
#include <algorithm>
using namespace std;
int man[100][3],d=0;
int dfs(int root)
{
    if(!root) return 0;
    int lchild=dfs(man[root][1]);
    int rchild=dfs(man[root][2]);
    int depth=max(lchild,rchild)+1;
    d=max(d,lchild+rchild);
    return depth;
}
int main()
{
    int n;
    cin>>n;
    int x,y;
    for(int i=1;i<n;i++)
    {
        cin>>x>>y;
        if(!man[x][1]) man[x][1]=y;
        else man[x][2]=y;
    }
    dfs(1);
    cout<<d<<endl;
    return 0;
}

解析:这道题还是讲解标程思路,还是同样的思路,利用另一个数组存各节点的孩子节点方便处理。仔细看看dfs函数:

int dfs(int root)
{
    if(!root) return 0;
    int lchild=dfs(man[root][1]);
    int rchild=dfs(man[root][2]);
    int depth=max(lchild,rchild)+1;
    d=max(d,lchild+rchild);
    return depth;
}

首先我们必须清楚,直径是左右子树高度之和,为什么?高度即为最深叶子结点到当前节点距离是多少,假设当前节点的左子树高度为2(路径A → B → 当前节点),右子树高度为3(路径当前节点 → C → D → E),则经过当前节点的最长路径为 A → B → 当前节点 → C → D → E,长度为5。

所以,我们只需要递归遍历左子树和右子树的高度,不断更新深度就行。

8609 哈夫曼树

Description

利用静态链表建立赫夫曼树,建树过程中要求左子树权值小于右子树权值,求各结点的编码。要求:叶子结点的个数n及结点值由键盘录入。本题给出程序代码,要求填空以满足测试要求.
#include "stdio.h"
#include "string.h"
#include
using namespace std;
typedef struct
{
    unsigned int weight;
    unsigned int parent,lchild,rchild;
} HTNode,*HuffmanTree;
typedef char **HuffmanCode;
void   select(HuffmanTree &HT, int n, int &s1, int &s2)
{//在HT[1..n]中选择parent为0且weight最小的两个结点, 其序号分别为s1(最小)和s2(次小)。
   __________________________
}
void createHuffmanTree(HuffmanTree &HT, int n)
{ //构造哈夫曼树HT
    int i, m, s1, s2;
    if (n<=1) return;
    m = 2 * n - 1;
    HT = new HTNode[m+1];  // 0号单元未用
    for (i=1; i<=m; i++) { //初始化HT数组
        HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;
    }
    for (i=1; i<=n; i++)
        cin>>HT[i].weight;
    for (i=n+1; i<=m; i++)    // 建哈夫曼树
    { //在HT[1..i-1]中选择parent为0且weight最小的两个结点, 其序号分别为s1(最小)和s2(次小)
         _______________________________
    }
}
void createHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{//--- 从叶子到根逆向求每个字符的哈夫曼编码 ---
    char *cd = new char[n];    // 分配求编码的工作空间
    cd[n-1] = '\0';  // 编码结束符。
    int i,c,f,start;
    for (i=1; i<=n; ++i)
    {
        start = n-1;
        c=i, f=HT[i].parent;
        while(f)// 从叶子到根逆向求编码
        {
            --start;
            if (HT[f].lchild==c) cd[start] = '0';
            else cd[start] = '1';
            c=f,f=HT[f].parent;
        }
        HC[i] = new char[n-start];// 为第i个字符编码分配空间
        strcpy(HC[i], &cd[start]);    // 从cd复制编码(串)到HC
    }
}
int main()
{
    int i,n;
    int *w;
    HuffmanTree HT;
    HuffmanCode HC;
    scanf("%d",&n);  //权值个数
    HC=new char*[n+1]; //0空间未用
    createHuffmanTree(HT,n);
    createHuffmanCode(HT,HC,n);
    for (i = 1; i<=n; i++)
        printf("%s\n",HC[i]);  //输出哈夫曼编码
}

输入格式

第一行:权值个数
第二行:输入n个权值,用空格分隔

输出格式

输出n行
每行表示各权值对应的哈夫曼编码

输入样例

8
5 29 7 8 14 23 3 11

输出样例

0001
10
1110
1111
110
01
0000
001

#include "stdio.h"
#include "string.h"
#include <iostream>
using namespace std;
typedef struct
{
    unsigned int weight;
    unsigned int parent,lchild,rchild;
} HTNode,*HuffmanTree;
typedef char **HuffmanCode;
void   select(HuffmanTree &HT, int n, int &s1, int &s2)
{//在HT[1..n]中选择parent为0且weight最小的两个结点, 其序号分别为s1(最小)和s2(次小)。
   int minn=1;
   for(int i=1;i<=n;++i)
   {
       if(HT[i].parent==0)
       {
           minn=i;
           break;
       }
   }
   for(int i=minn;i<=n;++i)
   {
       if(HT[i].parent==0&&HT[i].weight<HT[minn].weight)
       {
           minn=i;
       }
   }
   s1=minn;
   minn=1;
   for(int i=1;i<=n;++i)
   {
       if(HT[i].parent==0&&s1!=i)
       {
           minn=i;
           break;
       }
   }
   for(int i=minn;i<=n;++i)
   {
       if(HT[i].parent==0&&HT[i].weight<HT[minn].weight&&i!=s1)
       {
           minn=i;
       }
   }
   s2=minn;
}
void createHuffmanTree(HuffmanTree &HT, int n)
{ //构造哈夫曼树HT
    int i, m, s1, s2;
    if (n<=1) return;
    m = 2 * n - 1;
    HT = new HTNode[m+1];  // 0号单元未用
    for (i=1; i<=m; i++) { //初始化HT数组
        HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;
    }
    for (i=1; i<=n; i++)
        cin>>HT[i].weight;
    for (i=n+1; i<=m; i++)    // 建哈夫曼树
    { //在HT[1..i-1]中选择parent为0且weight最小的两个结点, 其序号分别为s1(最小)和s2(次小)
         select(HT,i-1,s1,s2);
         HT[s1].parent=i;HT[s2].parent=i;
         HT[i].lchild=s1;HT[i].rchild=s2;
         HT[i].weight=HT[s1].weight+HT[s2].weight;
    }
}
void createHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{//--- 从叶子到根逆向求每个字符的哈夫曼编码 ---
    char *cd = new char[n];    // 分配求编码的工作空间
    cd[n-1] = '\0';  // 编码结束符。
    int i,c,f,start;
    for (i=1; i<=n; ++i)
    {
        start = n-1;
        c=i, f=HT[i].parent;
        while(f)// 从叶子到根逆向求编码
        {
            --start;
            if (HT[f].lchild==c) cd[start] = '0';
            else cd[start] = '1';
            c=f,f=HT[f].parent;
        }
        HC[i] = new char[n-start];// 为第i个字符编码分配空间
        strcpy(HC[i], &cd[start]);    // 从cd复制编码(串)到HC
    }
}
int main()
{
    int i,n;
    int *w;
    HuffmanTree HT;
    HuffmanCode HC;
    scanf("%d",&n);  //权值个数
    HC=new char*[n+1]; //0空间未用
    createHuffmanTree(HT,n);
    createHuffmanCode(HT,HC,n);
    for (i = 1; i<=n; i++)
        printf("%s\n",HC[i]);  //输出哈夫曼编码
}

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐