基础回顾--基本树形结构运用

本篇只是非常基础的一些树形结构的入门。

树形结构与链表感觉很类似,只是有分叉的链表。内存不连续,内存之间(节点之间)的联系是通过内存的编号

很多时候树的题目往往不需要构造出树,只需要就着树的性质灵活模拟。

下面就挑一道感觉比较经典的题~

原题连接

题目描述:

我们可以把由“000”和“111”组成的字符串分为三类:全“000”串称为BBB串,全“111”串称为I串,既含“000”又含“111”的串则称为F串。

FBIFBIFBI树是一种二叉树,它的结点类型也包括FFF结点,BBB结点和I结点三种。由一个长度为2N2^N2N的“010101”串S可以构造出一棵FBIFBIFBI树TTT,递归的构造方法如下:

  1. TTT的根结点为RRR,其类型与串SSS的类型相同;
  2. 若串SSS的长度大于111,将串SSS从中间分开,分为等长的左右子串S1S_1S1S2S_2S2;由左子串S1S_1S1构造R的左子树T1T_1T1,由右子串S2S_2S2构造RRR的右子树T2T_2T2

现在给定一个长度为2N2^N2N的“010101”串,请用上述构造方法构造出一棵FBIFBIFBI树,并输出它的后序遍历序列。

解析:

  这题的题目我读了好几遍才懂,阅读能力有待提升啊(如果还是英文就炸了)。大意就是可以直接将0看作B,将1看作I,一列出来就是完全二叉树的叶子节点了,之后再往回递归就能构造出整棵树。前序遍历左右根。

#include <iostream>
#include <cstdio>
using namespace std;
int t[(1<<10) + 2];
int n, cnt;

//左右根
void dp(int x1, int x2) {

    // 将x1--x2区间的点分成两部分继续递归
    if(x2 > x1) {
        int mid = x1 + (x2-x1)/2;
        dp(x1, mid); //左子树
        dp(mid+1, x2);  // 右子树
    }
    // 根输出
    // 这里一开始可能有些难理解
    // 由于此根是左右子树的汇聚点
    // 所以左右子树的所有节点对此根有影响
    // 可以试试从1个3个节点的二叉树推推试试
    int b=0, i=0;
    for(int it=x1; it<=x2; ++it) {
        if(t[it] == 0) b += 1;
        else if(t[it] == 1) i += 1;
    }
    if(b && i) cout << 'F';
    else if(b) cout << 'B';
    else cout << 'I';
    return;
}

int main() {
    scanf("%d", &n);
    cnt = (1 << n);
    for(int i=1; i<=cnt; ++i) {
        scanf("%1d", t+i);
    }
    dp(1, cnt);
    return 0;
}

 

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int mx = 1e6 + 4;
const int mv = 2000;
struct Tree {
    int l, r, w;
}node[mx];
int ans;
inline int max(int a, int b) { return a>b ? a:b; }
int f[mx];  // 计算节点时记忆化
bool is_system = true;

// 计算子树的节点数
// 由于多次使用且树是一定的,开记忆化
// @para rt 树根的编号
int cnt_node(int rt) {
    if(rt == -1) return 0;
    if(f[rt]) return f[rt];
    else {
        f[rt] = 1; // 根节点本身占一个
        f[rt] += cnt_node(node[rt].l) + cnt_node(node[rt].r);
    }
    return f[rt];
}

// 递归判断对称性
// 由于要持续判断每一次的状态
// 递归的类型为void
// 若是状态累加、特例终止等则考虑其他类型
// @para a b 两棵树节点的编号
// @para rt a和b的父节点
void judge(int a, int b) {
    if((a==-1 && b==-1))
        return;
    else if(a==-1 || b==-1) {
        is_system = false;
        return;
    }

    // a b节点存在
    if(node[a].w != node[b].w) {
        is_system = false;
        return;
    }
    else {
        judge(node[a].l, node[b].r);
        judge(node[a].r, node[b].l);
    }
    return;
}

int n, l, r;
int main() {
    scanf("%d", &n);
    for(int i=1; i<=n; ++i)
        scanf("%d", &node[i].w);
    for(int i=1; i<=n; ++i)
        scanf("%d %d", &node[i].l, &node[i].r);

    //没有必要bfs,节点的编号全部已知且连续
    for(int now=1; now<=n; ++now) {
        judge(node[now].r, node[now].l);
        if(is_system)
            ans = max(cnt_node(now), ans);
        else
            is_system = true;
    }
    printf("%d\n", ans);
    return 0;
}
判断对称二叉树
 
#include <iostream>
#include <cstdio>
using namespace std;
string pre;

void find_r(string mid, string aft) {
    if(mid.size() == 0) return;
    char root = *(aft.rbegin());
    pre += root;
    if(aft.size() <= 1 || mid.size() <= 1) return;

    int l;
    for(l=0; l<mid.size(); ++l)
        if(mid[l] == root) break;

    find_r(mid.substr(0,l), aft.substr(0, l));
    find_r(mid.substr(l+1), aft.substr(l, aft.size()-1-l));
    return;
}
int main() {
    string mid, aft;
    cin >> mid >> aft;
    find_r(mid, aft);
    cout << pre << endl;
    return 0;
}
基础的二推1,中序后序推前序
posted @ 2019-09-02 21:37  Bankarian  阅读(665)  评论(0编辑  收藏  举报