10 篇文章 0 订阅
9 篇文章 0 订阅

二叉树

树叶子节点PAT1004

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

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

const int N = 110;
int n,m;
//用数组模拟链表模拟树
int e[N],ne[N],head[N],idx;
int max_height;
int every_height_leaf[N];

void add(int a,int b)
{
    e[idx] = b;
    ne[idx] = head[a];
    head[a] = idx;
    idx++;
}

void dfs(int node,int depth)
{
    max_height = max(max_height,depth);
    if(head[node]==-1){
        every_height_leaf[depth]++;
        return;
    }
    for(int i=head[node];i!=-1;i=ne[i])
    {
        int now_node = e[i];
        dfs(now_node,depth+1);
    }
}

int main()
{
    cin >> n >> m;
    memset(head,-1,sizeof head);
    
    while(m--)
    {
        int id,k;
        cin >> id >> k;
        for(int i=0;i<k;i++){
            int num;
            cin >> num;
            add(id,num);
        }
    }
    dfs(1,0);
    for(int i=0;i<=max_height;i++) cout << every_height_leaf[i]<<" ";
    return 0;
}

树的遍历PAT1020

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

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<unordered_map>
using namespace std;

const int N = 40;
int n;
int inorder[N],postorder[N];  //记录中序和后续的顺序
unordered_map<int,int> l,r,pos;   //pos记录在中序遍历中i节点的下标
 
int build(int in_l,int in_r,int post_l,int post_r)
{
    int root = postorder[post_r];
    int k = pos[root];//在中序遍历中根节点的位置
    if(k>in_l) l[root] = build(in_l,k-1,post_l,post_l+(k-1-in_l));
    if(k<in_r) r[root] = build(k+1,in_r,post_l+(k-1-in_l)+1,post_r-1);
    
    return root;
    
}


int main()
{
    cin >> n;
    for(int i=0;i<n;i++) cin >> postorder[i];
    for(int i=0;i<n;i++){
        cin >> inorder[i];
        pos[inorder[i]] = i;
    }
    
    int the_root = build(0,n-1,0,n-1);
    queue<int> q;
    q.push(the_root);
    int head = q.front();
    q.pop();
    if(l[head]) q.push(l[head]);
    if(r[head]) q.push(r[head]);
    cout << head;
    while(!q.empty())
    {
        int now_node = q.front();
        q.pop();
        if(l[now_node]) q.push(l[now_node]);
        if(r[now_node]) q.push(r[now_node]);
        cout <<" " <<now_node;
    }
    return 0;
}

c++中的set就是由二叉搜索树实现的

PAT1024

在这里插入图片描述
在这里插入图片描述
这个题给了一个序列,这个序列可能是一个BST的前序序列或者是其镜像的前序序列
由于一个BST的中序序列一定是有序的,所以我们思想就是,假定给的序列是正确的前序序列,看看能不能构造出一个树
如果是正确的,那么一定可以构造出一个树,反之,则不可以
而对于BST的镜像,如果给的序列是BST镜像的前序遍历,那么其中序遍历就是从大到小的一个排列了

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

const int N = 1010;
int n;
int preorder[N],inorder[N],postorder[N];
int cnt;
//镜像之前中序遍历是从小到大的顺讯,镜像之后的中序遍历是一个从大到小的顺序

//对于这个BST左子树的值是严格小于根节点的
//所以比如 1 2 3 4 5 5 6 7 8这个中序遍历,有两个5,那就只能认为第一个5是根节点了
//如果第二个5作为根节点,那么左子树就不严格小于根节点了
//反之对于镜像的8 7 6 5 5 4 3 2 1 我们找的是最后一个5

bool build(int il,int ir,int prel,int prer,int type)//type表示输入的是镜像还是正常
{
    //如果当前区间没有元素了,那么就可以重建
    if(il>ir) return true;
    
    //看看根节点能在中序序列中找到吗
    //因为如果是一个树的正确前序序列,那么在中序中一定能找到
    int root = preorder[prel];
    int k;  //k是根节点在中序中的位置
    if(type==0)
    {
        for(k = il;k<=ir;k++)
        {
            if(inorder[k]==root) break;
        }
        if(k>ir) return false;
    }
    else{
        for(k=ir;k>=il;k--)
        {
            if(inorder[k]==root) break;
        }
        if(k<il) return false;
    }
    
    //找到根节点后,看看左右子树能不能构建成功
    bool res = true;
    if(!build(il,k-1,prel+1,prel+1+k-1-il,type)) res = false;  //先建立左子树 
    if(!build(k+1,ir,prel+1+k-1-il+1,prer,type)) res = false;   //再建立右子树
    
    postorder[cnt] = root;  //这条语句是慢执行于44,45行这两条语句的,所以是先给postorder数组找到左右子树再赋值根节点
    cnt++;
    return res;
}
int main()
{
    cin >> n;
    for(int i=0;i<n;i++)
    {
        cin >> preorder[i];
        inorder[i] = preorder[i];
    }
    sort(inorder,inorder+n);
    
    cnt = 0;
    if(build(0,n-1,0,n-1,0))
    {
        puts("YES");
        cout<<postorder[0];
        for(int i=1;i<n;i++) cout<<" "<<postorder[i]; 
    }
    else{//如果没成功,那么我们要考虑是不是镜像的前序了
        reverse(inorder,inorder+n);
        
        cnt = 0;//这个cnt=0必须写,因为来到这个分支,之前已经执行过一个build了,cnt已经变了
        if(build(0,n-1,0,n-1,1))
        {
            puts("YES");
            cout<<postorder[0];
            for(int i=1;i<n;i++) cout<<" "<<postorder[i]; 
        }
        else{
            puts("NO");
        }
    }
    return 0;
}

PAT1064

在这里插入图片描述
在这里插入图片描述
完全二叉树的好处就是他用一个一维数组就可以存下n个节点了,下标从1-n
i号节点的左儿子是2i,右儿子是2i+1
并且层序遍历就是这个数组从1-n的遍历

如果我们要构造一个完全二叉搜索树,那么

  1. 构造一个完全二叉树
  2. 这些节点有序序列就是二叉搜索树的中序遍历,那么我们把中序遍历的这个序列填进去就好了
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 1010;
int n;
int node[N];  //记录权重,之后经过排序是存储的中序序列
int bst[N];   //记录构造的BST下标从1开始

void dfs(int v,int& k)
{
    if(2*v<=n) dfs(2*v,k);  //找到左子树
    bst[v] = node[k];       //给根节点赋值
    k++;
    if(2*v+1 <= n) dfs(2*v+1,k); //找到右子树
}

int main()
{
    cin >> n;
    for(int i=0;i<n;i++) cin >> node[i];
    sort(node,node+n);
    int k=0;
    dfs(1,k);
    for(int i=1;i<=n;i++) cout<<bst[i]<<" ";
    return 0;
    
}

PAT1086

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

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

const int N = 40;
int n;
stack<int> st;
int type = -1;  //type记录上一次操作是push还是pop
                //0表示是pop,1表示是push,-1代表还没操作


//因为根据非递归写法,上次操作是push的话这次push的节点是上个节点的左儿子
//如果上次操作是pop的话,这次push的节点是上次节点的右儿子
int root;  //记录根节点是哪个
int l[N],r[N];  //记录节点的左右儿子
int father;  //记录当前操作的上一次操作的节点号

//后序遍历输出,左右根
//后序遍历最后一个节点一定是根节点,由于PAT不能有行末空格
//所以判断当前点是不是根节点来确定加不加空格
void dfs(int u,int root)
{
    if(!u) return;  //没有左右儿子值是0
    dfs(l[u],root);
    dfs(r[u],root);
    cout << u;
    if(u!=root) cout<<" ";
}

int main()
{
    cin >> n;
    for(int i=0;i<n*2;i++)
    {
        string op;
        cin >> op;
        if(op=="Push")
        {
            int num;
            cin >> num;
            if(type==-1)
            {
                root = num;
            }
            else if(type==0)//上次是pop
            {
                r[father] = num;
            }
            else if(type==1){
                l[father] = num;
            }
            father = num;
            type = 1;
            st.push(num);
        }
        else if(op=="Pop")
        {
            type = 0;
            father = st.top();
            st.pop();
        }
        
    }
    dfs(root,root);
    return 0;
}

PAT1099

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

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

const int N = 110;
int n;
int l[N],r[N];
int read[N],tree[N];  //read数组记录中序遍历结果,tree是我们最终构建的树

//进行中序遍历,k是当前要从read中填入的数的下标
void dfs(int u,int &k)
{
    if(u == -1) return;
    
    dfs(l[u],k);
    tree[u] = read[k];
    k++;
    dfs(r[u],k);
}

queue<int> q;
void BFS(int u)
{
    q.push(u);
    while(!q.empty())
    {
        int node = q.front();
        q.pop();
        if(l[node]!=-1){
            q.push(l[node]);
        }
        if(r[node]!=-1){
            q.push(r[node]);
        }
        cout << tree[node] << " ";
    }
}

int main()
{
    memset(l,-1,sizeof l);
    memset(r,-1,sizeof r);
    
    cin >> n;
    for(int i=0;i<n;i++) cin >> l[i] >> r[i];
    for(int i=0;i<n;i++) cin >> read[i];
    sort(read,read+n);
    
    int k = 0;  //这里k一定要赋值0,不要就写个int k;  否则会有段错误
    int root = 0;  //这里根节点是0号位置
    dfs(root,k);
    BFS(root);
    return 0;
}

PAT1102

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里翻转的意思就是把树关于中轴线做个轴对称,那么翻转我们只要把左右儿子进行交换就好了

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

const int N = 20;
int n;
int l[N],r[N];
bool have_father[N]; //由于题目没说哪个是根节点,所以需要求出父节点是哪个

void dfs_reverse(int u)
{
    if(u==-1) return;
    
    dfs_reverse(l[u]);
    dfs_reverse(r[u]);
    swap(l[u],r[u]);
}

queue<int> q;
void bfs(int u)
{
    q.push(u);
    while(!q.empty())
    {
        int node = q.front();
        q.pop();
        if(l[node]!=-1) q.push(l[node]);
        if(r[node]!=-1) q.push(r[node]);
        cout << node <<" ";
    }
    cout <<endl;
}

void dfs_inorder(int u)
{
    if(u==-1) return;
    
    dfs_inorder(l[u]);
    cout << u <<" ";
    dfs_inorder(r[u]);
    
}

int main()
{
    memset(l,-1,sizeof l);
    memset(r,-1,sizeof r);
    cin >> n;
    for(int i=0;i<n;i++)
    {
        char lop,rop;
        cin >> lop >> rop;
        if(lop != '-'){
            l[i] = lop - '0';
            have_father[l[i]] = true;
        }
        if(rop != '-'){
            r[i] = rop - '0';
            have_father[r[i]] = true;
        }
    }
    
    int root = 0;
    for(int i=0;i<n;i++){
        if(have_father[i]){
            root++;
        }
        else{
            break;
        }
    }
    //把二叉树翻转只要交换每个节点的左右子树就好了
    dfs_reverse(root);

    bfs(root);
    dfs_inorder(root);
    return 0;
}

完全二叉树PAT1110

在这里插入图片描述
在这里插入图片描述
对于一个完全二叉树,如果是完全二叉树的话,一定可以把这个数存到一个一维数组中的1~n号位置中
如果他不能够存到一维数组的1~n号位置中,那么他就不是完全二叉树

思路就是如果不是完全二叉树,那么会假象用空节点补全成完全二叉树,那么最后构造出的最后一个节点在
一维数组中的下标一定是大于n的

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

const int N = 25;
int n;
int l[N],r[N];
bool have_father[N];


int maxk,lastid;   //maxk表示假设是个完全二叉树,一维数组的最大下标号
                  //maxid表示最后一个节点的ID是啥
void dfs(int u,int k) //k表示u号节点在一维数组中的下标位置
{
    if(u==-1) return;
    if(k>maxk){
        maxk = k;
        lastid = u;
    }
    dfs(l[u],2*k);
    dfs(r[u],2*k+1);
}

int main()
{
    memset(l,-1,sizeof l);
    memset(r,-1,sizeof r);
    cin >> n;
    for(int i=0;i<n;i++)
    {
        string a,b;
        cin >> a >> b;
        if(a!="-"){
            l[i] = stoi(a);
            have_father[l[i]] = true;
        }
        if(b!="-"){
            r[i] = stoi(b);
            have_father[r[i]] = true;
        }
    }
    int root = 0;
    while(have_father[root]) root++;
    
    
    dfs(root,1);
    if(maxk==n){
        printf("YES %d",lastid);
    }
    else{
        printf("NO %d",root);
    }
    return 0;
}

PAT1115

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

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

const int N = 1010;
int n;
int l[N],r[N],e[N],idx;  //idx记录当前可用的节点编号
void insert(int& u,int v)  //由于这里写了引用u所以,左右儿子的节点会自动更新成下标
{
    if(u==0){
        idx++;
        u = idx;
        e[u] = v;
    }
    else if(v<=e[u]) insert(l[u],v);
    else if(v>e[u]) insert(r[u],v);
}
int cnt[N],max_depth;  //cnt记录每层最大节点数量
void dfs(int u,int depth)
{
    if(u==0) return;
    cnt[depth]++;
    max_depth = max(max_depth,depth);
    dfs(l[u],depth+1);
    dfs(r[u],depth+1);
}


int main()
{
    cin >> n;
    int root = 0;  
    for(int i=0;i<n;i++)
    {
        int w;
        cin >> w;
        insert(root,w);
    }
    dfs(root,0); //根节点是第0层(防止空树情况所以用第0层)
    int a = cnt[max_depth],b = cnt[max_depth-1];
    printf("%d + %d = %d",a,b,a+b);
    return 0;
    
}

PAT1119 前序遍历 + 后序遍历 输出可能二叉树

在这里插入图片描述
在这里插入图片描述
中序遍历+ 后序遍历 或者 前序遍历 确定唯一二叉树
后序遍历 + 前序遍历 不能唯一确定一个二叉树

就是如果给了你前序遍历和后序遍历
以前序遍历为例,你只知道第一个节点是根节点,你不知道左子树和右子树的长度
这个题节点数比较小,所以可以暴搜枚举左子树长度,来找到一个合适的二叉树构造

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

const int N = 40;
int n;
int pre[N],post[N];

//是前序和后序树的包含的节点的位置序列
int dfs(int prel,int prer,int postl,int postr,string & scheme)
{
    //递归边界
    if(prel > prer) return 1;  //达到这个条件说明这个方案是成功的,因为我们子树长度是由prer,prel定的
    //当前子树构建成功
    
    if(pre[prel] != post[postr]) return 0;   //如果当前前序根节点根节点与后序根节点不同,则方案不成功
    
    int cnt = 0;  //代表当前方案
    //下面枚举左子树包含的节点数量
    for(int i=prel;i<=prer;i++)  //i从prel开始是左子树需要从空开始,不可以从prel+1开始这样是左子树从长度为1开始了
    {
        string scheme_l,scheme_r;  //记录左右子树中序方案
        int lcnt = dfs(prel+1,i,postl,postl+i-prel-1,scheme_l);
        int rcnt = dfs(i+1,prer,postl+i-prel,postr-1,scheme_r);
        
        if(lcnt && rcnt)
        {
            scheme = scheme_l + to_string(pre[prel]) + " " + scheme_r;
            cnt += lcnt * rcnt;
            if(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 scheme;   //记录中序遍历结果
    int cnt;
    cnt = dfs(0,n-1,0,n-1,scheme);  //dfs返回构造的树的数量
    
    if(cnt > 1) puts("No");
    else puts("Yes");
    scheme.pop_back();
    cout << scheme;
    return 0;
}

Z字型遍历二叉树

思想就是按照层序遍历来遍历二叉树,然后每一层单独存储遍历结果,根据是奇数层还是偶数层翻转对应层序序列即可
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N = 40;
unordered_map<int,int> l,r,pos;
int postorder[N],inorder[N];

int dfs(int in_l,int in_r,int post_l,int post_r)
{
    int root = postorder[post_r];
    if(in_l < pos[root]) l[root] = dfs(in_l,pos[root]-1,post_l,post_l+pos[root]-1-in_l);
    if(in_r > pos[root]) r[root] = dfs(pos[root]+1,in_r,post_l+pos[root]-in_l,post_r-1);
    return root;
}



int main()
{
    int n;
    cin >> n;
    for(int i=0;i<n;i++)
    {
        cin >> inorder[i];
        pos[inorder[i]] = i;
    }
    for(int i=0;i<n;i++) cin >> postorder[i];
    
    int root = postorder[n-1];
    dfs(0,n-1,0,n-1);
    
    int q[N];
    int hh = 0,tt = -1;
    q[hh] = root;
    tt++;
    int level = 0;
    
    while(hh<=tt)
    {
        int head = hh,tail = tt;
        
        while(hh <= tail)
        {
            int now_node = q[hh++];
            if(l[now_node]!=0) q[++tt] = l[now_node];
            if(r[now_node]!=0) q[++tt] = r[now_node];
        }
        
        level++;
        if(level % 2) reverse(q+head,q+tail+1);
    }
    
    for(int i=0;i<n;i++) cout << q[i] <<" ";
    
    
    
    return 0;
}

AVL树

AVL树详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新城里的旧少年^_^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值