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

7-1 还原二叉树

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

输入格式:

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

输出格式:

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

输入样例:

9
ABDFGHIEC
FDHGIBEAC

输出样例:

5
#include<stdio.h>
int height(char x[],char z[],int n)
{
    if(n==0)
        return 0;
    char m=x[0];
    int i;
    for(i=0;i<n;i++)
    {
        if(m==z[i])
            break;
    }
    int leftHeight=height(x+1,z,i);
    int rightHeight=height(x+i+1,z+i+1,n-i-1);
    return leftHeight>rightHeight?leftHeight+1:rightHeight+1;
}
int main()
{
    int n;
    scanf("%d",&n);
    char x[10086],z[10086];
    scanf("%s",x);
    scanf("%s",z);
    printf("%d",height(x,z,n));
    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

输出样例:

4
#include<stdio.h>
int family[30000];
int find_root(int x)
{
    if(x==family[x])
        return x;
    return find_root(family[x]);
}
void merg(int x,int y)
{
    family[find_root(y)]=family[find_root(x)];
}
int main()
{
    int n,m,num,t1,t2,i,k;
    scanf("%d %d",&n,&m);
    for(i=1;i<=n;i++)
    {
        family[i]=i;
    }
    for(i=0;i<m;i++)
    {
        scanf("%d",&num);
        scanf("%d",&t1);
        for(k=1;k<num;k++)
        {
            scanf("%d",&t2);
            if(find_root(t1)!=find_root(t2))
            {
                merg(t1,t2);
            }
        }
    }
    int max=0;
    int same_root[30000]={0};
    for(i=1;i<=n;i++)
    {
        same_root[find_root(i)]++;
        if(same_root[find_root(i)]>max)
            max=same_root[find_root(i)];
    }
    printf("%d",max);
    return 0;
}

7-3 修理牧场

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

但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为 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;
int main()
{
    priority_queue<int,vector<int>,greater<int>>q;
    int n,i;
    int a[10086];
    scanf("%d",&n);
    for(i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        q.push(a[i]);
    }
    int sum=0;
    while(!q.empty()&&q.size()!=1)
    {
    int temp1=q.top();
    q.pop();
    int temp2=q.top();
    q.pop();
    int temp=temp1+temp2;
    sum=sum+temp;
        q.push(temp);
    }
    printf("%d",sum);
    return 0;
}

7-4 根据后序和中序遍历输出前序遍历

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

输入格式:

第一行给出正整数 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<stdio.h>
void trans(int z[],int h[],int n)
{
    int i;
    if(n==0)
        return;
    int m=h[n-1];
    for(i=0;i<n;i++)
    {
        if(z[i]==m)
            break;
    }
    printf(" %d",m);
    trans(z,h,i);
    trans(z+i+1,h+i,n-i-1);
}
int main()
{
    int n,i;
    int z[10086],h[10086];
    scanf("%d",&n);
    for(i=0;i<n;i++)
    {
        scanf("%d",&h[i]);
    }
    for(i=0;i<n;i++)
    {
        scanf("%d",&z[i]);
    }
    printf("Preorder:");
    trans(z,h,n);
    return 0;
}

7-5 玩转二叉树

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

输入格式:

输入第一行给出一个正整数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<stdio.h>
int pre[10086],in[10086],level[10086]={0};
void dfs(int pre[],int in[],int n,int index)
{
    if(n==0)
        return;
    int i;
    level[index]=pre[0];
    for(i=0;i<n;i++)
    {
        if(pre[0]==in[i])
            break;
    }
    dfs(pre+1,in,i,index*2+2);
    dfs(pre+i+1,in+i+1,n-i-1,index*2+1);
}
int main()
{
    int n,i;
    scanf("%d",&n);
    for(i=0;i<n;i++)
    {
        scanf("%d",&in[i]);
    }
    for(i=0;i<n;i++)
    {
        scanf("%d",&pre[i]);
    }
    dfs(pre,in,n,0);
    int num=1;
    for(i=0;i<10086;i++)
    {
        if(num<n&&level[i]!=0)
        {
            printf("%d ",level[i]);
            num++;
        }
        else if(level[i]!=0)
        {
            printf("%d",level[i]);
            break;
        }
    }
    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<stdio.h>
int a[10086];
void creat(int x,int n)
{
    if(x<=n)
    {
        creat(x*2,n);
        creat(x*2+1,n);
        scanf("%d",&a[x]);
    }
}
int main()
{
    int n,i;
    scanf("%d",&n);
    creat(1,n);
    for(i=1;i<=n;i++)
    {
        if(i==1)
            printf("%d",a[i]);
        else
            printf(" %d",a[i]);
    }
    return 0;
}

7-7 部落

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

输入格式:

输入在第一行给出一个正整数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<stdio.h>
#define Max 100000
int book[Max];
int f[Max]; // 祖宗结点数组
 
// 初始化init
void init(int n)
{
    for(int i=1;i<=n;i++){
        f[i]=i;     // 设置父节点(父节点初始为自身)
        book[i]=0;  // 设置人数为0
    }
}
// 查询find:找到祖宗
int find(int x)
{
    if(f[x]==x)     // 递归出口,到达祖先位置,返回祖先
        return x;
    else{
        f[x]=find(f[x]);    // 不断向上查找祖先(路径亚索)
        return f[x];           // 返回父节点(祖先)
    }
}
// 合并merge/union:合并祖宗
void merge(int x,int y)
{
    f[find(x)]=f[find(y)];
}
 
// 找到最大朋友数
void FindTribe(int n)
{
    int tribe[Max]={0};
    int num_per=n,num_tri=0;
    for(int i=1;i<=n;i++){
        if(tribe[find(i)] == 0){
            tribe[find(i)] = 1;
            num_tri++;
        }
    }
    printf("%d %d\n",num_per,num_tri);   // 输出祖宗朋友数最大的
}
 
void judge(int a, int b){
    if(find(a) == find(b))
        printf("Y\n");
    else
        printf("N\n");
}
 
int main()
{
    int m;
    int num=0;  // 用于统计成员数量,初始为0
    scanf("%d",&m);
    init(Max);
    // 录入数据
    for(int i=0;i<m;i++){
        int x,j;
        scanf("%d",&x);
        int a[x];       // a[x]数组作为临时数组用来存学生编号
        for(j=0;j<x;j++){
            scanf("%d",&a[j]);
            if(book[a[j]]==0){
                book[a[j]] = 1;
                num++;
            }
        }
        for(j=1;j<x;j++)
            merge(a[j-1],a[j]);
    }
    FindTribe(num);
    int n;
    scanf("%d",&n);
    for(int i=0; i<n; i++){
        int a,b;
        scanf("%d %d",&a, &b);
        judge(a,b);
    }
    return 0;
}

 7-8 列出叶结点

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

输入格式:

首先第一行给出一个正整数 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 tree
{
    int left,right;
}a[15];
int main()
{
    int n,check[15]={0},root,p[10],j=0,i;
    queue<int>q;
    cin>>n;
    for(i=0;i<n;i++)
    {
        char l,r;
        cin>>l>>r;
        a[i].left=l!='-'?l-'0':-1;//自定义-1表示空,如果左不等于‘-’,那我们就把l化成整数存给left,如果等于我们就自定义空-1
        a[i].right=r!='-'?r-'0':-1;
        check[a[i].left]=1;
        check[a[i].right]=1;//将节点进行标记
    }
    for(root=0;root<n;root++)
        if(check[root]==0)
            break;//循环遍历得到根节点,其他左右子树已经被标记为1了
    q.push(root);//将根节点入队
    while(!q.empty())//循环遍历完整棵树,只要还剩下结点就一直遍历
    {
        int i=q.front();//定义i为q的队首
        q.pop();//将队首出队
        if(i==-1)//如果当前结点为空,那下面的操作都不用进行了
            continue;
        if(a[i].left==-1&&a[i].right==-1)
            p[j++]=i;//如果左右子树都为-1,也就是空,那说明这是一个叶节点,把他的编号存到p数组里
        q.push(a[i].left);
        q.push(a[i].right);
    }
    cout<<p[0];
    for(i=1;i<j;i++)
        cout<<' '<<p[i];
    return 0;
}

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

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

输入格式:

输入二叉树的先序序列。

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

输出格式:

输出有两行:

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

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

输入样例:

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

输出样例:

CBEGDFA

AFDGEBC
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
    char data;
    struct node *left,*right;
}*tree;
tree creat()
{
    char a;
    tree root;
    scanf("%c",&a);
    if(a=='#')
        return NULL;
    else
    {
        root=(tree)malloc(sizeof(struct node));
        root->data=a;
        root->left=creat();
        root->right=creat();
    }
    return root;
}
void outmid(tree root)
{
    if(root)
    {
        outmid(root->left);
        printf("%c",root->data);
        outmid(root->right);
    }
}
void swap(tree root)
{
    if(!root)
        return ;
    if(!root->left&&!root->right)
        return;
    tree temp;
    temp=root->left;
    root->left=root->right;
    root->right=temp;
    swap(root->left);
    swap(root->right);
}
int main()
{
    tree root=creat();
    outmid(root);
    printf("\n");
    swap(root);
    outmid(root);
    return 0;
}

7-10 建立与遍历二叉树

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

输入格式:

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

输出格式:

中序遍历结果

输入样例:

在这里给出一组输入。例如:

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

输出样例:

在这里给出相应的输出。例如:

CBEGDFA
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
    char data;
    struct node *left,*right;
}*tree;
tree creat()
{
    tree root;
    char a;
    scanf("%c",&a);
    if(a=='#')
        return NULL;
    else
    {
        root=(tree)malloc(sizeof(struct node));
        root->data=a;
        root->left=creat();
        root->right=creat();
    }
    return root;
}
void outmid(tree root)
{
    if(root)
    {
        outmid(root->left);
        printf("%c",root->data);
        outmid(root->right);
    }
}
int main()
{
    tree root=creat();
    outmid(root);
}

 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;
typedef struct node{
    int data;
    struct node *left,*right;
}*tree;
tree creat(int a[],int b[],int n)
{
    tree root;
    root=(tree)malloc(sizeof(struct node));
    if(n==0)
        return NULL;
    int i;
    root->data=a[n-1];
    for(i=0;i<n;i++)
    {
        if(b[i]==a[n-1])
            break;
    }
        root->left=creat(a,b,i);
        root->right=creat(a+i,b+i+1,n-i-1);
    return root;
}
int flag=0;
void out(tree root) {
    if (!root) return;
    queue<tree> q;
    q.push(root);
    while (!q.empty()) {
        tree current = q.front();
        q.pop();
if(flag==0)
{
    printf("%d",current->data);
    flag=1;
}
        else
            printf(" %d",current->data);
        if (current->left) {
            q.push(current->left);
        }

        if (current->right) {
            q.push(current->right);
        }
    }
}
int main()
{
    int n,i;
    int a[50],b[50];
    tree root;
    root=(tree)malloc(sizeof(struct node));
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%d",&a[i]);
    for(i=0;i<n;i++)
        scanf("%d",&b[i]);
    root=creat(a,b,n);
    out(root);
    return 0;
}

7-12 树层次遍历

我们已知二叉树与其自然对应的树相比,二叉树中结点的左孩子对应树中结点的左孩子,二叉树中结点的右孩子对应树中结点的右兄弟。进而我们可以利用“基于带空指针信息的先根序列构建二叉树”的方法来构建其对应的树的左孩子-右兄弟存储结构。如8 5 1 0 6 0 2 0 0 3 4 0 0 7 0 0 0对应图1(a)所示的树,1 2 0 3 0 4 0 0 0对应如图1(b)所示的树。

tree.jpg

请编写程序用上述方法构建树,并给出树的层次遍历序列。

输入格式:

输入为一组用空格间隔的整数,个数不超过100个,表示带空指针信息的二叉树先根序列。其中空指针信息用0表示

输出格式:

输入为一组整数,每个整数后一个空格,表示该树的层次遍历序列。

输入样例:

1 2 0 3 0 4 0 0 0

输出样例:

1 2 3 4 
#include <bits/stdc++.h>
using namespace std;
int a[200];
vector<vector<int> > v(105);
void creat() {
    stack<int> s;
    s.push(a[0]);
    int i = 1;
    while (!s.empty()) {
        if(a[i])  s.push(a[i ++]);
        else {
            int len = s.size(), t = s.top();
            s.pop();
            i++;
            v[len].push_back(t);
        }
    }
    return;
}
int main() {
    int len = 0;
    int t;
    while (cin >> t) {
    	if(t == '\n') break;
        a[len ++] = t;
    }
    creat();
    for (int i = 0; i < v.size(); i ++) {
        for (int j = 0; j < v[i].size(); j ++) {
            cout << v[i][j] << ' ';
        }
    }
    return 0;
}

7-13 哈夫曼树

哈夫曼树,第一行输入一个数n,表示叶结点的个数。

需要用这些叶结点生成哈夫曼树,根据哈夫曼树的概念,这些结点有权值,即weight,题目需要输出哈夫曼树的带权路径长度(WPL)。

输入格式:

第一行输入一个数n,第二行输入n个叶结点(叶结点权值不超过1000,2<=n<=1000)。

输出格式:

在一行中输出WPL值。

输入样例:

5
1 2 2 5 9

输出样例:

37
//哈夫曼树思想
#include<bits/stdc++.h>
using namespace std;
int main()
{
    priority_queue<int,vector<int>,greater<int>>p;//greater是从大到小排列,最小的在最顶上
    int N,a[10086],i,sum=0;
    scanf("%d",&N);
    for(i=0;i<N;i++)
    {
        scanf("%d",&a[i]);
        p.push(a[i]);
    }
    while(!p.empty()&&p.size()!=1)
    {
        int temp1=p.top();
        p.pop();
        int temp2=p.top();
        p.pop();//弹出队列中最小的两个数
        int temp=temp1+temp2;
        p.push(temp);
        sum=sum+temp;
    }
    printf("%d",sum);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值