pat甲级 树

A1151

#include<iostream>
#include<map>
using namespace std;

const int maxn=10000;

int n,m;

int seq[maxn],in[maxn],pre[maxn],p[maxn],depth[maxn];

map<int,int> pos;

int build(int preL,int preR,int inL,int inR,int d)
{
    int root=pre[preL];     //用root表示当前的根结点
    int k=root;           //其实这个root就是在原始数组中根结点的下标,记住就行
    depth[root]=d;    //设置根结点的深度为d
    if(inL<k)   //左子树 inL<k表示左子树
    {
        p[build(preL+1,preL+(k-inL),inL,k-1,d+1)]=root;     //这个计算方法和之前一样
    }

    if(k<inR)    //右子树
    {
        p[build(preL+1+(k-inL),preR,k+1,inR,d+1)]=root;
    }
    return root;    //最后返回根结点
}

int main()
{
    cin >> m >> n;
    for(int i=0; i<n; i++)
    {
        cin >> seq[i];     //seq表示一个存储结点的数组
        pos[seq[i]]=i;      //pos就是映射数组seq,将输入的每个结点都映射为0~n
        in[i]=i;        //中序数组,因为把输入的中序数组的每一个值都在上一行中映射为i,所以中序数组就是i

    }

    for(int i=0; i<n; i++)
    {
        cin >> pre[i];       //输入前序数组
        pre[i]=pos[pre[i]];         //将前序数组中,存放的数也进行映射
    }

    build(0,n-1,0,n-1,0);      //够将二叉树,最后一个参数为深度

    int a,b;
    while(m--)
    {
        cin >> a >> b;
        if(pos.count(a) && pos.count(b))   //如果找得到a,b才进行爬山
        {
            a=pos[a],b=pos[b];     //先将输入的a,b进行映射
            int x=a,y=b;    //记录x为a,y为b
		//刚一开始,a,b两个人开始爬山,直到a,b到了同一个位置
            while(a!=b)       //如果a,b不同,则将深度大的进行向上爬
                if(depth[a]>depth[b]) a=p[a];
                else b=p[b];
		//如果说a,b一起爬山到了,一个不为x,y的位置,表示x,y的祖先就是a
                if(a!=x && a!=y) printf("LCA of %d and %d is %d.\n",seq[x],seq[y],seq[a]);
                else if(a==x)  //如果a,b一起爬山,到了同一个位置,恰好为x,则表示x是根结点
                	printf("%d is an ancestor of %d.\n",seq[x],seq[y]);
                else printf("%d is an ancestor of %d.\n",seq[y],seq[x]);
        }
        else if(pos.count(a)==0 && pos.count(b)==0)
            printf("ERROR: %d and %d are not found.\n",a,b);
        else if(pos.count(a)==0)
            printf("ERROR: %d is not found.\n",a);
        else
            printf("ERROR: %d is not found.\n",b);

    }
}

A1143

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;

const int maxn=101000;

int n,m;

map<int,int> mp;

int seq[maxn],in[maxn],fuck[maxn],fuck1[maxn],pre[maxn],p[maxn],depth[maxn];

int build(int preL,int preR,int inL,int inR,int d)
{
    int root=pre[preL];
    int k=root;
    depth[root]=d;
    if(inL<k)
    {
        p[build(preL+1,preL+(k-inL),inL,k-1,d+1)]=root;
    }
    if(k<inR)
    {
        p[build(preL+(k-inL)+1,preR,k+1,inR,d+1)]=root;
    }
    return root;
}

int main()
{
    cin >> m >> n;
    for(int i=0; i<n; i++)
    {
        cin >> seq[i];
        fuck[i]=seq[i];
    }
    sort(seq,seq+n);         //对搜索二叉树进行排序后的数组就是该二叉搜索树的前序数组
    //前面只是为了和A1151的思路一致
    //使用映射是为了时间复杂度,这个过程称为离散化
    for(int i=0; i<n; i++)
    {
        mp[seq[i]]=i;         //seq[i]表示的是中序遍历
        in[i]=i;        //in[i]是映射后的中序遍历数组
    }
    for(int i=0; i<n; i++)
    {
        pre[i]=mp[fuck[i]];    //fuck[i]表示的就是前序遍历,pre[i]是映射后的前序遍历数组
    }

    build(0,n-1,0,n-1,0);

    int a,b;

    while(m--)
    {
        cin >> a >> b;
        if(mp.count(a) && mp.count(b))
        {
            a=mp[a],b=mp[b];
            int x=a,y=b;

            while(a!=b)
            {
                if(depth[a]>depth[b])
                    a=p[a];
                else
                    b=p[b];
            }

            if(a!=x && a!=y)
            {
                printf("LCA of %d and %d is %d.\n",seq[x],seq[y],seq[a]);
            }
            else if(a==x)
            {
                printf("%d is an ancestor of %d.\n",seq[x],seq[y]);
            }
            else
            {
                printf("%d is an ancestor of %d.\n",seq[y],seq[x]);
            }
        }
        else if(mp.count(a)==0 && mp.count(b)==0)
            printf("ERROR: %d and %d are not found.\n",a,b);
        else if(mp.count(a)==0)
            printf("ERROR: %d is not found.\n",a);
        else
            printf("ERROR: %d is not found.\n",b);
    }
    return 0;
}

A1130

#include<iostream>
using namespace std;

const int maxn=24;

int l[maxn],r[maxn];

bool st[maxn];

int n;

string w[maxn];

bool is_ye[maxn];

string dfs(int root)
{
    string left,right;       
    if(l[root]!=-1)       //如果根结点root的左孩子不是-1,即存在
    {
        left=dfs(l[root]);         //递归
        if(is_ye[l[root]]==false)       //如果左孩子不是叶子结点,进行加括号
        {
            left="(" + left + ")";
        }
    }
    if(r[root]!=-1)            //右节点同上
    {
        right=dfs(r[root]);
        if(is_ye[r[root]]==false)
        {
            right="(" + right + ")";
        }
    }

    return left+w[root]+right;      //直接返回 左值 中间的符号  右值
}

int main()
{
    cin >> n;          //输入n个结点
    for(int i=1; i<=n; i++)     //for循环
    {
        cin >> w[i] >> l[i] >> r[i];
        st[l[i]]=st[r[i]]=true;     //对该结点的左右孩子进行标记,从而找出根结点

        if(l[i]==-1 && r[i]==-1)     //如果该结点是叶子结点,也对该结点进行标记
            is_ye[i]=true;
    }

    int root;
    for(int i=1; i<=n; i++)     //找到根结点
    {
        if(st[i]==false)
        {
            root=i;            
            break;
        }
    }
    cout << dfs(root);     //dfs递归
}

A1155

#include<iostream>
#include<vector>
using namespace std;

const int maxn=10010;

int n;

bool gt,lt;

int h[maxn];

vector<int> path;

void dfs(int root)
{
    path.push_back(h[root]);  //先将根结点表示的值放入到数组path中

    if(root*2>n)      //到达南墙,需要输出路径,同时通过一条路径上的前后值,来判断是大顶堆还是小顶堆
    {
        cout << path[0];
        for(int i=1; i<path.size(); i++)
        {
            cout << " " << path[i];
            if(path[i]>path[i-1])      //后一个值大于前一个值,表示小根堆
                gt=true;
            if(path[i]<path[i-1])           //表示大根堆
                lt=true;
        }
        cout << endl;
    }

    if(root*2+1<=n)        //先不断的递归右子树,最后输出的是时候就是从右至左输出
        dfs(root*2+1);

    if(root*2<=n)       //在不断的递归左子树
        dfs(root*2);

    //如果对于上面的递归是先递归左子树,在递归右子树,则最后输出路径是从左至右

    path.pop_back();   //因为该函数一开始是直接将根结点表示的值放入到path中,然后就一直递归右子树,直到一条路径结束,然后需要一步一步的返回函数,也就是不断的path_popback()
}

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

    dfs(1);

    if(lt && gt)
        cout << "Not Heap" << endl;
    else if(gt)
        cout << "Min Heap" << endl;
    else if(lt)
        cout << "Max Heap" << endl;

    return 0;

}

A1135

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;

/*
    红黑树判断:
    1.根结点是黑的,也就是值<0
    2.红结点的左右孩子是黑的
    3.每条路径上的黑结点的个数相同===对一个二叉树而言,左孩子到达根结点的黑点个数等于右孩子到达根结点的个数

*/

const int maxn=44;

int k,n;

int pre[maxn],in[maxn];

map<int,int> pos;
bool res;
int build(int preL,int preR,int inL,int inR,int& sum)    //这里sum使用引用,是为了,在下面的build函数递归中,实参是ls或者rs,然后形参是sum,通过改变sum值从而直接改变ls,rs值
{
    int root=pre[preL];   //root就是根结点所表示的数值
    int k=pos[abs(root)];  //k表示根结点的下标

    if(k<inL || k>inR)        //给出的前序数组,不一定能构成一个树,需要特判
    {
        res=false;
        return 0;
    }
/*
1.这个将ls,rs设置为任意一个值都可以,主要必须是相同的值,你设置2,3,4都可以,这里面主要通过这个值判断左右孩子到达根结点的黑点的个数是否相同,后面这个sum++的目的是,为了走到下一个结点时来判断黑点的个数是否相同
2.这里将left,right设置为0,让我联想到在使用链表构建数的过程中,不是每次在递归前都有root=new node,然后后面在是root->lchild=Create...,root->rchild=Create...这里每次将left,right设置为0是一样的道理,而且你会发现在输入的过程中,根结点的下标就是从0开始的,所以每次必须将left和right初始化为0
3.这个ls,rs能发生变化主要是sum++,形参是引用,形参的改变直接影响实参ls或者rs
4.ls,rs表示的意思是除去根结点,其左孩子的黑点个数,和右孩子的黑点个数
*/

    int left=0,right=0,ls=0,rs=0;

    if(inL<k)  //构建左子树
    {
        left=build(preL+1,preL+k-inL,inL,k-1,ls);
    }

    if(k<inR)  //构建右子树
    {
        right=build(preL+k-inL+1,preR,k+1,inR,rs);
    }

    if(ls!=rs)          //对于一个二叉树而言,如果左孩子黑点个数不同于右孩子黑点个数,直接表示res为false
    {
        res=false;
    }


    sum=ls;     //将左右孩子的黑点个数用那一个表示,sum=ls表示有多条路时,用左边那条路的黑点个数进行表示,反之,用右边那条路的黑点个数表示,如下图所示

    if(root<0)        //如果root为红点,则判断其左右孩子是否是黑点,如果有一个是红点,则res为false
    {
        if(left<0 || right<0)
        {
            res=false;
        }
    }
    else         //root表示黑点
        sum++;        //为了更新ls和rs,所以到达根结点时,需要将黑点个数加1
    return root;
}

int main()
{
    cin >> k;
    while(k--)
    {
        cin >> n;
        for(int i=0; i<n; i++)
        {
            cin >> pre[i];     //输入前序数组
            in[i]=abs(pre[i]);   //对pre[i]进行绝对化
        }
        sort(in,in+n);   //排完序后,就是中序数组
        pos.clear();      //每次都要清空
        for(int i=0; i<n; i++)
        {
            pos[in[i]]=i;    //通过中序数组进行映射,对中序遍历的数组对应的数进行映射,但是对pre数组的值不映射,我发现通过映射这是为了在build函数中,找到根结点的位置也就是下标
        }

        res=true;   //最后都是将不满足条件的情况,将res设置为false,所以这里必须将res先设置为true

        int sum;   //这个sum就是在build函数中,不断的改变,以表示左右孩子的黑点个数

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

        if(root<0)      //根结点为红点,则res为fasle
        {
            res=false;
        }


        if(res) 
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}

在这里插入图片描述

sum = ls;

在这里插入图片描述

在这里插入图片描述

sum=rs;

在这里插入图片描述
在这里插入图片描述

A1123

#include<iostream>
using namespace std;

const int maxn=22;

int l[maxn],r[maxn],v[maxn],h[maxn],idx;

int q[maxn],pos[maxn];    //q 表示BFS的队列,pos表示下标

int n;

int get_balance(int root)
{
    return h[l[root]]-h[r[root]];
}

void update(int root)
{
    h[root]=max(h[l[root]],h[r[root]])+1;
}

void R(int &root)
{
    int temp=l[root];
    l[root]=r[temp];
    r[temp]=root;
    update(root),update(temp);
    root=temp;
}

void L(int &root)
{
    int temp=r[root];
    r[root]=l[temp];
    l[temp]=root;
    update(root),update(temp);
    root=temp;
}

void insert(int &root,int x)   //注意root为引用,因为二叉树的形态改变
{
    if(!root)    //如果root为0
    {
        root=++idx;
        v[root]=x;
    }
    else if(x<v[root])      //如果要插入的数小于根结点的数,则插入左子树
    {
        insert(l[root],x);          //在这里直接建立l[root],l[]数组存储的就是下标
        if(get_balance(root)==2)      //RR型
        {
            if(get_balance(l[root])==1)
                R(root);
            else          //RL型
            {
                L(l[root]);
                R(root);
            }
        }
    }
    else     //反之,将数字插入到右子树
    {
        insert(r[root],x);      //在这里直接建立r[root],同样r[]也存储下标
        if(get_balance(root)==-2)    //LL型
        {
            if(get_balance(r[root])==-1)     
                L(root);
            else        //LR型
            {
                R(r[root]);
                L(root);
            }
        }
    }
    update(root);
}

bool bfs(int root)     //BFS
{
    int hh=0,tt=0;
    q[0]=root;       //将root插入到队列q中    
    pos[root]=1;   //标记root的标记为1

    bool res=true;
    while(hh<=tt)   //如果队列不为空
    {
        int t=q[hh++];  //记录队首元素
        
        if(pos[t]>n)   //如果队首元素的位置超过n,则表示不是完全二叉树
            res=false;

        if(l[t])    //如果左子树不为空
        {
            q[++tt]=l[t];     //将左子树插入到队列中
            pos[l[t]]=pos[t]*2;      //左孩子的下标为根结点的下标的2倍
        }

        if(r[t])       //如果右子树不为空
        {
            q[++tt]=r[t];          //将右子树插入到队列中
            pos[r[t]]=pos[t]*2+1;       //右孩子的下标为根结点的2倍+1
        }
    }
    return res;     //最后返回结果
}

int main()
{
    int root=0;       //设置根结点为0
    cin >> n;
    int temp;
    for(int i=0; i<n; i++)
    {
        cin >> temp;
        insert(root,temp);    //构建AVL树
    }

    bool res=bfs(root);

    for(int i=0; i<n; i++)    
    {
        if(i!=0)
            cout << " ";
        cout << v[q[i]];        //这个q就是在BFS中,进行层序遍历的下标,按照下标输出权值
    }
    
    cout << endl;
    if(res)
        cout << "YES" << endl;
    else
        cout << "NO" << endl;

    return 0;
}

A1138

#include<iostream>
#include<vector>
using namespace std;

const int maxn=501000;

int n;

int pre[maxn],in[maxn];

struct node
{
    int data;
    node *lchild;
    node *rchild;
};

node *Create(int preL,int preR,int inL,int inR)
{
    if(preL>preR)
        return NULL;

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

    int k=0;

    for(k=inL; k<=inR; k++)
    {
        if(in[k]==pre[preL])
            break;
    }

    int num=k-inL;

    root->lchild=Create(preL+1,preL+(k-inL),inL,k-1);
    root->rchild=Create(preL+(k-inL)+1,preR,k+1,inR);

    return root;
}

vector<int> path;
void postOrder(node *root)
{
    if(root==NULL)
        return ;
    postOrder(root->lchild);
    postOrder(root->rchild);
    path.push_back(root->data);
}

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

    node *root=Create(0,n-1,0,n-1);
    postOrder(root);
    cout << path[0];
    return 0;
}

A1127

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;

const int maxn=33;

int n,in[maxn],post[maxn];

map<int,int> l,r,mp;

int q[maxn];

int build(int postL,int postR,int inL,int inR)
{
    int root=post[postR];
    int k=mp[root];

    if(inL<k)      //遍历左子树
    {
        l[root]=build(postL,postL+(k-inL)-1,inL,k-1);      //注意l,r是map,root下放的是左孩子,以后记着通过build函数得出的是孩子结点
    }

    if(k<inR)
    {
        r[root]=build(postL+(k-inL),postR-1,k+1,inR);
    }

    return root;
}

void bfs(int root)         //宽搜有没有递归
{
    int hh=0,tt=0;       //设置hh,tt
    q[0]=root;          //q表示队列,在队列中先放入root
    int step=1;         //step表示遍历的层数
    while(hh<=tt)           //队列不为空
    {
        int head=hh,tail=tt;   //head,tail分别表示遍历的每层中的左右两个结点
        while(hh<=tail)         //表示遍历完该层
        {
            int t=q[hh++];    //取出队首元素

            if(l.count(t))          //如果t的左孩子是存在的
            {
                q[++tt]=l[t];      //则将左子树放入到队列中
            }

            if(r.count(t))      //如果t的右孩子是存在的
            {
                q[++tt]=r[t];       //将右子树放入到队列中
            }
        }
        if(++step%2==0)        //第1层换,第2层不换,第3层换
            reverse(q+head,q+tail+1);
    }
}

int main()
{
    int n;
    cin >> n;
    for(int i=0; i<n; i++)
    {
        cin >> in[i];
        mp[in[i]]=i;           //通过中序数进行映射
    }

    for(int i=0; i<n; i++)
        cin >> post[i];      //输入后序数组

    int root=build(0,n-1,0,n-1);    //建立二叉树

    bfs(root);        //宽搜二叉树

    for(int i=0; i<n; i++)     //依次输出队列
    {
        if(i!=0)
            cout << " ";
        cout << q[i];
    }
    return 0;
}

A1119

#include<iostream>
using namespace std;

const int maxn=33;

int n;

int pre[maxn],post[maxn];

int build(int preL,int preR,int postL,int postR,string &in)   //建立二叉树
{
    if(preL>preR)     //这个表示空树,空树也是唯一的
        return 1;
        
    if(pre[preL]!=post[postR])    //这个表示已经构不成1颗树了,所以返回0,你想在下面lcnt和rcnt中调用build函数,当构建不成一个数的时候,lcnt和rcnt就表示0
    {
        return 0;
    }

    int cnt=0;

    for(int i=preL; i<=preR; i++)        //在前序数组中枚举左子树中所有可能的长度,也就是枚举所有点
    {
        string left,right;
        //大家都是枚举左子树,所以在前序遍历中的长度肯定等于后序遍历中的左子树长度
        int lcnt=build(preL+1,i,postL,postL+i-preL-1,left);   //lcnt,rcnt分别表示左子树中合适的情况,右子树中所有合适的情况,一共有lcnt*rcnt
        int rcnt=build(i+1,preR,postL+i-preL,postR-1,right);

        if(lcnt && rcnt)      //如果左子树中存在情况,右子树中也存在情况,则记录中序的结果
        {
            in=left+to_string(pre[preL])+" "+right;
            cnt+=lcnt*rcnt;    //cnt需要每次累加,枚举不同的左子树长度而构建出的所有情况的二叉树个数
            if(cnt>1)   //如果cnt已经超过1中情况,则退出循环
                break;
        }
    }

    return cnt;
}

int main()
{
    cin >> n;
    for(int i=0; i<n; i++)     //输入中序数组
        cin >> pre[i];
    for(int i=0; i<n; i++)
    {
        cin >> post[i];    //输入后序数组
    }
    
    string in;      //这个in表示要输出的中序数组
    int cnt=build(0,n-1,0,n-1,in);      //建立二叉树,同时统计出可能有多少种,可能的二叉树

    if(cnt>1)      //情况超过1,输出no
        cout << "No" << endl;
    else
        cout << "Yes" << endl;     //情况为0,或者1输出yes,空树也是唯一的,所以输出yes

    in.pop_back();
    cout << in << endl;     //注意输出换行,否则就是错的
    return 0;
}

A1115

/*
    通过DFS,存储每层中结点的个数,同时统计出最大的深度,如果就能输出最深两层的结点的个数

*/
#include<iostream>
#include<vector>
using namespace std;

const int maxn=1111;

int num[maxn]={0};

struct node
{
    int data;
    node *lchild;
    node *rchild;
}*root;      //这个insert函数主要将root设置为全局变量

int n;

vector<int> path;

void insert(node *&root,int x)     //注意这个root使用指针的引用
{
    if(root==NULL)
    {
        root = new node;
        root->data=x;
        root->lchild=root->rchild=NULL;
        return ;         //千万不要忘记返回值
    }

    //该二叉搜索树的定义是小于等于根结点的值插入到左子树中,大于根结点的值插入到右子树中
    if(x<=root->data)      //如果x小于根结点的值,则将x插入到左子树中
    {
        insert(root->lchild,x);
    }
    else
    {
        insert(root->rchild,x);
    }
}

int maxv=0x3f;

void dfs(node *root,int depth)
{
    num[depth]++;
    if(root==NULL)
    {
        if(depth>maxv)
        {
            maxv=depth;      //这个maxv并不是二叉树中的最大深度,只是为了我后面方便输出数字而设置的变量
        }
        return ;
    }
    if(root->lchild!=NULL)
        dfs(root->lchild,depth+1);
    if(root->rchild!=NULL)
        dfs(root->rchild,depth+1);
}

int main()
{
    cin >> n;
    int temp;
    for(int i=0; i<n; i++)      //按照结点的方式将数字插入到root中
    {
        cin >> temp;
        insert(root,temp);
    }
    dfs(root,0);
    for(int i=0; i<maxv; i++)     
    {
        if(num[i]!=0)
        {
            path.push_back(num[i]);     //统计出来数字不为0的值,如果数字为0说明就不是树了
        }
    }
    //最后找出后面两层的结点个数
    cout << path[path.size()-1] << " + " << path[path.size()-2] << " = " << path[path.size()-2]+path[path.size()-1];
}

A1110

#include<iostream>
#include<cstring>
using namespace std;

const int maxn=33;

int n;

int l[maxn],r[maxn];      //设置左右数组,下标表示根结点,元素值表示左右孩子

bool st[maxn]={false};

int maxk=-1,maxid;       //如果是完全二叉树,需要输出最后一个结点的值,我们不是从1开始对二叉树进行赋值,这样最后一个值就是表示赋的值最大的对应的值

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

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

    dfs(l[u],k*2);       //dfs遍历左子树
    dfs(r[u],k*2+1);        //遍历右子树
}

int main()
{
    memset(l,-1,sizeof(l));
    memset(r,-1,sizeof(r));
    cin >> n;
    string temp1,temp2;
    for(int i=0; i<n; i++)
    {
        cin >> temp1 >> temp2;
        if(temp1!="-")
        {
            l[i]=stoi(temp1);
            st[stoi(temp1)]=true;
        }
        if(temp2!="-")
        {
            r[i]=stoi(temp2);
            st[stoi(temp2)]=true;
        }
    }
    int root;
    for(int i=0; i<n; i++)     //找到根结点
    {
        if(st[i]==false)
        {
            root=i;
            break;
        }
    }
    dfs(root,1);       //root表示根结点,1表示我们对他赋的值
    if(maxk==n)      //如果统计出来最大的k也就是maxk为n,说明是完全二叉树,否则不是
    {
        cout << "YES " << maxid;  //是完全二叉树,输出最后一个结点也就是编号最大的对应的值
    }
    else
    {
        cout << "NO " << root;  //否则,不是输出根结点
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值