CSP历年复赛题-P5018 [NOIP2018 普及组] 对称二叉树
原题链接:https://www.luogu.com.cn/problem/P5018
题意解读:找到是对称二叉树的最大子树节点数。
解题思路:
1、先统计每一个节点为子树的节点数
int dfs1(int root)
{
if(root == -1) return 0;
return cnt[root] = dfs1(tree[root].l) + dfs1(tree[root].r) + 1;
}
2、再从根依次向下枚举每一个子节点,判断左右子树是否对称,如果对称则输出子树节点数,否则递归从左、右子树中找最大的对称子树节点数
int dfs2(int root)
{
if(root == -1) return 0;
if(check(tree[root].l, tree[root].r)) return cnt[root];
else return max(dfs2(tree[root].l), dfs2(tree[root].r));
}
3、检查一个节点的左、右子树是否对称
如果左、右子树的根都是-1,则是对称
如果左、右子树节点数不相等,肯定不对称
如果左、右子树根节点权值不相等,肯定不对称
递归判断左子树的左子树是否和右子树的右子树对称,左子树的右子树是否和右子树的左子树对称
bool check(int left, int right)
{
//左右都是空,只有根节点,也是对称的
if(left == -1 && right == -1) return true;
//左右子树节点数不一样,肯定不对称
if(cnt[left] != cnt[right]) return false;
//左右节点权值不同,也不对称
if(tree[left].w != tree[right].w) return false;
//递归判断左-左、右-右,左-右、右-左是否对称
return check(tree[left].l, tree[right].r) && check(tree[left].r, tree[right].l);
}
时间复杂度分析:枚举每一个节点复杂度是n,如果从某个节点开始是对称,则需要递归logn次,总体复杂度n*logn
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n;
struct node
{
int l, r, w;
} tree[N];
int cnt[N]; //以每个节点为根的子树的节点数
//统计所有节点为根的子树的节点数
int dfs1(int root)
{
if(root == -1) return 0;
return cnt[root] = dfs1(tree[root].l) + dfs1(tree[root].r) + 1;
}
//检查以left,right为根的子树是否对称
bool check(int left, int right)
{
//左右都是空,只有根节点,也是对称的
if(left == -1 && right == -1) return true;
//左右子树节点数不一样,肯定不对称
if(cnt[left] != cnt[right]) return false;
//左右节点权值不同,也不对称
if(tree[left].w != tree[right].w) return false;
//递归判断左-左、右-右,左-右、右-左是否对称
return check(tree[left].l, tree[right].r) && check(tree[left].r, tree[right].l);
}
//枚举所有点,看左右是否对称,返回对称的最大子树节点数
int dfs2(int root)
{
if(root == -1) return 0;
if(check(tree[root].l, tree[root].r)) return cnt[root];
else return max(dfs2(tree[root].l), dfs2(tree[root].r));
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> tree[i].w;
}
for(int i = 1; i <= n; i++)
{
cin >> tree[i].l >> tree[i].r;
}
dfs1(1);
cout << dfs2(1);
return 0;
}