Loading

PTA 21级数据结构与算法实验5—树和二叉树

7-1 还原二叉树

给定一棵二叉树的先序遍历序列和中序遍历序列,要求计算该二叉树的高度。

输入格式:

输入首先给出正整数N(≤50),为树中结点总数。下面两行先后给出先序和中序遍历序列,均是长度为N的不包含重复英文字母(区别大小写)的字符串。

输出格式:

输出为一个整数,即该二叉树的高度。

输入样例:

9
ABDFGHIEC
FDHGIBEAC

输出样例:

5

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;

struct node {
    char data;
    node *l, *r;
};

char pre[N], mid[N];

node *buildTree(char pre[], char mid[], int len) {
    if (len == 0) return NULL;

    int k;
    for (int i = 0; i < len; i++) {
        if (pre[0] == mid[i]) {
            k = i;
            break;
        }
    }

    node *root = new node;
    root -> data = pre[0];

    root -> l = buildTree(pre + 1, mid, k);
    root -> r = buildTree(pre + 1 + k, mid + 1 + k, len - 1 - k);

    return root;
}

int high(node *root) {
    int h = 0;
    if (root) h = max(high(root -> l), high(root -> r)) + 1;
    return h;
}

int main() {
    int n;
    cin >> n;
    cin >> pre >> mid;
    
    node *root = buildTree(pre, mid, n);
    
    cout << high(root) << endl;
    return 0;
}

7-2 朋友圈

某学校有N个学生,形成M个俱乐部。每个俱乐部里的学生有着一定相似的兴趣爱好,形成一个朋友圈。一个学生可以同时属于若干个不同的俱乐部。根据“我的朋友的朋友也是我的朋友”这个推论可以得出,如果A和B是朋友,且B和C是朋友,则A和C也是朋友。请编写程序计算最大朋友圈中有多少人。

输入格式:

输入的第一行包含两个正整数N(≤30000)和M(≤1000),分别代表学校的学生总数和俱乐部的个数。后面的M行每行按以下格式给出1个俱乐部的信息,其中学生从1~N编号:

第i个俱乐部的人数Mi(空格)学生1(空格)学生2 … 学生Mi

输出格式:

输出给出一个整数,表示在最大朋友圈中有多少人。

输入样例:

7 4
3 1 2 3
2 1 4
3 5 6 7
1 6

思路 : 并查集

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 3e4 + 10;
int f[N], s[N];

int Find(int x) {
    while (f[x] != x) x = f[x];
    return x;
}

void F(int x, int y) {
    int xx = Find(x), yy = Find(y);

    if (xx != yy) {
        if (s[xx] > s[yy]) swap(xx, yy);
        f[xx] = yy;
        s[yy] += s[xx];
    }
}


int main() {
    int n, m;
    cin >> n >> m;
    fill(s + 1, s + n + 1, 1);
    for (int i = 1; i <= n; i++) f[i] = i;

    int x, y, len;
    while (m--) {
        cin >> len;
        cin >> x;
        for (int i = 1; i < len; i++) {
            cin >> y;
            F(x, y);
        }
    }
    int Max = -1;

    for (int i = 1; i <= n; i++) 
        Max = max(Max, s[i]);
    cout << Max << endl;
    return 0;
}

7-3 修理牧场

农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数L**i个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是L**i的总和。

但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。

请编写程序帮助农夫计算将木头锯成N块的最少花费。

输入格式:

输入首先给出正整数N(≤104),表示要将木头锯成N块。第二行给出N个正整数(≤50),表示每段木块的长度。

输出格式:

输出一个整数,即将木头锯成N块的最少花费。

输入样例:

8
4 5 1 2 1 3 1 1

输出样例:

49

代码

#include<bits/stdc++.h>
using namespace std;

// C++ STL库, 优先队列
priority_queue<int, vector<int>, greater<int>> Q;

int main() {
    int n, x;
    cin >> n;
    
    for (int i = 0; i < n; i++) {
        cin >> x;
        Q.push(x);
    }

    int y;
    int sum = 0;
    while (!Q.empty() && Q.size() != 1) {
        x = Q.top();
        Q.pop();
        y = Q.top();
        Q.pop();
        sum += x + y;
        Q.push(x + y);
    }

    cout << sum << endl;
    return 0;
}

7-4 玩转二叉树

给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。

输入格式:

输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其中序遍历序列。第三行给出其前序遍历序列。数字间以空格分隔。

输出格式:

在一行中输出该树反转后的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:

7
1 2 3 4 5 6 7
4 1 3 2 6 5 7

输出样例:

4 6 1 7 5 3 2

代码

#include<bits/stdc++.h>
using namespace std;
 
int pre[111], mid[111];
int ans[111];
int cnt = 0;
 
struct node {
    int data;
    node *l, *r;
};
 
node *buildTree(int pre[], int mid[], int len) {
    if (len == 0) return NULL;
 
    int k;
    for (int i = 0; i < len; i++) {
        if (pre[0] == mid[i]) {
            k = i;
            break;
        }
    }
    node *root = new node;
    root -> data = pre[0];
 
    root -> l = buildTree(pre + 1, mid, k);
    root -> r = buildTree(pre + 1 + k, mid + 1 + k, len - 1 - k);
 
    return root;
}
 
void change(node *root) {
    if (root) {
        if (!(!root -> l && !root -> r)) swap(root -> l, root -> r);
        change(root -> l);
        change(root -> r);
    }
}
 
void levorder(node *root) {
    queue<node *> Q;
    if (root) Q.push(root);
 
    while (!Q.empty()) {
        ans[cnt++] = Q.front() -> data;
 
        if (Q.front()->l) Q.push(Q.front()->l);
        if (Q.front()->r) Q.push(Q.front()->r);
 
        Q.pop();
    }
}
 
int main() {
    int n;
    cin >> n;
 
    for (int i = 0; i < n; i++) cin >> mid[i];
    for (int i = 0; i < n; i++) cin >> pre[i];
 
    node *root = buildTree(pre, mid, n);
 
    change(root);
 
    levorder(root);
 
    for (int i = 0; i < cnt; i++) {
        if (i == 0) cout << ans[i];
        else cout << " " << ans[i];
    }
 
    return 0;
}

7-5 根据后序和中序遍历输出先序遍历

本题要求根据给定的一棵二叉树的后序遍历和中序遍历结果,输出该树的先序遍历结果。

输入格式:

第一行给出正整数N(≤30),是树中结点的个数。随后两行,每行给出N个整数,分别对应后序遍历和中序遍历结果,数字间以空格分隔。题目保证输入正确对应一棵二叉树。

输出格式:

在一行中输出Preorder: 以及该树的先序遍历结果。数字间有1个空格,行末不得有多余空格。

输入样例:

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

输出样例:

Preorder: 4 1 3 2 6 5 7

代码

#include<bits/stdc++.h>
using namespace std;

int mid[111], pos[111];

struct node {
    int data;
    node *l, *r;
};

node *buildTree(int pos[], int mid[], int len) {
    if (len == 0) return NULL;

    int k;
    for (int i = 0; i < len; i++) {
        if (mid[i] == pos[len - 1]) {
            k = i;
            break;
        }
    }

    node *root = new node;
    root -> data = mid[k];

    root -> l = buildTree(pos, mid, k);
    root -> r = buildTree(pos + k, mid + 1 + k, len - 1 - k);
    
    return root;
}

void preorder(node *root) {
    if (root) {
        cout << " " << root -> data;
        preorder(root->l);
        preorder(root->r);
    }
}

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

    for (int i = 0; i < n; i++) cin >> pos[i];
    for (int i = 0; i < n; i++) cin >> mid[i];

    node *root = buildTree(pos, mid, n);

    cout << "Preorder:";
    preorder(root);

    return 0;
}

7-6 完全二叉树的层序遍历

一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是完美二叉树。对于深度为 D 的,有 N 个结点的二叉树,若其结点对应于相同深度完美二叉树的层序遍历的前 N 个结点,这样的树就是完全二叉树

给定一棵完全二叉树的后序遍历,请你给出这棵树的层序遍历结果。

输入格式:

输入在第一行中给出正整数 N(≤30),即树中结点个数。第二行给出后序遍历序列,为 N 个不超过 100 的正整数。同一行中所有数字都以空格分隔。

输出格式:

在一行中输出该树的层序遍历序列。所有数字都以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

8
91 71 2 34 10 15 55 18

输出样例:

18 34 55 71 2 10 15 91

代码

#include<bits/stdc++.h>
using namespace std;

int a[111], b[111];
int n;
int cnt = 0;

void f(int x) {
    if (x <= n) {
        f(x * 2);
        f(x * 2 + 1);
        b[x] = a[cnt++];
    }
}

int main() {
    cin >> n;

    for (int i = 0; i < n; i++) cin >> a[i];

    f(1);

    for (int i = 1; i <= n; i++) {
        if (i == 1) cout << b[i];
        else cout << " " << b[i];
    }
    return 0;
}

7-7 列出叶结点

对于给定的二叉树,本题要求你按从上到下、从左到右的顺序输出其所有叶节点。

输入格式:

首先第一行给出一个正整数 N(≤10),为树中结点总数。树中的结点从 0 到 N−1 编号。随后 N 行,每行给出一个对应结点左右孩子的编号。如果某个孩子不存在,则在对应位置给出 "-"。编号间以 1 个空格分隔。

输出格式:

在一行中按规定顺序输出叶节点的编号。编号间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

8
1 -
- -
0 -
2 7
- -
- -
5 -
4 6

输出样例:

4 1 5

代码

#include<bits/stdc++.h>
using namespace std;

struct node {
    int l, r;
}p[111];
vector<int> ans;
bool vis[111];

void levorder(int root) {
    queue<int> Q;

    if (root != -1) Q.push(root);

    while (!Q.empty()) {
        int x = Q.front();
        if (p[x].l == -1 && p[x].r == -1) ans.push_back(x);
        else {
            if (p[x].l != -1) Q.push(p[x].l);
            if (p[x].r != -1) Q.push(p[x].r);
        }
        Q.pop();
    }
}


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

    memset(p, -1, sizeof p);

    char x, y;
    getchar();
    for (int i = 0; i < n; i++) {
        scanf("%c %c", &x, &y);

        if (x != '-') p[i].l = x - '0', vis[p[i].l] = 1;
        if (y != '-') p[i].r = y - '0', vis[p[i].r] = 1;

        getchar();
    }
    int root = -1;

    for (int i = 0; i < n; i++) {
        if (!vis[i]) {
            root = i;
            break;
        }
    }
    
    levorder(root);
    
    for (int i = 0; i < ans.size(); i++) {
        if (i == 0) cout << ans[i];
        else cout << " " << ans[i];
    }
    return 0;
}

7-8 部落

在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈。我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是否属于同一个部落。

输入格式:

输入在第一行给出一个正整数N(≤104),是已知小圈子的个数。随后N行,每行按下列格式给出一个小圈子里的人:

K P[1] P[2] ⋯ P[K]

其中K是小圈子里的人数,P[i](i=1,⋯,K)是小圈子里每个人的编号。这里所有人的编号从1开始连续编号,最大编号不会超过104。

之后一行给出一个非负整数Q(≤104),是查询次数。随后Q行,每行给出一对被查询的人的编号。

输出格式:

首先在一行中输出这个社区的总人数、以及互不相交的部落的个数。随后对每一次查询,如果他们属于同一个部落,则在一行中输出Y,否则输出N

输入样例:

4
3 10 1 2
2 3 4
4 1 5 7 8
3 9 6 4
2
10 5
3 7

输出样例:

10 2
Y
N

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 1e4 + 10;
int f[N];
bool v[N];

int Find(int x) {
    while (f[x] != x) x = f[x];
    return x;
}

void F(int x, int y) {
    int xx = Find(x), yy = Find(y);
    if (xx != yy) f[xx] = yy;
}

int main() {
    int n;
    cin >> n;
    
    memset(v, 0, sizeof(v));
    
    for (int i = 1; i <= N - 10; i++) f[i] = i;
    
    int cnt = 0;
    int t, x, y;

    while (n--) {
        scanf("%d %d", &t, &x);

        if (!v[x]) v[x] = 1, cnt++;

        for (int i = 1; i < t; i++) {
            scanf("%d", &y);
            if (!v[y]) v[y] = 1, cnt++;
            F(x, y);
        }
    }

    int num = 0;
    for (int i = 1; i <= N - 10; i++) {
        if (v[i] && f[i] == i) num++;
    }

    cout << cnt << " " << num << endl;
    
    int m;
    cin >> m;
    
    while (m--) {
        scanf("%d %d", &x, &y);
        
        if (Find(x) == Find(y)) printf("Y\n");
        else printf("N\n");
    }
    return 0;
}

7-9 建立与遍历二叉树

以字符串的形式定义一棵二叉树的先序序列,若字符是‘#’, 表示该二叉树是空树,否则该字符是相应结点的数据元素。读入相应先序序列,建立二叉链式存储结构的二叉树,然后中序遍历该二叉树并输出结点数据。

输入格式:

字符串形式的先序序列(即结点的数据类型为单个字符)

输出格式:

中序遍历结果

输入样例:

ABC##DE#G##F###

输出样例:

CBEGDFA

代码

#include<bits/stdc++.h>
using namespace std;

struct node {
    char data;
    node *l, *r;
};

char st[100010];
int cnt = 0;

node *buildTree(node *root) {
    if (st[cnt] == '#') {
        root = NULL;
        cnt++;
    }
    else {
        root = new node;
        root->data = st[cnt++];
        root->l = buildTree(root->l);
        root->r = buildTree(root->r);
    }
    return root;
}

void midorder(node *root) {
    if (root) {
        midorder(root->l);
        cout << root->data;
        midorder(root->r);
    }
}

int main() {
    cin >> st;
    node *root;
    root = buildTree(root);
    
    midorder(root);
    return 0;
}

7-10 交换二叉树中每个结点的左孩子和右孩子

以二叉链表作为二叉树的存储结构,交换二叉树中每个结点的左孩子和右孩子。

输入格式:

输入二叉树的先序序列。

提示:一棵二叉树的先序序列是一个字符串,若字符是‘#’,表示该二叉树是空树,否则该字符是相应结点的数据元素。

输出格式:

输出有两行:

第一行是原二叉树的中序遍历序列;

第二行是交换后的二叉树的中序遍历序列。

输入样例:

ABC##DE#G##F###

输出样例:

CBEGDFA
AFDGEBC

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
struct node {
    char data;
    node *l, *r;
};
int cnt = 0;
char st[N];

node *buildTree(node *root) {
    if (st[cnt] == '#') {
        root = NULL;
        cnt++;
    } else {
        root = new node;
        root -> data = st[cnt++];

        root -> l = buildTree(root -> l);
        root -> r = buildTree(root -> r);
    }
    return root;
}

void change(node *root) {
    if (root) {
        swap(root -> l, root -> r);
        change(root -> l);
        change(root -> r);
    }
}

void midorder(node *root) {
    if (root) {
        midorder(root -> l);
        cout << root -> data;
        midorder(root -> r);
    }
}

int main() {
    cin >> st;
    node *root;
    root = buildTree(root);

    midorder(root);
    cout << endl;

    change(root);

    midorder(root);
    cout << endl;
    return 0;
}

7-11 树的遍历

给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。

输入格式:

输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其后序遍历序列。第三行给出其中序遍历序列。数字间以空格分隔。

输出格式:

在一行中输出该树的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

输出样例:

4 1 6 3 5 7 2

代码

#include <bits/stdc++.h>
using namespace std;

int mid[111], pos[111];
vector<int> ans;
struct node {
    int data;
    node *l, *r;
};

node *buildTree(int pos[], int mid[], int len) {
    if (len == 0) return NULL;

    int k;
    for (int i = 0; i < len; i++) {
        if (mid[i] == pos[len - 1]) {
            k = i;
            break;
        }
    }

    node *root = new node;
    root->data = mid[k];

    root->l = buildTree(pos, mid, k);
    root->r = buildTree(pos + k, mid + 1 + k, len - 1 - k);

    return root;
}

void levorder(node *root) {
    queue<node *> Q;

    if (root) Q.push(root);

    while (!Q.empty()) {
        ans.push_back(Q.front() -> data);

        if (Q.front()->l) Q.push(Q.front()->l);
        if (Q.front()->r) Q.push(Q.front()->r);
        Q.pop();
    }
}

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

    for (int i = 0; i < n; i++) cin >> pos[i];
    for (int i = 0; i < n; i++) cin >> mid[i];

    node *root;
    root = buildTree(pos, mid, n);

    levorder(root);

    for (int i = 0; i < ans.size(); i++) {
        if (i == 0) cout << ans[i];
        else cout << " " << ans[i];
    }

    return 0;
}
posted @ 2022-09-30 20:14  Oneway`  阅读(285)  评论(1编辑  收藏  举报