PAT 甲级 树(一)

数叶子节点

题目

家庭关系可以用家谱树来表示,给定一个家谱树,你的任务是找出其中没有孩子的成员。

输入格式
第一行包含一个整数 N 表示树中结点总数以及一个整数 M 表示非叶子结点数。

接下来 M 行,每行的格式为:

ID K ID[1] ID[2] … ID[K]
ID 是一个两位数字,表示一个非叶子结点编号,K 是一个整数,表示它的子结点数,接下来的 K 个 ID[i] 也是两位数字,表示一个子结点的编号。

为了简单起见,我们将根结点固定设为 01。

所有结点的编号即为 01,02,03,…,31,32,33,…,N。

输出格式
输出从根结点开始,自上到下,树的每一层级分别包含多少个叶子节点。

输出占一行,整数之间用空格隔开。

数据范围
0<N<100
输入样例:
2 1
01 1 02
输出样例:
0 1
样例解释
该样例表示一棵只有 2 个结点的树,其中 01 结点是根,而 02 结点是其唯一的子节点。

因此,在根这一层级上,存在 0 个叶结点;在下一个级别上,有 1 个叶结点。

所以,我们应该在一行中输出0 1。

思想

dfs中记录深度的深度优先遍历
遍历过程中多一个参数为depth
用数组cnt[] 记录各层的叶子节点数量

void dfs(int u, int depth){
    if(h[u] == -1){
        cnt[depth]++;
        max_depth = max(depth, max_depth);
    }
    else{
        for(int i = h[u]; i != - 1; i = ne[i]){
            int j = e[i];
            dfs(j, depth + 1);
        }
    }
}

自己的代码

#include<iostream>
#include <cstring>
using namespace std;
const int N = 200; 
int h[N], e[N], ne[N], idx = 0;
int cnt[N] = {0};
int max_depth = 0;
void add(int a, int b){
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx;
    idx ++;
}
void dfs(int u, int depth){
    if(h[u] == -1){
        cnt[depth]++;
        max_depth = max(depth, max_depth);
    }
    else{
        for(int i = h[u]; i != - 1; i = ne[i]){
            int j = e[i];
            dfs(j, depth + 1);
        }
    }
}
int main(){
    int n, m, i;
    cin>>n>>m;
    memset(h, -1, sizeof h);
    for(i = 0; i < m; i ++){
        int id, k;
        cin>>id>>k;
        for(int j = 0; j < k;j ++){
            int ki;
            cin>>ki;
            add(id, ki);
        }
    }
    dfs(1, 0);
    cout<<cnt[0];
    for(i = 1; i <= max_depth; i ++) cout<<" "<<cnt[i];
}

y总的代码

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 110;

int n, m;
int h[N], e[N], ne[N], idx;
int cnt[N], max_depth;

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u, int depth)
{
    if (h[u] == -1)  // 说明u是叶子节点
    {
        cnt[depth] ++ ;
        max_depth = max(max_depth, depth);
        return;
    }

    for (int i = h[u]; ~i; i = ne[i])
        dfs(e[i], depth + 1);
}

int main()
{
    cin >> n >> m;

    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i ++ )
    {
        int id, k;
        cin >> id >> k;
        while (k -- )
        {
            int son;
            cin >> son;
            add(id, son);
        }
    }

    dfs(1, 0);

    cout << cnt[0];
    for (int i = 1; i <= max_depth; i ++ ) cout << ' ' << cnt[i];
    cout << endl;

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/279623/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

树的遍历

题目

一个二叉树,树中每个节点的权值互不相同。

现在给出它的后序遍历和中序遍历,请你输出它的层序遍历。

输入格式
第一行包含整数 N,表示二叉树的节点数。

第二行包含 N 个整数,表示二叉树的后序遍历。

第三行包含 N 个整数,表示二叉树的中序遍历。

输出格式
输出一行 N 个整数,表示二叉树的层序遍历。

数据范围
1≤N≤30
输入样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
输出样例:
4 1 6 3 5 7 2

思想

1、根据后序遍历和中序遍历重建树

int build(int il, int ir, int pl, int pr)
{
    int root = postorder[pr];
    int k = pos[root];
    if (il < k) l[root] = build(il, k - 1, pl, pl + (k - 1 - il));
    if (k < ir) r[root] = build(k + 1, ir, pl + (k - 1 - il) + 1, pr - 1);
    return root;
}

重建树会得到两个保存了左右节点的l和r数组
这里由于root可能会很大,所以用unordered_map存的

2、层次遍历

void bfs(int root)
{
    int hh = 0, tt = 0;
    q[0] = root;

    while (hh <= tt)
    {
        int t = q[hh ++ ];
        if (l.count(t)) q[ ++ tt] = l[t];
        if (r.count(t)) q[ ++ tt] = r[t];
    }

    cout << q[0];
    for (int i = 1; i < n; i ++ ) cout << ' ' << q[i];
    cout << endl;
}

自己的代码

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 50;
int a[N], b[N], n;
int h[N], e[N], ne[N], idx = 0;
void add(int a, int b){
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx;
    idx ++;
}
int find_root(int l1, int r1, int l2, int r2){
    int root_index;
    if(l1 > r1) return 0;
    int root = a[r2];
    for(int i = l1; i <= r1; i ++){
        if(b[i] == root){
            root_index = i; 
            break;
        }
    }
    int rchild = find_root(root_index + 1, r1, l2 + root_index - l1,r2 - 1);
    if(rchild) add(root, rchild);
    int lchild = find_root(l1, root_index - 1, l2, l2 + root_index - l1 - 1);
    if(lchild) add(root, lchild);
    //cout<<root<<" "<<lchild<<" "<<rchild<<endl;
    return root;
}
int main(){
    int i;
    cin>>n;
    memset(h, -1, sizeof h);
    for(i = 0; i < n; i ++) cin>>a[i];
    for(i = 0; i < n; i ++) cin>>b[i];
    int root = find_root(0, n - 1, 0, n - 1);
    queue<int> q;
    q.push(root);
    cout<<root;
    while(q.size()){
        int top = q.front();
        q.pop();
        for(i = h[top]; i != -1; i = ne[i]){
            int j = e[i];
            q.push(j);
            cout<<" "<<j;
        }
    }
    
}

y总的代码

#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>

using namespace std;

const int N = 40;

int n;
int postorder[N], inorder[N];
unordered_map<int, int> l, r, pos;
int q[N];

int build(int il, int ir, int pl, int pr)
{
    int root = postorder[pr];
    int k = pos[root];
    if (il < k) l[root] = build(il, k - 1, pl, pl + (k - 1 - il));
    if (k < ir) r[root] = build(k + 1, ir, pl + (k - 1 - il) + 1, pr - 1);
    return root;
}

void bfs(int root)
{
    int hh = 0, tt = 0;
    q[0] = root;

    while (hh <= tt)
    {
        int t = q[hh ++ ];
        if (l.count(t)) q[ ++ tt] = l[t];
        if (r.count(t)) q[ ++ tt] = r[t];
    }

    cout << q[0];
    for (int i = 1; i < n; i ++ ) cout << ' ' << q[i];
    cout << endl;
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> postorder[i];
    for (int i = 0; i < n; i ++ )
    {
        cin >> inorder[i];
        pos[inorder[i]] = i;
    }

    int root = build(0, n - 1, 0, n - 1);

    bfs(root);

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/279654/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最深的根

题目

一个无环连通图可以被视作一个树。

树的高度取决于所选取的根节点。

现在,你要找到可以使得树的高度最大的根节点。

它被称为最深的根。

输入格式
第一行包含整数 N,表示节点数量。

节点编号为 1∼N。

接下来 N−1 行,每行包含两个整数,表示两个节点之间存在一条边。

输出格式
输出最深的根的节点编号。

如果最深的根不唯一,则按照从小到大的顺序,将它们依次输出,每个占一行。

如果给定的图不是树,输出 Error: K components,其中 K 是图中连通分量的数量。

数据范围
1≤N≤104
输入样例1:
5
1 2
1 3
1 4
2 5
输出样例1:
3
4
5
输入样例2:
5
1 3
1 4
2 5
3 4
输出样例2:
Error: 2 components

思想

1、求联通块的数量——并查集

int find(int x){
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}
	int cnt = n;
    for(i = 0; i < n - 1; i ++){
        int a, b;
        cin>>a>>b;
        add(a, b);
        add(b, a);
        int ax = find(a), bx = find(b);
        if(ax != bx) {
            p[ax] = bx;
            cnt --;
        }
    }
    if(cnt != 1) cout<<"Error: "<<cnt<<" components"<<endl;

2、判断该根节点的最大深度
一般都是!st[j].但是这里用st好像会有问题
由于树使没有环的,所以不等于父节点就可以了!

int dfs(int u, int father)
{
    int depth = 0;
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (j == father) continue;
        depth = max(depth, dfs(j, u) + 1);
    }
    return depth;
}

自己的代码

//考点——求连通块——并查集、求深度——dfs

//get_depth不能用st来做,会超时。由于是树,所以是没有环的,只要不等于父节点就可以
#include <iostream>
#include <cstring>
using namespace std;
const int N = 10010, M = N * 2;
int h[N], e[M], ne[M], idx;//有些要定义成M个空间,否则会内存不够,就可能报内存不够
int depth[N], p[N];
int n;
int get_depth(int u, int father){//由于这个是无向边,如果是father-自身,自身再到father会一直循环下去,所以如果father使用过了,之后就不能再使用了
    int depth = 0;
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(j == father) continue;
        depth = max(get_depth(j, u) + 1, depth);
    }
    return depth;
}
void add(int a, int b){
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx;
    idx ++;
}
int find(int x){
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}
int main(){
    int i;
    cin>>n;
    memset(h, -1, sizeof h);
    for(i = 1; i <= n; i ++) p[i] = i;
    int cnt = n;
    for(i = 0; i < n - 1; i ++){
        int a, b;
        cin>>a>>b;
        add(a, b);
        add(b, a);
        int ax = find(a), bx = find(b);
        if(ax != bx) {
            p[ax] = bx;
            cnt --;
        }
    }
    if(cnt != 1) cout<<"Error: "<<cnt<<" components"<<endl;
    else{
        int max_depth = 0;
        for(i = 1; i <= n; i ++){
            depth[i] = get_depth(i, -1);
            max_depth = max(depth[i], max_depth);
        }
        for(i = 1; i <= n; i ++){
            if(max_depth == depth[i])
                cout<<i<<endl;
        }
    }
}

y总的代码

#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int N = 10010, M = N * 2;

int n;
int h[N], e[M], ne[M], idx;
int p[N];

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

int dfs(int u, int father)
{
    int depth = 0;
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (j == father) continue;
        depth = max(depth, dfs(j, u) + 1);
    }
    return depth;
}

int main()
{
    cin >> n;

    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i ++ ) p[i] = i;

    int k = n;
    for (int i = 0; i < n - 1; i ++ )
    {
        int a, b;
        cin >> a >> b;
        if (find(a) != find(b))
        {
            k -- ;
            p[find(a)] = find(b);
        }
        add(a, b), add(b, a);
    }

    if (k > 1) printf("Error: %d components", k);
    else
    {
        vector<int> nodes;
        int max_depth = -1;

        for (int i = 1; i <= n; i ++ )
        {
            int depth = dfs(i, -1);
            if (depth > max_depth)
            {
                max_depth = depth;
                nodes.clear();
                nodes.push_back(i);
            }
            else if (depth == max_depth)
                nodes.push_back(i);
        }

        for (auto v : nodes) cout << v << endl;
    }

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/279675/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

判断二叉搜索树

题目

二叉搜索树 (BST) 递归定义为具有以下属性的二叉树:

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
若它的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值
它的左、右子树也分别为二叉搜索树
我们将二叉搜索树镜面翻转得到的树称为二叉搜索树的镜像。

现在,给定一个整数序列,请你判断它是否可能是某个二叉搜索树或其镜像进行前序遍历的结果。

输入格式
第一行包含整数 N,表示节点数量。

第二行包含 N 个整数。

输出格式
如果给定整数序列是某个二叉搜索树或其镜像的前序遍历序列,则在第一行输出 YES,否则输出 NO。

如果你的答案是 YES,则还需要在第二行输出这棵树的后序遍历序列。

数据范围
1≤N≤1000
输入样例1:
7
8 6 5 7 10 8 11
输出样例1:
YES
5 7 6 8 11 10 8
输入样例2:
7
8 10 11 8 6 7 5
输出样例2:
YES
11 8 10 7 5 6 8
输入样例3:
7
8 6 8 5 10 9 11
输出样例3:
NO

思想

1、二叉搜索树的中序遍历是有序的,题目给了我们先序遍历,我们进行排序得到中序遍历
然后重建二叉树

type 用来表示式二叉搜索树还是其镜像
如果是镜像,则中序遍历是反着的,要从末尾往前面搜索root。
在这里插入图片描述

要注意这里是不用找到左右孩子的,所以镜像与不镜像只有在找root的时候不同

!! 在重建的时候就可以直接输出后续遍历的结果

bool build(int il, int ir, int pl, int pr, int type)
{
    if (il > ir) return true;

    int root = preorder[pl];
    int k;
    if (type == 0)
    {
        for (k = il; k <= ir; k ++ )
            if (inorder[k] == root)
                break;
        if (k > ir) return false;
    }
    else
    {
        for (k = ir; k >= il; k -- )
            if (inorder[k] == root)
                break;
        if (k < il) return false;
    }

    bool res = true;
    if (!build(il, k - 1, pl + 1, pl + 1 + (k - 1 - il), type)) res = false;
    if (!build(k + 1, ir, pl + 1 + (k - 1 - il) + 1, pr, type)) res = false;

    postorder[cnt ++ ] = root;
    return res;
}

2、 先判断是否是二叉搜索树,不是则翻转中序遍历结果,然后再进行一遍搜索

自己的代码

#include<iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int preorder[N], inorder[N], postorder[N], cnt;
bool build(int il, int ir, int pl, int pr, int type){
    if(il > ir) return true;
    int root = preorder[pl];
    int root_index;
    int i;
    if(type == 0){
        for(i = il; i <= ir; i ++){
            if(inorder[i] == root){
                root_index = i;
                break;
            }
        }
        if(i > ir ) return false;
        
    }
    else{
        for(i = ir; i >= il; i --){
            if(inorder[i] == root){
                root_index = i;
                break;
            }
        }
        if(i < il) return false;
    }
    
    int k = root_index;
    bool res = true;
    if (!build(il, k - 1, pl + 1, pl + 1 + (k - 1 - il), type)) res = false;
    if (!build(k + 1, ir, pl + 1 + (k - 1 - il) + 1, pr, type)) res = false;

    postorder[cnt ++ ] = root;//题目要求写出的是后序遍历,可以在每次最后加上这个得到,有点类似于平常写后序遍历的递归
    return res;
}
int main(){
    int n, i;
    cin>>n;
    
    for(i = 0; i < n; i ++){
        cin>>preorder[i];
        inorder[i] = preorder[i];
    }
    
    sort(inorder, inorder + n);
    
    if(build(0, n - 1, 0, n - 1, 0)){
        cout<<"YES"<<endl;
        cout<<postorder[0];
        for(i = 1; i < n; i ++) cout<<" "<<postorder[i];
    }
    else{
        //数组可以使用reverse函数进行翻转
        reverse(inorder, inorder + n);
        cnt = 0;
        if(build(0, n - 1, 0, n - 1, 1)){
            cout<<"YES"<<endl;
            cout<<postorder[0];
            for(i = 1; i < n; i ++) cout<<" "<<postorder[i];
        }
        else
            cout<<"NO"<<endl;
    }
}

y总的代码

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int n;
int preorder[N], inorder[N];
int postorder[N], cnt;

bool build(int il, int ir, int pl, int pr, int type)
{
    if (il > ir) return true;

    int root = preorder[pl];
    int k;
    if (type == 0)
    {
        for (k = il; k <= ir; k ++ )
            if (inorder[k] == root)
                break;
        if (k > ir) return false;
    }
    else
    {
        for (k = ir; k >= il; k -- )
            if (inorder[k] == root)
                break;
        if (k < il) return false;
    }

    bool res = true;
    if (!build(il, k - 1, pl + 1, pl + 1 + (k - 1 - il), type)) res = false;
    if (!build(k + 1, ir, pl + 1 + (k - 1 - il) + 1, pr, type)) res = false;

    postorder[cnt ++ ] = root;
    return res;
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ )
    {
        cin >> preorder[i];
        inorder[i] = preorder[i];
    }

    sort(inorder, inorder + n);

    if (build(0, n - 1, 0, n - 1, 0))
    {
        puts("YES");
        cout << postorder[0];
        for (int i = 1; i < n; i ++ ) cout << ' ' << postorder[i];
        cout << endl;
    }
    else
    {
        reverse(inorder, inorder + n);
        cnt = 0;
        if (build(0, n - 1, 0, n - 1, 1))
        {
            puts("YES");
            cout << postorder[0];
            for (int i = 1; i < n; i ++ ) cout << ' ' << postorder[i];
            cout << endl;
        }
        else puts("NO");
    }

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/279699/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

完全二叉搜索树

二叉搜索树 (BST) 递归定义为具有以下属性的二叉树:

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
若它的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值
它的左、右子树也分别为二叉搜索树
完全二叉树 (CBT) 定义为除最深层外的其他层的结点数都达到最大个数,最深层的所有结点都连续集中在最左边的二叉树。

现在,给定 N 个不同非负整数,表示 N 个结点的权值,用这 N 个结点可以构成唯一的完全二叉搜索树。

请你输出该完全二叉搜索树的层序遍历。

输入格式
第一行包含整数 N,表示结点个数。

第二行包含 N 个不同非负整数,表示每个结点的权值。

输出格式
共一行,输出给定完全二叉搜索树的层序遍历序列。

数据范围
1≤N≤1000,
结点权值不超过 2000。

输入样例:
10
1 2 3 4 5 6 7 8 9 0
输出样例:
6 3 8 1 5 7 9 0 2 4

思想

1、完全二叉搜索树

中序遍历已知——排序之后得到
数组结构存储树

这道题相当于模仿一遍中序遍历的递归过程,输出更改为放入对应位置

void dfs(int u, int &k){
    if(u * 2 <= n) dfs(u * 2, k);
    tr[u] = a[k ++];
    if(u * 2 + 1 <= n) dfs(u * 2 + 1, k);
}

可以以此类推,知道了树的结构且某种遍历的时候,都可以通过这种方式去重建树

2、层次遍历
直接输出数组即可

自己的代码

//完全二叉树,可以用数组的方式存储数据信息
//二叉搜索数——中序遍历是有序的

//构造完全二叉搜索树,先得到中序遍历的序列,由于是完全二叉树,按数组填入

//已知中序遍历,构造其完全二叉树,可以利用中序遍历的类似方法写,然后获得其改造好的树

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int a[N], tr[N];
int n;

void dfs(int u, int &k){
    if(u * 2 <= n) dfs(u * 2, k);
    tr[u] = a[k ++];
    if(u * 2 + 1 <= n) dfs(u * 2 + 1, k);
}
int main(){
    int  i;
    cin>>n;
    for(i = 0; i < n; i ++) cin>>a[i];
    
    sort(a, a + n);
    int k = 0;
    dfs(1, k);
    
    cout<<tr[1];
    for(i = 2; i <= n; i ++)cout<<" "<<tr[i];
}

y总的代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int n;
int w[N], tr[N];

void dfs(int u, int& k)  // 中序遍历
{
    if (u * 2 <= n) dfs(u * 2, k);
    tr[u] = w[k ++ ];
    if (u * 2 + 1 <= n) dfs(u * 2 + 1, k);
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> w[i];

    sort(w, w + n);

    int k = 0;
    dfs(1, k);

    cout << tr[1];
    for (int i = 2; i <= n; i ++ ) cout << ' ' << tr[i];
    cout << endl;

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/279716/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

再次树遍历

题目

通过使用栈可以以非递归方式实现二叉树的中序遍历。

例如,假设遍历一个如下图所示的 6 节点的二叉树(节点编号从 1 到 6)。

则堆栈操作为:push(1); push(2); push(3); pop(); pop(); push(4); pop(); pop(); push(5); push(6); pop(); pop()。

我们可以从此操作序列中生成唯一的二叉树。

你的任务是给出这棵树的后序遍历。

3.png

输入格式
第一行包含整数 N,表示树中节点个数。

树中节点编号从 1 到 N。

接下来 2N 行,每行包含一个栈操作,格式为:

Push X,将编号为 X 的节点压入栈中。
Pop,弹出栈顶元素。
输出格式
输出这个二叉树的后序遍历序列。

数据保证有解,数和数之间用空格隔开,末尾不能有多余空格。

数据范围
1≤N≤30
输入样例:
6
Push 1
Push 2
Push 3
Pop
Pop
Push 4
Pop
Pop
Push 5
Push 6
Pop
Pop
输出样例:
3 4 2 6 5 1
难度:简单
时/空限制:0.4s / 64MB
总通过数:605
总尝试数:820
来源:PAT甲级真题1086
算法标签

思想

1、先考虑我们是如何通过非递归的形式实现中序遍历的

(1) 如果有左孩子,则一直把左孩子push
(2)如果没有左孩子了,就pop,然后把右孩子push进去,重复刚才的过程

所以我们可以发现
push——前面也是push,说明是刚push进去的节点的左节点
push——前面是pop,说明是右节点,是刚pop完成的节点的右节点。
于是我们抓住这个规律去重建树

自己的代码

//关键在于根据push、 pop的关系,构造树
//根据push 和pop关系判断是左孩子还是右孩子
//push——前面也是push,说明是刚push进去的节点的左节点
//push——前面是pop,说明是右节点,是刚pop完成的节点的右节点。
#include<iostream>
#include <cstring>
#include <stack>
using namespace std;
int count = 0;
const int N = 40;
int l[N], r[N];
void postorder(int u){
    if(l[u] != -1) postorder(l[u]);
    if(r[u] != -1) postorder(r[u]);
    if(!count) cout<<u;
    else cout<<" "<<u;
    count ++;
}
int main(){
    int n;
    cin>>n;
    string before_s = "Push";
    int i, root, before_k;
    memset(l, -1, sizeof l);
    memset(r, -1, sizeof r);
    stack<int> sta;
    for(i = 0 ;i < 2 * n; i ++){
        string s;
        int k;
        cin>>s;
        if(s == "Push"){
            cin>>k;
            if(before_s == "Push"){
                if(!i) root = k;
                else {
                    before_k = sta.top();
                    l[before_k] = k;
                }
                
            }
            else if(before_s == "Pop"){
                r[before_k] = k;
            }
            sta.push(k);
        }
        else if(s == "Pop"){
            before_k = sta.top();
            sta.pop();
        }
        before_s = s;
    }
    postorder(root);
    cout<<endl;
}

y总的代码

#include <iostream>
#include <stack>

using namespace std;

const int N = 40;

int n;
int l[N], r[N];

void dfs(int u, int root)
{
    if (!u) return;

    dfs(l[u], root);
    dfs(r[u], root);

    cout << u;
    if (u != root) cout << ' ';
}

int main()
{
    cin >> n;

    int root;
    int last = 0, type;
    stack<int> stk;
    for (int i = 0; i < n * 2; i ++ )
    {
        string op;
        cin >> op;
        if (op == "Push")
        {
            int x;
            cin >> x;
            if (!last) root = x;
            else
            {
                if (type == 0) l[last] = x;
                else r[last] = x;
            }
            stk.push(x);
            last = x;
            type = 0;  // 表示push
        }
        else
        {
            last = stk.top();
            stk.pop();
            type = 1;  // 表示pop
        }
    }

    dfs(root, root);

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/279748/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

构建二叉搜索树

题目

二叉搜索树 (BST) 递归定义为具有以下属性的二叉树:

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
若它的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值
它的左、右子树也分别为二叉搜索树
给定二叉树的具体结构以及一系列不同的整数,只有一种方法可以将这些数填充到树中,以使结果树满足 BST 的定义。

请你输出结果树的层序遍历。

示例如图 1 和图 2 所示。

24c2521f-aaed-4ef4-bac8-3ff562d80a1b.jpg
在这里插入图片描述

输入格式
第一行包含一个正整数 N,表示树的结点个数。

所有结点的编号为 0∼N−1,并且编号为 0 的结点是根结点。

接下来 N 行,第 i 行(从 0 计数)包含结点 i 的左右子结点编号。如果该结点的某个子结点不存在,则用 −1 表示。

最后一行,包含 N 个不同的整数,表示要插入树中的数值。

输出格式
输出结果树的层序遍历序列。

数据范围
1≤N≤100
输入样例:
9
1 6
2 3
-1 -1
-1 4
5 -1
-1 -1
7 -1
-1 8
-1 -1
73 45 11 58 82 25 67 38 42
输出样例:
58 25 82 11 38 67 45 73 42
难度:中等
时/空限制:0.2s / 64MB
总通过数:1015
总尝试数:1201
来源:PAT甲级真题1099
算法标签

思想

这道题和上面那道题类似,这种已知树的结构的,直接根据中序遍历填充进去即可

自己的代码

#include<iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 110;
int l[N], r[N], a[N], tr[N];
void inorder(int u, int &k){
    if(l[u] != -1) inorder(l[u], k);
    tr[u] = a[k ++];
    if(r[u] != -1) inorder(r[u], k);
}
int main(){
    int n, i;
    cin >> n;
    for(i = 0; i < n; i ++)
        cin>>l[i]>>r[i];
    for(i = 0; i < n; i ++) cin>>a[i];
    sort(a, a + n);
    
    int k = 0;
    inorder(0, k);
    
    queue<int> q;
    q.push(0);
    cout<<tr[0];
    while(q.size()){
        int top = q.front();
        q.pop();
        if(l[top] != -1) {
            q.push(l[top]);
            cout<<" "<<tr[l[top]];
        }
        if(r[top] != -1) {
            q.push(r[top]);
            cout<<" "<<tr[r[top]];
        }
    }
    cout<<endl;
    
}

y总的代码

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 110;

int n;
int l[N], r[N];
int a[N], w[N];
int q[N];

void dfs(int u, int& k)
{
    if (u == -1) return;

    dfs(l[u], k);
    w[u] = a[k ++ ];
    dfs(r[u], k);
}

void bfs()
{
    int hh = 0, tt = 0;
    q[0] = 0;

    while (hh <= tt)
    {
        int t = q[hh ++ ];
        if (l[t] != -1) q[ ++ tt] = l[t];
        if (r[t] != -1) q[ ++ tt] = r[t];
    }

    cout << w[q[0]];
    for (int i = 1; i < n; i ++ ) cout << ' ' << w[q[i]];
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> l[i] >> r[i];
    for (int i = 0; i < n; i ++ ) cin >> a[i];
    sort(a, a + n);

    int k = 0;
    dfs(0, k);
    bfs();

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/279760/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

反转二叉树

题目

以下是来自 Max Howell @twitter 的内容:

谷歌:我们的百分之九十的工程师都使用你编写的软件,但是你连在白板上反转二叉树都做不到,还是滚吧。
现在,请你证明你会反转二叉树。

输入格式
第一行包含一个整数 N,表示树的结点数量。

所有结点编号从 0 到 N−1。

接下来 N 行,每行对应一个 0∼N−1 的结点,给出该结点的左右子结点的编号,如果该结点的某个子结点不存在,则用 − 表示。

输出格式
输出反转后二叉树的层序遍历序列和中序遍历序列,每个序列占一行。

相邻数字之间用空格隔开,末尾不得有多余空格。

数据范围
1≤N≤10
输入样例:
8
1 -

0 -
2 7

5 -
4 6
输出样例:
3 7 2 6 4 0 5 1
6 5 7 4 3 2 0 1
难度:中等
时/空限制:0.4s / 64MB
总通过数:612
总尝试数:753
来源:PAT甲级真题1102
算法标签

思想

1、二叉树的反转

void dfs_reverse(int u)
{
    if (u == -1) return;

    dfs_reverse(l[u]);
    dfs_reverse(r[u]);
    swap(l[u], r[u]);
}

自己的代码


#include <iostream>
#include <queue>
#include <sstream>
using namespace std;
const int N = 20;
int l[20], r[20], a[20];
bool st[N];
int count = 0;
void inorder(int u){
    if(r[u] != -1) inorder(r[u]);
    if(!count) cout<<u;
    else cout<<" "<<u;
    count ++;
    if(l[u] != -1) inorder(l[u]);
}
int main(){
    int n, i;
    cin>>n;
    for(i = 0; i < n; i ++){
        string a, b;
        cin>>a>>b;
        if(a == "-") l[i] = -1;
        else {
            stringstream sstr;
            sstr << a;
            int aa;
            sstr >> aa;
            l[i] = aa;
            st[aa] = true;
        }
        if(b == "-") r[i] = -1;
        else {
            stringstream sstr;
            sstr << b;
            int bb;
            sstr >> bb;
            r[i] = bb;
            st[bb] = true;
        }
    }
    int root;
    for(i = 0; i < n; i ++){
        if(st[i] == false) 
            root = i;
    }
    //层序遍历
    queue<int> q;
    q.push(root);
    cout<<root;
    while(q.size()){
        int top = q.front();
        q.pop();
        if(r[top] != -1) {
            q.push(r[top]);
            cout<<" "<<r[top];
        }
        if(l[top] != -1) {
            q.push(l[top]);
            cout<<" "<<l[top];
        }
    }
    cout<<endl;
    //反转二叉树
    inorder(root);
    cout<<endl;
}

y总的代码

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 15;

int n;
int l[N], r[N];
int q[N];
bool has_father[N];

void dfs_reverse(int u)
{
    if (u == -1) return;

    dfs_reverse(l[u]);
    dfs_reverse(r[u]);
    swap(l[u], r[u]);
}

void bfs(int root)
{
    int hh = 0, tt = 0;
    q[0] = root;
    while (hh <= tt)
    {
        int t = q[hh ++ ];
        if (l[t] != -1) q[ ++ tt] = l[t];
        if (r[t] != -1) q[ ++ tt] = r[t];
    }

    cout << q[0];
    for (int i = 1; i < n; i ++ ) cout << ' ' << q[i];
    cout << endl;
}

void dfs(int u, int& k)
{
    if (u == -1) return;
    dfs(l[u], k);

    cout << u;
    if ( ++ k != n) cout << ' ';

    dfs(r[u], k);
}

int main()
{
    cin >> n;

    memset(l, -1, sizeof l);
    memset(r, -1, sizeof r);
    for (int i = 0; i < n; i ++ )
    {
        char lc, rc;
        cin >> lc >> rc;
        if (lc != '-') l[i] = lc - '0', has_father[l[i]] = true;
        if (rc != '-') r[i] = rc - '0', has_father[r[i]] = true;
    }

    int root = 0;
    while (has_father[root]) root ++ ;

    dfs_reverse(root);
    bfs(root);

    int k = 0;
    dfs(root, k);

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/279781/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

完全二叉树

题目

给定一个树,请你判断它是否是完全二叉树。

输入格式
第一行包含整数 N,表示树的结点个数。

树的结点编号为 0∼N−1。

接下来 N 行,每行对应一个结点,并给出该结点的左右子结点的编号,如果某个子结点不存在,则用 - 代替。

输出格式
如果是完全二叉树,则输出 YES 以及最后一个结点的编号。

如果不是,则输出 NO 以及根结点的编号。

数据范围
1≤N≤20
输入样例1:
9
7 8

0 1
2 3
4 5

输出样例1:
YES 8
输入样例2:
8

4 5
0 6

2 3

  • 7

输出样例2:
NO 1
难度:简单
时/空限制:0.4s / 64MB
总通过数:631
总尝试数:1321
来源:PAT甲级真题1110
算法标签

思想

y总的方法是去记录maxk是不是等于n

自己的代码

//需要注意的一个
//当只有个位数的时候,可以a - '0',当多位数的时候不可以,要进行int转换
//所以,一开始的时候要看清楚n的大小
//string 和int转换可以用stoi()函数
//int 到string,用itos
#include <iostream>
#include <cstring>
#include <queue>
#include <sstream>
using namespace std;
const int N = 30;
int l[N], r[N], tr[N];
bool st[N];
int k = 0;
void dfs(int u, int k){
    tr[u] = k;
    if(l[k] != -1) dfs(2 * u, l[k]);
    if(r[k] != -1) dfs(2 * u + 1, r[k]);
}
int main(){
    
    int n, i;
    cin>>n;
    for(i = 0; i < n; i ++){
        string a, b;
        cin>>a>>b;
        if(a == "-") l[i] = -1;
        else {
            stringstream sstr;
            sstr << a;
            int aa;
            sstr >> aa;
            l[i] = aa;
            st[aa] = true;
        }
        if(b == "-") r[i] = -1;
        else {
            stringstream sstr;
            sstr << b;
            int bb;
            sstr >> bb;
            r[i] = bb;
            st[bb] = true;
        }
    }
    int root;
    for(i = 0; i < n; i ++){
        if(st[i] == false) 
            root = i;
    }
    memset(tr, -1, sizeof tr);
    dfs(1, root);
    bool flag = true;
    for(i = 1; i <= n; i ++){
        //cout<<tr[i]<<endl;
        if(tr[i] == -1) {
            //cout<<i<<endl;
            flag = false;
            break;
        }
    }
    if(flag) cout<<"YES "<<tr[n]<<endl;
    else cout<<"NO "<<root<<endl;
}

y总的代码

#include <cstring>
#include <iostream>

using namespace std;

const int N = 25;

int n;
int l[N], r[N];
bool has_father[N];
int maxk, maxid;

void dfs(int u, int k)
{
    if (u == -1) return;

    if (k > maxk)
    {
        maxk = k;
        maxid = u;
    }

    dfs(l[u], k * 2);
    dfs(r[u], k * 2 + 1);
}

int main()
{
    memset(l, -1, sizeof l);
    memset(r, -1, sizeof r);

    cin >> n;
    for (int i = 0; i < n; i ++ )
    {
        string a, b;
        cin >> a >> b;
        if (a != "-") l[i] = stoi(a), has_father[l[i]] = true;
        if (b != "-") r[i] = stoi(b), has_father[r[i]] = true;
    }

    int root = 0;
    while (has_father[root]) root ++ ;

    dfs(root, 1);

    if (maxk == n) printf("YES %d\n", maxid);
    else printf("NO %d\n", root);

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/283547/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值