【数据结构初学笔记11】第三章二叉树MOOC练习题

课后题

  1. 树的同构 小白专场会做详细讲解,基本要求,一定要做

  2. List Leaves 训练建树和遍历基本功,一定要做

  3. Tree Traversals Again 是2014年秋季PAT甲级考试真题,稍微要动下脑筋,想通了其实程序很基础,建议尝试

1.树的同构

//树的同构练习题:给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换,就变成T2,则我们称两棵树是“同构”的。
//现给定两棵树,请你判断它们是否是同构的。
//解题思路:1,二叉树表示2,建立二叉树3,同构判断
// 1,二叉树表示:首先输入二叉树结点个数,依次输入第i个结点,包括结点数据,左右孩子结点编号,输入-表示孩子结点不存在
// 2,建立二叉树:采用静态链表结构,由结点组成数组,每个结点存储数据和左右孩子结点的编号。若某个序号均不与左右孩子序号相符,则该节点为根节点。
//  若想调用该二叉树,需要知道其根结点序号Root1,Root2。
// 3.判别同构,分为以下几种情况:
//结点为空:1,两个结点都为空,同构;2,一个结点为空,一个结点非空,不同构;
//结点都不空:3,两个结点值不相同,不同构;4,结点值相同,左孩子都为空,继续判断右孩子;
// 5,结点值相同,左孩子都不空,且左孩子的值相等,继续判断其左孩子,右孩子均同构;6,情况5的补充,继续判断其左右孩子与对应右左孩子是否同构。
#include <stdio.h>
#include <stdlib.h>
#define MaxTree 10
#define ElementType char
#define Tree int
#define Null -1

struct TreeNode
{
    ElementType Data;
    Tree Left;
    Tree Right;
} T1[MaxTree], T2[MaxTree];
int BuildTree(struct TreeNode T[MaxTree]); //获取输入数据,并建立二叉树
int Isomorphic(int Root1, int Root2);      //判断二叉树是否同构,是则返回1,否则返回0.
int main(void)
{
    Tree Root1, Root2;
    Root1 = BuildTree(T1);
    Root2 = BuildTree(T2);
    if (Isomorphic(Root1, Root2))
        printf("Yes\n");
    else
        printf("No\n");
    return 0;
}
int BuildTree(struct TreeNode T[MaxTree])
{
    int N, i, Root, check[MaxTree];
    char cl, cr;
    scanf("%d", &N);
    // printf("N=%d\n", N);
    // getchar();
    if (N)
    {
        for (i = 0; i < N; i++)
        {
            check[i] = 0;
        }
        for (i = 0; i < N; i++)
        {
            scanf(" %c %c %c", &T[i].Data, &cl, &cr); //在格式字符串中,将空格放到%c前面,scanf()便会跳过空格,从第一个非空白字符开始读取。

            if (cl != '-') //若左子树存在,记录左子树序号,并将对应位置check值记为1;
            {
                T[i].Left = cl - '0';
                check[T[i].Left] = 1;
            }
            else //若左子树不存在,将左子树序号记为Null=-1;
                T[i].Left = Null;
            if (cr != '-')
            {
                T[i].Right = cr - '0';
                check[T[i].Right] = 1;
            }
            else
                T[i].Right = Null;
            // printf("Data=%c,cl=%d,cr=%d\n", T[i].Data, T[i].Left, T[i].Right);
        }
        for (i = 0; i < N; i++)
        {
            if (!check[i])
            {
                Root = i;
                break;
            }
        }
    }
    else
        Root = Null; //根节点为空
    // printf("Root:%d\n", Root);
    return Root;
}
int Isomorphic(int Root1, int Root2)
{ //判别同构,分为以下几种情况:
    //结点为空:1,两个结点都为空,同构;2,一个结点为空,一个结点非空,不同构;
    //结点都不空:3,两个结点值不相同,不同构;4,结点值相同,左孩子都为空,继续判断右孩子;
    // 5,结点值相同,左孩子都不空,且左孩子的值相等,继续判断其左孩子,右孩子均同构;
    // 6,情况5的补充,继续判断其左右孩子与对应右左孩子是否同构。
    if ((Root1 == Null) && (Root2 == Null))
        return 1;
    if (((Root1 == Null) && (Root2 != Null)) || ((Root1 != Null) && (Root2 == Null)))
        return 0;
    if (T1[Root1].Data != T2[Root2].Data)
        return 0;
    if ((T1[Root1].Left == Null) && (T2[Root2].Left == Null))
        return Isomorphic(T1[Root1].Right, T2[Root2].Right);
    if ((T1[Root1].Right == Null) && (T2[Root2].Right == Null))
        return Isomorphic(T1[Root1].Left, T2[Root2].Left);
    if (((T1[Root1].Left != Null) && (T2[Root2].Left != Null)) && ((T1[T1[Root1].Left].Data) == (T2[T2[Root2].Left].Data)))
        return (Isomorphic(T1[Root1].Left, T2[Root2].Left) && Isomorphic(T1[Root1].Right, T2[Root2].Right));
    else
        return (Isomorphic(T1[Root1].Left, T2[Root2].Right) && Isomorphic(T1[Root1].Right, T2[Root2].Left));
}

2.List Leaves

//题目要求:从上到下,从左到右的顺序输出叶结点
//解题思路:应采用层序遍历的方法,进行输出;采用链表数组形式来存储二叉树;记录每个二叉树的根节点;采用队列方法进行层序遍历
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 10
#define Null -1
struct TreeNode
{
    int Left;
    int Right;
} T1[MaxSize];
static struct QueueNode
{
    int Data[MaxSize];
    int front;
    int rear;
} Q;
int BuildTree(struct TreeNode T[MaxSize]); //接收输入建立二叉树,并返回二叉树根节点的序号Root
void LevelOderPrintLeaves(int Root);       //按照层序遍历的顺序,输出二叉树叶结点序号
void Add(struct QueueNode *Qp, int Data);  //入队列
int DeleteQueue(struct QueueNode *Qp);     //出队列
int main(void)
{
    int Root1; //二叉树的叶结点
    Root1 = BuildTree(T1);
    LevelOderPrintLeaves(Root1);
    return 0;
}
int BuildTree(struct TreeNode T[MaxSize])
{
    int N, Root, i;
    scanf("%d", &N);
    // printf("N=%d\n", N);
    if (N) //输入二叉树非空
    {
        int check[MaxSize]; //用于记录根节点位置,根节点对应位置为0,其余位置为1
        char tl, tr;
        for (i = 0; i < N; i++)
        {
            check[i] = 0;
        }
        for (i = 0; i < N; i++)
        {
            scanf(" %c %c", &tl, &tr);
            if (tl != '-') //若左子树存在,记录左子树序号
            {
                T[i].Left = tl - '0';
                check[T[i].Left] = 1;
            }
            else //若左子树不存在,将左子树序号记为Null=-1;
                T[i].Left = Null;
            if (tr != '-')
            {
                T[i].Right = tr - '0';
                check[T[i].Right] = 1;
            }
            else
                T[i].Right = Null;
        }
        for (i = 0; i < N; i++) //找到根节点位置
        {
            if (!check[i])
            {
                Root = i;
                break;
            }
        }
    }
    else
        Root = Null;
    // printf("Root:%d\n", Root);
    return Root;
}
void LevelOderPrintLeaves(int Root)
{                     //按照层序遍历的顺序输出叶结点
int i=0;//输出空格的标志
    if (Root == Null) //二叉树为空,直接返回
        return;
    Q.front = 0; //初始化队列Q
    Q.rear = 0;
    Add(&Q, Root);            //将根结点序号送入队列
    while (Q.rear != Q.front) //队列非空,表示二叉树还没结束
    {
        Root = DeleteQueue(&Q);                                  //输出当前结点的序号
        if ((T1[Root].Left == Null) && (T1[Root].Right == Null)) //左子树,右子树均不存在,为叶子结点,并输出结点序号Root
        {
            if (i)
                printf(" ");
            printf("%d", Root);
            i=1;
        }
        if (T1[Root].Left != Null) //左子树非空,将左子树序号存入队列中
            Add(&Q, T1[Root].Left);
        if (T1[Root].Right != Null) //右子树非空,将右子树序号存入队列中
            Add(&Q, T1[Root].Right);
    }
}
void Add(struct QueueNode *Qp, int Data)
{
    if ((Qp->rear + 1) % MaxSize == Qp->front)
    {
        printf("队列满");
        return;
    }
    Qp->Data[Qp->rear] = Data;
    Qp->rear = (Qp->rear + 1) % MaxSize;
    return;
}
int DeleteQueue(struct QueueNode *Qp)
{
    int item;
    if (Qp->rear == Qp->front)
    {
        printf("队列空");
        return -1;
    }
    item = Qp->Data[Qp->front];
    Qp->front = (Qp->front + 1) % MaxSize;
    return item;
}

3.Tree Traversals Again

注意:该程序提交PTA进行运行,部分正确,当输入二叉树N=30且为复杂组合时,运行程序报出段错误。

---------------------------------------------------分割线---------------------------------------------------

20221108,陈越老师讲了一种不用建树的方法,利用先序中序和后序遍历的子树关系,每次确定当前树根结点,利用迭代方法便可以得到后序遍历结果。完成程序实现如下:

/*Tree Traversals Again:给定Push和Pop操作,Push对应中序遍历.Pop对应后序遍历序列,根据递归程序生成后序遍历*/
/*老师思路:先序遍历:根结点+左子树+右子树;中序遍历:左子树+根结点+右子树;后序遍历:左子树+右子树+根结点
1,首先将先序遍历根结点pre[preL]存入后序遍历post[postL+n-1],然后根据根结点在中序遍历中的序号inL+i;
2,确定左子树[preL+1,inL,postL,L=i],和右子树[preL+1+L,inL+L+1,postL+L,R=n-L-1]
3,解决临界点:n=0;n=1;
*/
#include <stdio.h>
#include <stdlib.h>
#define Maxsize 100
int in[Maxsize], pre[Maxsize], post[Maxsize];
/*堆栈的链式存储*/
#define ElementType int
#define ERROR 0
typedef struct SNode *Stack;
struct SNode
{
    ElementType Data;
    Stack Next;
};

/*堆栈的函数声明*/
Stack CreateStack();
int IsEmpty(Stack S);
void Push(Stack S, ElementType item); //入栈
ElementType Pop(Stack S);             //出栈

void solve(int preL, int inL, int postL, int n);

int main(void)
{
    Stack S;
    S = CreateStack();
    int N, i, p, q;
    p = 0;
    q = 0;
    char a[4];
    scanf("%d", &N);
    getchar();
    for (i = 0; i < 2 * N; i++)
    {
        a[0] = getchar();
        a[1] = getchar();
        a[2] = getchar();
        if (a[0] == 'P' && a[1] == 'u')
        {
            getchar();
            getchar();              /*Push+空格*/
            scanf("%d", &pre[p++]); /*对应的是先序遍历*/
            Push(S, pre[p - 1]);
            getchar(); /*回车符*/
        }
        else if (a[0] == 'P' && a[1] == 'o')
        {
            in[q++] = Pop(S); /*对应的是中序遍历*/
            getchar();        /*回车符*/
        }
        else
            printf("输入错误\n");
    }
    solve(0, 0, 0, N);

    /*打印*/
    for (i = 0; i < N - 1; i++)
    {
        printf("%d ", post[i]);
    }
    printf("%d\n", post[i]);

    return 0;
}
void solve(int preL, int inL, int postL, int n)
{
    int root, i, L, R;
    /*解决临界点*/
    if (n == 0)
        return;
    if (n == 1)
    {
        post[postL] = pre[preL];
        return;
    }
    root = pre[preL];           /*保存根结点*/
    post[postL + n - 1] = root; /*将根结点放到后序遍历序列中,位置在当前子树的最后一个位置*/
    for (i = 0; i < n; i++)     /*在中序遍历序列中寻找根结点的下标*/
        if (in[inL + i] == root)
            break;
    L = i;                                          /*左子树的结点数*/
    R = n - L - 1;                                  /*右子树的结点数*/
    solve(preL + 1, inL, postL, L);                 /*解决左子树,先序遍历preL+1,中序遍历inL,后序遍历postL,树的结点数L*/
    solve(preL + L + 1, inL + L + 1, postL + L, R); /*解决右子树,先序遍历preL+1+L,中序遍历inL+L+1,后序遍历postL+L,树的结点数L*/
}
//堆栈初始化(建立空栈)
/*注意这里创建的是一个带头结点链式堆栈,还存在无头节点的形式*/
Stack CreateStack()
{
    Stack S;
    S = (Stack)malloc(sizeof(struct SNode));
    S->Next = NULL;
    return S;
}
//判断堆栈S是否为空,若为空返回整数1,否则返回0
int IsEmpty(Stack S)
{
    return (S->Next == NULL);
}
// 02.入栈
void Push(Stack S, ElementType item)
{
    Stack TmpCell;
    TmpCell = (Stack)malloc(sizeof(struct SNode));
    TmpCell->Data = item;
    TmpCell->Next = S->Next;
    S->Next = TmpCell;
}
// 03.出栈
ElementType Pop(Stack S)
{
    Stack FirstCell;
    ElementType TopElem;
    if (S->Next == NULL)
    {
        printf("堆栈空");
        return ERROR;
    }
    else
    {
        FirstCell = S->Next;
        S->Next = FirstCell->Next;
        TopElem = FirstCell->Data;
        free(FirstCell);
        return TopElem;
    }
}

---------------------------------------------------分割线---------------------------------------------------

20221103,段错误是原因使用了getchar函数作为输入,使得元素值>=10时无法被正确读取,导致段错误出现,已经进行更正。


//题中给出的堆栈操作指令,其中Push对应二叉树先序遍历结果,Pop对应二叉树中序遍历结果;要求实现二叉树的后序遍历输出
//操作注意:scanf输入一个数据后,注意匹配回车键,否则回车符会保存在缓冲区,影响下一个键盘输入,尤其是输入为字符串或者字符时。
#include <stdio.h>
#include <stdlib.h>
// #include <cstring>
#define ElementType int
#define MaxSize 31
#define ERROR -1
int idx=0;
int Post[MaxSize+1];//存储后序遍历的输出结果
int PreOrder[MaxSize], InOrder[MaxSize]; //保存二叉树先序和中序遍历结果
//二叉树的链式存储
typedef struct TreeNode *TNode;
struct TreeNode
{
    ElementType Data;
    TNode Left;
    TNode Right;
};
//堆栈的顺序存储
typedef struct SNode *Stack;
struct SNode
{
    ElementType Data[MaxSize];
    int Top;
} S1;
TNode BuildTree(int PreL, int PreR, int InL, int InR); //建立二叉树(采用链式存储结构)
void Push(Stack PtrS, ElementType item);               //入栈
ElementType Pop(Stack PtrS);                           //出栈
void PostOrderTraversal(TNode BT);                     //后序遍历
int main(void)
{
    //接收输入
    int nums, i, j, k, data,item; // nums二叉树结点个数
    i = 0;
    j = 0;
    char Str[5]; //输入的命令,要预留一个元素位置保存空格
    scanf("%d\n", &nums);
    if (nums) //二叉树非空
    {
        for (k = 0; k < (2 * nums); k++) //接收输入
        {
            Str[0]=getchar();
            Str[1]=getchar();
            Str[2]=getchar();
            Str[5]='\0';
            if (Str[0] == 'P' && Str[1] == 'u')
            {
                Str[3]=getchar();
                Str[4]=getchar();
                scanf("%d",&data);/*注意,这里要输入一个数字,不可以再用getchar,否则导致无法读取数字十位及以上部分*/
                getchar();//处理缓冲区的回车符
                // printf("Str:%s Data:%d\n", Str, data);
                PreOrder[i++] = data;
                Push(&S1, data);
            }
            else // Pop操作
            {
                getchar();//处理缓冲区的回车符
                // printf("Str:%s\n", Str);
                InOrder[j++] = Pop(&S1);
            }
        }
    }
    TNode BT = BuildTree(0, nums - 1, 0, nums - 1);
    PostOrderTraversal(BT);
    for(i=0;i<idx-1;i++)
    printf("%d ",Post[i]);
    printf("%d",Post[idx-1]);
    return 0;
}
//建立二叉树,先找根结点,确定根节点左右子树的序号区间,然后通过递归找左右子结点,返回根结点地址
//先序遍历:根结点-左子树-右子树;
//中序遍历:左子树-根结点-右子树;
TNode BuildTree(int PreL, int PreR, int InL, int InR)
{
    if (PreL > PreR)
        return NULL;
    int k; //根节点在中序遍历中的序号
    TNode Root = (TNode)malloc(sizeof(struct TreeNode));
    Root->Data = PreOrder[PreL];
    for (int i = InL; i <= InR; i++)
    {
        if (Root->Data == InOrder[i])
        {
            k = i;
            break;
        }
    }
    int Leftnum = k - InL;
    Root->Left = BuildTree(PreL + 1, PreL + Leftnum, InL, k - 1);
    Root->Right = BuildTree(PreL + Leftnum + 1, PreR, k + 1, InR);
    return Root;
}
void Push(Stack PtrS, ElementType item)
{ //入栈
    if (PtrS->Top == MaxSize)
    {
        printf("堆栈满\n");
        return;
    }
    else
    {
        PtrS->Top++;
        PtrS->Data[PtrS->Top] = item;
        return;
    }
}
ElementType Pop(Stack PtrS)
{ //出栈
    if (PtrS->Top == -1)
    {
        printf("堆栈空\n");
        return ERROR;
    }
    else
        return (PtrS->Data[(PtrS->Top)--]);
}
void PostOrderTraversal(TNode BT)
{
    if (BT)
    {
        PostOrderTraversal(BT->Left);
        PostOrderTraversal(BT->Right);
        Post[idx++]=BT->Data;
    }
}
/*N=30,测试
30
Push 1
Push 2
Push 3
Push 4
Push 5
Push 6
Push 7
Push 8
Push 9
Push 10
Pop
Pop
Pop
Pop
Pop
Pop
Pop
Pop
Pop
Pop
Push 11
Push 12
Push 13
Push 14
Push 15
Push 16
Push 17
Push 18
Push 19
Push 20
Pop
Pop
Pop
Pop
Pop
Pop
Pop
Pop
Pop
Pop
Push 21
Push 22
Push 23
Push 24
Push 25
Push 26
Push 27
Push 28
Push 29
Push 30
Pop
Pop
Pop
Pop
Pop
Pop
Pop
Pop
Pop
Pop
*/

结束

课程来源:浙江大学数据结构慕课MOOC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值