信息学奥赛一本通 1981:【18NOIP普及组】对称二叉树 | 洛谷 P5018【NOIP2018 普及组】 对称二叉树

【题目链接】

ybt 1981:【18NOIP普及组】对称二叉树
洛谷 P5018【NOIP2018 普及组】 对称二叉树

【题目考点】

  1. 二叉树

【解题思路】

  1. 先求出二叉树中各子树的结点数
  2. 遍历二叉树,判断每个子树是否是对称二叉树
  • 判断一个二叉树是否是对称二叉树:
  • 对称变换:将该树中所有结点的左右子树交换。显然,一棵树经过两次对称变换后,会变为原来的树。
  • 两树对称:如果树1经过对称变换可以变为树2,那么称树1与树2对称。显然,树1与树2对称时,树2与树1对称。
  • 如果一个二叉树是对称二叉树,那么其左子树应该与右子树对称。
    • 证明:二叉树是对称的,那么其经过对称变换后的左子树,是由原右子树变换过来的。已知对称变换后的树与原树相同,那么变换后的左子树与原左子树相同,那么原左子树与原右子树是对称的。
  • 设函数check(),参数为树1、树2两棵树的根,该函数判断树1、树2是否对称。
  • 判断两棵树是否对称:
    • 如果两棵树都是空树(根结点地址都为-1),那么它们是对称的。
    • 如果两棵树都不是空树,同时满足如下条件时,二者才是对称的
      • 两棵树根结点的权值应该相同
      • 两棵树的结点数应该相同
      • 树1的左子树应该与树2右子树对称。
      • 树1的右子树应该与树2的左子树对称

【题解代码】

解法1:
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
typedef struct Node
{
    int val, left, right;//val:权值, left:左孩子地址 right:右孩子地址
}Node;
Node tree[N];//tree[i],为id为i的结点
int treeNodeNum[N];//treeNodeNum[i]保存id为i的结点的子树的结点数。
int n;
int mxNodeNum;//结点数量最大的对称子树的结点数

//判断以root1为根的子树和以root2为根的子树是否对称
bool check(int root1, int root2)
{
    if(root1 == -1 && root2 == -1)//空结点,相同
        return true;
    else if(root1 != -1 && root2 != -1 && //左右子树都有结点
            tree[root1].val == tree[root2].val && //树根结点权相同,
            treeNodeNum[root1] == treeNodeNum[root2] && //树结点数相同
            check(tree[root1].left, tree[root2].right) && //经过对称变换后,root2树的右子树应该与root1树的左子树对称
            check(tree[root1].right, tree[root2].left))//经过对称变换后,root2树的左子树应该与root1树的右子树对称
        return true;
    else
        return false;
}

//获取树的结点数
int getNodeNum(int root)
{
    if(root == -1)
        return 0;
    else
    {
        int nodeNum = getNodeNum(tree[root].left) + getNodeNum(tree[root].right) + 1;
        treeNodeNum[root] = nodeNum;
        return nodeNum;
    }
}

//确定结点最多的对称的子树的结点数
void Search(int root)
{
    if(root != -1)
    {
        if(treeNodeNum[root] <= mxNodeNum)//剪枝,如果当前子树的结点数已经比获得的最大对称子树结点数要小,那么没必要再搜索下去。
            return;
        if(check(tree[root].left, tree[root].right))//如果树root是对称的,那么其左右子树对称
            mxNodeNum = treeNodeNum[root];//如果对称,记录结点数。由于已有剪枝操作,此时treeNodeNum[root]一定大于mxNodeNum
        else
        {
            Search(tree[root].left);
            Search(tree[root].right);
        }
    }
}

int main()
{
    scanf("%d\n", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &tree[i].val);
    for(int i = 1; i <= n; ++i)
        scanf("%d %d", &tree[i].left, &tree[i].right);
    getNodeNum(1);//初始化树的各子树结点数
    Search(1);//从根结点开始,遍历所有子树,找出对称子树,并看其结点数,保存最大结点数
    printf("%d", mxNodeNum);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值