并查集,二叉树,快排题解

根据前序遍历遵循“根左右”和中序遍历遵循”左根右“的特点,首先一棵二叉树的前序第一个是根,那么在中序中,根就把中序分成左子树和右子树,然后我们再把左子树和右子树看成单独的两棵二叉树,然后跟之前一样在前序找到根,再分成左子树和右子树,不断递归直到不能再分就可以按照后序输出的规律输出

#include<stdio.h>
#include<string.h>
char a[100],b[100];
void fun(int root,int start,int end)//root表示在前序中根节点的下标,start表示在中序中当前子树的开头,end表示在中序中当前字子树的结尾
{
    if(start>end)
        return;
    int i=start;//用i标记当前树在中序的起始位置
    while(i<end&&a[i]!=b[root])//在中序中找到当前树的根节点
        i++;
    fun(root+1,start,i-1);//递归左子树,root+1:在前序中根节点+1就是左子树根节点,start:左子树的最左边的节点,i-1:在中序中根节点-1即为左子树的最右边的节点
    fun(root+i-start+1,i+1,end);//递归右子树,root+i-start+1是在前序中右子树根节点,i+1是右子树的开头,end是右子树结尾
    printf("%c",a[i]);
 }
int main()
{
    scanf("%s",a);//输入中序
    scanf("%s",b);//输入前序
    int leth=strlen(a);//计算此字符串的长度
    fun(0,0,leth-1);
    getchar();getchar();
    return 0;
}

 这个题感觉和上面那题差不多,只是输出方式有点差异,只需要先输出后递归就可以了

#include<stdio.h>
#include<string.h>
char a[10],b[10];
void fun(int root,int start,int end)//root为在后序的根,start为当前树的开端,end为当前树在中序的末端
{
    if(start>end)
        return;
    int i=start;
    while(i<end&&a[i]!=b[root])
        i++;//找到当前根在中序的位置
    printf("%c",a[i]);
    fun(root-(end-i+1),start,i-1);//递归左子树(root-减去右子树的长度之后在前序中就恰好到了左子树的节点)
    fun(root-1,i+1,end);//递归右子树
}
int main()
{
    scanf("%s",a);//输入中序
    scanf("%s",b);//输入后序
    int leth=strlen(a);
    fun(leth-1,0,leth-1);
}

 

  这个题我刚开始是想跟据所给的数据来建树,但是我发现我根本建不来,但是我发现一种更为便捷的方式,只要通过某种手段将它们联系在一起就行了。题目的输入方式是根左右然后在从左至右根据根左右的方式重复进行,第一行输入的第一个字符一定是整个二叉树的根节点,然后每一行的前一个字符是一个父节点,所以我们可以通过找到将每一个父节点作为连接关系,就是将其化为int型,作为结构体的下标,然后每输出一个父节点就不断递归直到遇到空节点。

#include<stdio.h>
struct tree
{
    char left;
    char right;
}t[130];//z的码值为122
int n;
char a,b,c,root;
void fun(char qq)
{
    if(qq=='*')//遇到空即返回
        return;
    printf("%c",qq);
    fun(t[qq].left);//递归左子树
    fun(t[qq].right);//递归右子树
}

int main()
{
    scanf("%d",&n);
    scanf(" %c %c %c",&a,&b,&c);//将第一次单独领出来方便后续从根节点开始遍历
    t[a].left=b;
    t[a].right=c;
    root=a;//标记根节点
    for(int i=2; i<=n; i++)
    {
        scanf(" %c %c %c",&a,&b,&c);
        t[a].left=b;
        t[a].right=c;
    }
 fun(root);//从根开始遍历
 return 0;
}

 这个题完全是上一个题的变形题,上一个题是通过首字母的ascll值看成下标作为连接点,这个题也是通过数组下标来寻找下一个,在寻找最大值的时候可以用搜索的方法,将每一条路都走一下,不断查找最长路径即深度,然后遇到空节点就返回

#include<stdio.h>
struct tree
{
    int left;
    int right;
};
struct tree t[1000009];
int max=-1;
change(int x,int y)//一个取较大值的函数
{
    if(x>y)
        return x;
    else
        return y;
}
void fun(root,deepth)
{

        if(root==0)//遇到叶子节点则返回
            return;
        max=change(deepth,max);//取最大值
        fun(t[root].left,deepth+1);//查找左子树
        fun(t[root].right,deepth+1);//查找右子树
}
int main()
{
    int n;
    scanf("%d",&n);
    scanf("%d %d",&t[1].left,&t[1].right);
    for(int i=2;i<=n;i++)//因为根节点是1,所以从2开始存起
        scanf("%d %d",&t[i].left,&t[i].right);
        fun(1,1);//从一这个根节点查起,此时深度为一
    printf("%d",max);
}

说真的看到这个题我读了好几遍,我始终没有任何思路,简直是一头雾水,满头大汗,然后我呆头呆脑的想了二个小时,算了

然后我就只好偷偷去问了大佬,我才明白这个题咋整,首先导致中序排列有很多种形式的原因是因为单子结点(只有一个儿子的父节点)的存在,而单子结点的表现形式就是在前序排列中为ab型,而在后序排列中出于ba型,先找到这种形式的结点,记下它们的数量,然后单子结点会使树成二种趋势,一是向左,二是向右,所以每有一个单子结点,就有2种中序排列的方式即2的多少次方

#include<stdio.h>
#include<string.h>
#include<math.h>
int main()
{
    char a[1000],b[1000];
    int sum=0;
    scanf("%s",&a);//得出a,b的长度
    scanf("%s",&b);
    int m=strlen(a);//得出前序的长度
    int n=strlen(b);//得出后序的长度
    for(int i=0; i<m-1; i++)
    {
        for(int j=0; j<n-1; j++)
        {
            if(a[i]==b[j+1]&&a[i+1]==b[j])//判断是不是单子节点
            sum++;
        }
    }
    sum=pow(2,sum);//有多少个单子节点就有2多少次方种中序排列方式
    printf("%d",sum);
    return 0;
}

这个题目可以形象化理解,假如有一群人,有不同的老大,然后题目是告诉有多少人是属于同一个团伙,以及判断他们是否为同一个老大。所以对于合并就使其加入团伙,查询就顺着支路找到老大然后判断是不是同一个老大就行。在最开始的时候还没有帮派的时候,通过人与人之间的关系分成帮派(随便派使后一个为前一个的老大),然后搜查,但是这样时间复杂度很高,于是我们将一些老大的小弟的小弟全部提拔成上下属关系,即

#include<stdio.h>
int n,m,a[10010];
void join(int y,int z)
{
    int fy=find(y),fz=find(z);//找到他们两个的老大
        a[fy]=fz;//将一个老大归属到另一个老大手下
}
int find(int s)
{
    while(a[s]!=s)//老大一般都是自身为数,找到最高层
        s=a[s]=a[a[s]]; //让s和s的老大变成他老大的老大
        return s;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
        a[i]=i;//将a[i]里头的数初始化
    for(int i=1; i<=m; i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if(x==1)
            join(y,z);//合并两个人
        if(x==2)
        {
            int q=find(y);//分别找到y,z的老大
            int p=find(z);

            if(p==q)//判断是不是同一个老大
                printf("Y\n");
            else
                printf("N\n");
        }
    }
    return 0;
}

 这个题和并查集模板没什么区别,我们依旧把这看成一群小弟和老大,然后在把小弟们分为一个帮派,判断老大是否相同就可以了

#include<stdio.h>
int a[6000];
int find(int x)
{
    while(x!=a[x])
        x=a[x]=a[a[x]];//优化路径,直接将上层的上层给与x
        return x;
}
void join(int y,int z)
{
    int fy=find(y),fz=find(z);//找到他们两个的老大
        a[fy]=fz;//将一个老大归属到另一个老大手下
}
int main()
{
    int n,m,p;
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=n;i++)
        a[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        join(x,y);//将小弟们整合
    }
    for(int i=1;i<=p;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        int fx=find(x),fy=find(y);//找到他们的老大
        if(fx==fy)
            printf("Yes\n");
            else
                printf("No\n");
    }
    return 0;
}

 这个题是一个资本家出的题目并查集的题目,以前的时候并查集只要找到它们的老大看他们是不是一伙的就行,但是这个题比那种题要更加深化一点,是那种查出你这个团伙全部成员,然后我们得到一些团伙数量,要得到在一定价格买到价值相对来说最高的方案,就要转化为01背包问题就可以了

#include<stdio.h>
int n,m,w,all=0;
int pack[100000]= {0}; //背了个包
struct massage//最开始录入的价格和价值
{
    int num;
    int mony;
    int value;
};
struct zong//小团体的总价值和价格
{
    int b;//价格
    int c;//价值
};
struct massage a[100009];
struct zong and[100090];
int find(int i)//划分小团体,简化关系(路径压缩)
{
    while(a[i].num!=i)
        i=a[i].num=a[a[i].num].num;
    return i;
}
void join(int y,int z)
{
    int fy=find(y),fz=find(z);//找到他们两个的老大
        a[fy].num=fz;//将一个老大归属到另一个老大手下
}
void fun()//背包问题
{
    for(int i=1; i<=all; i++)
    {
        for(int j=w; j>=and[i].b; j--)
        {
            if(pack[j]<pack[j-and[i].b]+and[i].c)//当找到较优方案
                pack[j]=pack[j-and[i].b]+and[i].c;
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&w);
    for(int i=1; i<=n; i++)
        a[i].num=i;//使云的编号首先等于它自身
    for(int i=1; i<=n; i++)
        scanf("%d %d",&a[i].mony,&a[i].value);//输入云的价格和价值
    for(int i=1; i<=m; i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
       join(x,y);//将小弟们整合
    }
    for(int i=1; i<=n; i++)
        find(i);//将他们全部变成单一关系
    for(int i=1; i<=n; i++)
    {
        if(a[i].num==i)//遇到自身是老大的情况
        {
            all++;
            for(int j=1; j<=n; j++) //搜寻自己的小弟
            {
                if(a[j].num==i)
                {
                    and[all].b=and[all].b+a[j].mony;
                    and[all].c=and[all].c+a[j].value;
                }
            }
        }
    }
    fun();//背包寻找能获得的最大价值
    printf("%d",pack[w]);
    return 0;
}

 害,这个题是上一个题的简化版,首先我们依旧要先分配团伙,找到老大,把路径压缩,然后在找到小红和小明的团伙有多少人,再取较小朋友的那一方的朋友数量。

#include<stdio.h>
int n,m,p,q,a[50000],b[50000];
void join1(int x,int y)//使A公司朋友归类
{
    int fx=find1(x),fy=find1(y);
        a[fx]=fy;
}
void join2(int x,int y)//使B公司朋友归类
{
    int fx=find2(x),fy=find2(y);
        b[fx]=fy;
}
int find1(int x)//找到A公司最高层
{
    while(x!=a[x])
        x=a[x]=a[a[x]];
        return x;
}
int find2(int x)//找到B公司最高层
{
    while(x!=b[x])
        x=b[x]=b[b[x]];
        return x;
}
int min(int x,int y)//求较小数的函数
{
    if(x>y)
        return y;
    else
        return x;
}
int main()
{
    scanf("%d %d %d %d",&n,&m,&p,&q);
    for(int i=1;i<=n;i++) //初始化A公司员工
        a[i]=i;
    for(int i=1;i<=m;i++)//初始化B公司员工
        b[i]=i;
    for(int i=1; i<=p; i++)
    {
        int gg,bb;
        scanf("%d%d",&gg,&bb);
        join1(gg,bb);//使A公司朋友归类
    }
    for(int i=1;i<=q;i++)
    {
        int gi,bi;
        scanf("%d%d",&gi,&bi);
        join2(-gi,-bi);//使B公司朋友归类
    }
    int sum1=0,sum2=0,sum;
    for(int i=1;i<=n;i++)//找小明的朋友有多少个
    {
        if(find1(i)==find1(1))
            sum1++;
    }
    for(int i=1;i<=m;i++)//找小红的朋友有多少个
    {
        if(find2(i)==find2(1))
            sum2++;
    }
    sum=min(sum1,sum2);//情侣的对数是他们较少朋友的一方的朋友数
    printf("%d",sum);
}

 这个题好像数据开的格外多和大,所以之前我们取一个基数在最左边的方式不可取·了·,我们·直接取中间数,运用二分既可以了。

#include<stdio.h>
int n,a[1000001];
void qsort(int l,int r)//应用二分思想
{
    int mid=a[(l+r)/2];//中间数
    int i=l,j=r;
    do{
        while(a[i]<mid) i++;//查找左半部分比中间数大的数
        while(a[j]>mid) j--;//查找右半部分比中间数小的数
        if(i<=j)//如果有一组不满足排序条件(左小右大)的数
        {
            int t=a[i];
            a[i]=a[j];
            a[j]=t;
            i++;
            j--;
        }
    }while(i<=j);
    if(l<j) qsort(l,j);//递归搜索左半部分
    if(i<r) qsort(i,r);//递归搜索右半部分
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    qsort(1,n);
    for(int i=1;i<=n;i++)
    printf("%d ",a[i]);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值