【刷题】树专题——2019年浙大上机模拟(晴神)

版权声明:转载先点个赞嘛~~ https://blog.csdn.net/qq_39011762/article/details/88355740

3月6日 树专题

A - 二叉查找树的层序遍历

Problem Description

给定一棵二叉查找树(BST)的插入序列,输出它的层序遍历序列。

Input

第一行给出一个正整数N(1<=N<=10^5),表示二叉查找树的结点个数。

第二行包含N个唯一的整数,每个数都在[0,10^9]范围内。数据为随机生成。

Output

输出一行用空格隔开的二叉查找树的层序遍历序列。行末不允许有多余的空格。

Sample Input

5
3 1 2 4 5

Sample Output

扫描二维码关注公众号,回复: 5468703 查看本文章
3 1 4 2 5

分析:简单题。先建立bst,然后层序输出

#include<bits/stdc++.h>
using namespace std;
struct node{
    int v;
    node *lchild, *rchild;
};
typedef node* pnode;
pnode newNode(int x){
    pnode temp = new node;
    temp->v = x;
    temp->lchild = temp->rchild = NULL;
    return temp;
}
void Insert(pnode &root, int x){
    if(root == NULL){
        root = newNode(x);
        return;
    }
    if(x < root->v)Insert(root->lchild, x);
    else Insert(root->rchild, x);
}
void level(pnode root){
    queue<pnode>q;
    q.push(root);
    int flag = 0;
    while(!q.empty()){
        pnode temp = q.front();
        q.pop();
        if(flag == 1)printf(" ");
        printf("%d", temp->v);
        flag = 1;
        if(temp->lchild != NULL)q.push(temp->lchild);
        if(temp->rchild != NULL)q.push(temp->rchild);
    }
    printf("\n");
}
int main(){
    pnode root = NULL;
    int n, temp;
    scanf("%d", &n);
    for(int i = 0;i < n; ++i){
        scanf("%d", &temp);
        Insert(root, temp);
    }
    level(root);
    return 0;
}

B - 进击的二叉查找树

Problem Description

给定1~N的两个排列,使用这两个排列分别构建两棵二叉查找树(也就是通过往一棵空树中依次插入序列元素的构建方式)。如果这两棵二叉查找树完全相同,那么输出YES;否则输出NO。之后,输出第一个排列对应的二叉查找树的后序序列、层序序列。

Input

每个输入文件中一组数据。

第一行1个正整数N(1<=N<=30),表示二叉查找树中的结点个数。

接下来两行,代表1~N的两个排列。

Output

如果两个排列代表的二叉查找树完全相同,那么输出一行YES,否则输出一行NO。

接下来两行分别输出第一个排列对应的二叉查找树的后序序列、层序序列,整数之间用空格隔开。

每行末尾不允许有多余的空格。

Sample Input

5
4 2 1 3 5
4 5 2 3 1

Sample Output

YES
1 3 2 5 4
4 2 5 1 3

分析:相同指树的结构和对应节点的值都要相同,递归判断即可

#include<bits/stdc++.h>
using namespace std;
struct node{
    int v;
    node *lchild, *rchild;
};
typedef node* pnode;
pnode newNode(int x){
    pnode temp = new node;
    temp->v = x;
    temp->lchild = temp->rchild = NULL;
    return temp;
}
void Insert(pnode &root, int x){
    if(root == NULL){
        root = newNode(x);
        return;
    }
    if(x < root->v)Insert(root->lchild, x);
    else Insert(root->rchild, x);
}
bool judge(pnode root1, pnode root2){
    if(root1 == NULL && root2 == NULL)return true;//空树相等
    if(root1 == NULL && root2 != NULL)return false;//空和不空不相等
    if(root1 != NULL && root2 == NULL)return false;
    if(root1->v != root2->v)return false;//节点值不等则树不相等
    if(root1->lchild == NULL && root2->lchild != NULL)return false;//树的结构不想同则不相等
    if(root1->lchild != NULL && root2->lchild == NULL)return false;
    if(root1->rchild == NULL && root2->rchild != NULL)return false;
    if(root1->rchild != NULL && root2->rchild == NULL)return false;
    return judge(root1->lchild, root2->lchild) && judge(root1->rchild, root2->rchild);
}
vector<int>ans;
void post(pnode root){
    if(root == NULL)return;
    post(root->lchild);
    post(root->rchild);
    ans.push_back(root->v);
}
void level(pnode root){
    queue<pnode>q;
    q.push(root);
    int flag = 0;
    while(!q.empty()){
        pnode temp = q.front();
        q.pop();
        if(flag == 1)printf(" ");
        printf("%d", temp->v);
        flag = 1;
        if(temp->lchild != NULL)q.push(temp->lchild);
        if(temp->rchild != NULL)q.push(temp->rchild);
    }
    printf("\n");
}
int main(){
    pnode root1 = NULL, root2 = NULL;
    int n, temp;
    scanf("%d", &n);
    for(int i = 0;i < n; ++i){
        scanf("%d", &temp);
        Insert(root1, temp);
    }
    for(int i = 0;i < n; ++i){
        scanf("%d", &temp);
        Insert(root2, temp);
    }
    bool flag = judge(root1, root2);
    if(flag == true)printf("YES\n");
    else printf("NO\n");
    post(root1);
    for(int i = 0; i < ans.size(); ++i){
        if(i > 0)printf(" ");
        printf("%d", ans[i]);
    }
    printf("\n");
    level(root1);
    return 0;
}

C - 宇宙树

Problem Description

在传承至今的典籍中认为,每个宇宙都是十种宇宙中的一种:炎之宇宙、光之宇宙、冰之宇宙、风之宇宙、雷之宇宙、土之宇宙、水之宇宙、木之宇宙、钢之宇宙、暗之宇宙,各表示了一个宇宙内部的主要元素。在最初始的大爆炸之后,在混沌中产生了最初的若干个宇宙,这些宇宙的类型是以上十种之一,可能相同,可能不同。从这些宇宙开始,每过一个纪元,各个宇宙都有可能孕育出多个新的宇宙(类型可能相同,可能不同)。可以预见,从任何一个初始宇宙开始,在经过若干个纪元之后,就有可能得到非常多新的宇宙。

在更高的维度上,一种名为“孵化者”的生物在观测着这些宇宙。“孵化者”们把宇宙之间的孕育关系表示成一棵树,称为“宇宙树”。下图便是一棵可能的宇宙树,其中用0~9来分别代表十种宇宙的类型,那么这棵宇宙树便是从一个类型为2的初始宇宙开始,孕育出了类型分别为6、1、2的三个宇宙,而类型为6的那个宇宙又孕育出了类型为0和9的两个宇宙,类型为2的那个非初始宇宙孕育出了类型为5的宇宙。

img

“孵化者”们发现,一个宇宙与它孕育出的新宇宙之间并不是完全没有联系的,旧宇宙的信息会被携带一部分到新的宇宙中。它们猜想,既然宇宙类型的生成是“随机”的,那么在很多很多个纪元之后,再孕育出的新宇宙,其宇宙内部很可能会是十种元素达到平衡的状态。“孵化者”们认为,可以观测就可以干涉,可以干涉就可以控制,那么研究当前这些宇宙的类型便十分重要。

“孵化者”们认为,每一个处于宇宙树叶结点的宇宙,都是其所在分支上的最新宇宙,可以把这些叶结点的宇宙称为“新宇宙”。根据它们的猜想,这些宇宙的类型虽然从主要元素上来说属于十种中的一种,但如果把它们划分得更细致的话,是可以得到更准确的类型信息的。“孵化者”们把一个宇宙的“准确类型”定义为:从单棵宇宙树的初始宇宙开始、沿着孕育关系向下到达该宇宙的过程中形成的路径上的各类型的拼接结果就是这个宇宙的“准确类型”。例如在上图中,主类型为0的宇宙的“准确类型”是260,主类型为9的的宇宙的“准确类型”是269,主类型为1的的宇宙的“准确类型”是21,主类型为5的宇宙的“准确类型”是225。

为了研究宇宙类型的混合结果,“孵化者”们把一棵宇宙树的所有“新宇宙”的“准确类型”求和,并将得到的结果称为这棵宇宙树的类型,这有助于它们进行下一步研究。例如对上面的图来说,这棵宇宙树的类型便是260+269+21+225=775。

现在“孵化者”们想知道,当前存在的所有宇宙树的类型分别是什么。

Input

每个输入文件中一组数据。
对每组数据,第一行为两个正整数N(1 <= N <= 10000, 0 <= M < N),分别表示宇宙的个数与孕育关系的条数。假设宇宙的编号为0~N-1,且每棵宇宙树的初始宇宙的编号一定是这棵宇宙树中最小的。
接下来一行N个整数,其中第i个整数表示编号为i-1的宇宙的主类型。
接下来M行,每行两个整数u和v(0 <= u < N, 0 <= v < N, u != v),表示编号为u的宇宙孕育了编号为v的宇宙。数据保证同一对(u,v)只会出现一次。

Output

第一行一个整数,表示宇宙树的个数K。
第二行K个整数,分别表示这K个宇宙树的类型。输出顺序为,如果一棵宇宙树的初始宇宙编号比其他宇宙树的小,那么就优先输出。
行末不允许有多余的空格。

Sample Input

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

Sample Output

2
775 36

分析:遍历什么的是常规操作,需要注意的是节点总数是10000,形成的路径的拼成的数字远远大于long long int的范围,需要用大数存储,或者用string进行加

注意:本题不能用bgn存储,因为bgn中的num数组要开到10000,内存会不足

#include<bits/stdc++.h>
using namespace std;
const int nmax = 10010;
vector<int>tree[nmax];
bool isRoot[nmax]  ={false};
int w[nmax] = {0};
vector<string>ans;
string temp;
void dfs(int root){
    temp += w[root] + '0';
    if(tree[root].size() == 0){
        ans.push_back(temp);
        reverse(ans.back().begin(), ans.back().end());
        return;
    }
    for(int i = 0; i < tree[root].size(); ++i){
        dfs(tree[root][i]);
        temp.pop_back();
    }
}
string add(string a, string b){
    int len = max(a.size(), b.size());
    for(int i = a.size(); i < len; ++i)a += '0';
    for(int i = b.size(); i < len; ++i)b += '0';
    string res;
    int carry = 0;
    for(int i = 0; i < len; ++i){
        int temp = a[i] - '0' + b[i] - '0' + carry;
        carry = temp / 10;
        temp = temp % 10;
        res += temp + '0';
    }
    if(carry != 0)res += carry + '0';
    while(res.size() > 0 && res.back() == '0')res.pop_back();
    return res;
}
string getsum(){
    string sum = ans[0];
    for(int i = 1; i < ans.size(); ++i)sum = add(sum, ans[i]);
    while(sum.size() > 1 && sum.back() == '0')sum.pop_back();
    reverse(sum.begin(), sum.end());
    return sum;
}
int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; ++i)scanf("%d", &w[i]);
    for(int i = 0; i < m; ++i){
        int u, v;
        scanf("%d%d", &u, &v);
        tree[u].push_back(v);
        isRoot[v] = true;
    }
    vector<int>root;
    for(int i  = 0; i < n; ++i){
        if(isRoot[i] == false)root.push_back(i);
    }
    cout<<root.size()<<endl;
    int flag = 0;
    for(int i = 0; i < root.size(); ++i){
        temp.clear();
        ans.clear();
        dfs(root[i]);
        string sum = getsum();
        if(flag == 1)cout<<" ";
        printf("%s", sum.c_str());
        flag = 1;
    }
    cout<<endl;
    return 0;
}

D - 上帝视角

Problem Description

给一棵二叉树的层序遍历序列和中序遍历序列,求这棵二叉树的先序遍历序列和后序遍历序列,并给出从右往左、从右上往左下、从上往下分别能看到的结点个数。注意,此处均把二叉树的每条边都设置为等长,角度为45度,因此结点可能在视觉上重叠。所谓从右往左看是指,对同一层的结点,右边的结点会挡住左边的结点,这样同一层结点就只能看到最右边的那一个;同样的,从右上往左下看是指,右上角的结点会挡住左下角45度的结点;从上往下看是指,对同一竖直位置来说,只能看到最上方的结点。

例如对下图来说,从右往左能看到3个结点,从右上往左下能看到3个结点,从上往下能看到5个结点。

img

Input

每个输入文件中一组数据。

第一行一个正整数N(1<=N<=30),代表二叉树的结点个数(结点编号为1~N)。

接下来两行,每行N个正整数,分别代表二叉树的层序遍历序列和中序遍历序列。数据保证序列中1~N的每个数出现且只出现一次。

Output

先输出两行,每行N个正整数,分别代表二叉树的先序遍历序列和后序遍历序列。

接下来分三行输出从右往左、从右上往左下、从上往下分别能看到的结点个数。

每行末尾均不允许输出多余的空格。

Sample Input

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

Sample Output

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

分析:

1. 建树。每棵树的层序第一个节点必然是根节点,根据根节点对中序序列进行划分,得到左右子树的中序和层序,递归建树;

2. 从左往右看的个数就是树的深度

3. 从上往下看的个数是树伸的最开的,定义新的长度len1,往左走减一,往右走加一

4. 从右上往左下看的是最右的,定义新的长度len2, 做走不动,右走加一

5. 用dfs搜索树,记录两个长度即可

注意:计算路径长度是当搜索到空节点时才进行,而不是在叶子节点进行,因而得到的长度会比实际长度大一,最后注意修正,或者修改搜索终止条件为遍历到叶子节点就结束

#include<bits/stdc++.h>
using namespace std;
const int nmax = 40;
int in[nmax];
struct node{
    int v;
    node *lchild, *rchild;
};
typedef node* pnode;
void Print(vector<int>temp){
    for(int i = 0; i < temp.size(); ++i)cout<<temp[i]<<" ";
    cout<<endl;
}
pnode creat(int inL, int inR, vector<int>layer){
    if(inL > inR)return NULL;
    pnode root = new node;
    root->v = layer[0];
    root->lchild = root->rchild = NULL;
    int pos = inL;
    while(pos <= inR && in[pos] != layer[0])pos++;
    vector<int>layerleft, layerright;
    for(int i = 1; i < layer.size(); ++i){
        bool flag = false;
        for(int j = inL; j < pos; ++j){
            if(in[j] == layer[i]){
                flag = true;
                break;
            }
        }
        if(flag)layerleft.push_back(layer[i]);
        else layerright.push_back(layer[i]);
    }
    root->lchild = creat(inL, pos - 1, layerleft);
    root->rchild = creat(pos + 1, inR, layerright);
    return root;
}
vector<int>ans;
void pre(pnode root){
    if(root == NULL)return;
    ans.push_back(root->v);
    pre(root->lchild);
    pre(root->rchild);
}
void post(pnode root){
    if(root == NULL)return;
    post(root->lchild);
    post(root->rchild);
    ans.push_back(root->v);
}
int dmax = 0, lmax = 0, rmax = 0, pathmax = 0;
void dfs(pnode root, int depth, int len, int path){
    if(root == NULL){
        dmax = max(dmax, depth);
        pathmax = max(pathmax, path);
        if(len > 0)rmax = max(rmax, len);
        else lmax = min(lmax, len);
        return;
    }
    dfs(root->lchild, depth + 1, len - 1, path);
    dfs(root->rchild, depth + 1, len + 1, path + 1);
}
int main(){
    int n;
    scanf("%d", &n);
    vector<int>layer(n);
    for(int i = 0; i < n; ++i)scanf("%d", &layer[i]);
    for(int i = 0; i < n; ++i)scanf("%d", &in[i]);
    pnode root = creat(0, n - 1, layer);
    //获取先序和后序
    pre(root);
    for(int i = 0; i < ans.size(); ++i){
        printf("%d", ans[i]);
        if(i < ans.size() - 1)printf(" ");
        else printf("\n");
    }
    ans.clear();
    post(root);
    for(int i = 0; i < ans.size(); ++i){
        printf("%d", ans[i]);
        if(i < ans.size() - 1)printf(" ");
        else printf("\n");
    }
    //计算视值
    dfs(root, 0, 0, 0);
    printf("%d\n%d\n%d\n", dmax, pathmax, rmax - lmax - 1);
    return 0;
}

E - 二叉树の狂欢

Problem Description

给定一棵二叉树,判断其是否是AVL树(此处只要求考虑结点平衡,不需要考虑左右子树结点权值大小),如果不是AVL树的话,输出"NOT AVL TREE!!!“以及不平衡的结点个数;否则判断其是否是一棵完全二叉树,如果不是完全二叉树的话,输出"NOT COMPLETE TREE!!!” 以及结点个数饱和的最后一层层号(假设根结点层号为1,且第i层的结点个数饱和是指该层的结点个数等于2^(i-1));否则将这棵完全二叉树经过若干次向下调整变成大顶堆,输出"OHHHHH HEAP!!!"以及此过程中将父结点与子结点交换的总次数(每次父结点与子结点交换都算一次,即同一轮向下调整的过程中可能有多次交换)。

Input

每个输入文件中一组数据。

第一行一个正整数N(N<=20),代表二叉树的结点个数(结点编号为1到N)。

第二行按结点编号从小到大的顺序给出N个结点的权值(各结点的权值均小于20且各不相同)。

接下来按结点编号从小到大的顺序给出N行,每行为两个编号,分别代表该结点的左孩子编号和右孩子编号,如果不存在左(右)孩子,那么就用字符’-'代替。数据保证编号在1到N之间。

Output

分两行按题目描述中的字符串和相应统计结果。

Sample Input 1

3
1 2 3
- 2
- 3
- -

Sample Output 1

NOT AVL TREE!!!
1

Sample Input 2

4
1 2 3 4
2 3
- 4
- -
- -

Sample Output 2

NOT COMPLETE TREE!!!
2

Sample Input 3

5
1 2 3 4 5
2 3
4 5
- -
- -
- -

Sample Output 3

OHHHHH HEAP!!!
3

分析:常规题目,注意读清题目

1. 根节点不知道,需要自己找

2. 层序遍历不是题目中给出的权值序列,需要自己遍历得到

#include<bits/stdc++.h>
using namespace std;
const int nmax = 30;
struct node{
    int val, lchild, rchild;
};
node tree[nmax];
vector<int>w;
bool isRoot[nmax] = {false};
//AVL
int height[nmax] = {0};
int getH(int root){
    if(root == -1)return 0;
    if(height[root] != 0)return height[root];
    return max(getH(tree[root].lchild), getH(tree[root].rchild)) + 1;
}
bool isAVL(int n, int &nbalance){
    bool flag = true;
    for(int i = 1; i <= n; ++i){
        int bf = getH(tree[i].lchild) - getH(tree[i].rchild);
        if(abs(bf) >= 2){
            flag = false;
            nbalance++;
        }
    }
    return flag;
}
//cbt
bool isCBT(int root, int &layer){
    queue<int>q;
    q.push(root);
    bool nochild = false, flag = true;
    int depth = 1;
    int cur = 1, next = 0;
    w.push_back(0);
    while(!q.empty()){
        int temp = q.front();
        q.pop();
        cur--;
        w.push_back(tree[temp].val);
        if(tree[temp].lchild != -1){
            q.push(tree[temp].lchild);
            if(nochild)flag = false;
            next++;
        }else{
            nochild = true;
        }
        if(tree[temp].rchild != -1){
            q.push(tree[temp].rchild);
            if(nochild)flag = false;
            next++;
        }else{
            nochild = true;
        }
        if(cur == 0){
            int cnt = pow(2, depth);
            if(cnt == next)layer = depth + 1;
            depth++;
            cur = next;
            next = 0;
        }
    }
    return flag;
}
//heap
int toHeap(int n){
    int cnt = 0;
    for(int i = n / 2; i >= 1; --i){
        int j = i, k = 2 * j;
        while(k <= n){
            if(k + 1 <= n && w[k] < w[k + 1])k++;
            if(w[j] < w[k]){
                swap(w[j], w[k]);
                cnt++;
                j = k;
                k = 2 * j;
            }else{
                break;
            }
        }
    }
    return cnt;
}
int main(){
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)scanf("%d", &tree[i].val);
    for(int i = 1; i <= n; ++i){
        string str1, str2;
        cin>>str1>>str2;
        if(str1[0] == '-')tree[i].lchild = -1;
        else{
            tree[i].lchild = stoi(str1);
            isRoot[stoi(str1)] = true;
        }
        if(str2[0] == '-')tree[i].rchild = -1;
        else{
            tree[i].rchild = stoi(str2);
            isRoot[stoi(str2)] = true;
        }
    }
    int root = 0;
    for(int i = 1; i <= n; ++i){
        if(isRoot[i] == false){
            root = i;
            break;
        }
    }
    //AVL
    int nbalance = 0;
    bool flag1 = isAVL(n, nbalance);
    if(flag1 == false){
        printf("NOT AVL TREE!!!\n%d\n", nbalance);
    }else{
        int layer = 0;
        bool flag2 = isCBT(root, layer);
        if(flag2 == false){
            printf("NOT COMPLETE TREE!!!\n%d\n", layer);
        }else{
            int turn = toHeap(n);
            printf("OHHHHH HEAP!!!\n%d\n", turn);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39011762/article/details/88355740