ACM CSP竞赛笔记(十五)——图-树

参考课程是我高中信息竞赛邱老师的课程。

【二叉树遍历】 https://www.bilibili.com/video/BV1prBQYnEHd/?share_source=copy_web\&vd_source=2c56c6a2645587b49d62e5b12b253dca

【给定二叉树遍历序列恢复二叉树】 https://www.bilibili.com/video/BV1hizCY1EeU/?share_source=copy_web\&vd_source=2c56c6a2645587b49d62e5b12b253dca

【给定二叉树序列恢复二叉树习题解答】 https://www.bilibili.com/video/BV1NUzCYBE6t/?share_source=copy_web\&vd_source=2c56c6a2645587b49d62e5b12b253dca

【二叉搜索树BST】 https://www.bilibili.com/video/BV13DkLYVE5H/?share_source=copy_web\&vd_source=2c56c6a2645587b49d62e5b12b253dca

【二叉树习题讲解】 https://www.bilibili.com/video/BV11FkjYKEB7/?share_source=copy_web\&vd_source=2c56c6a2645587b49d62e5b12b253dca

需要完整板子可以去我资料获取,我设定的0积分,如果还要花钱去B站私我。

树的存储

孩子表示法

双亲表示法

孩子兄弟表示法

二叉树遍历

模板 B3642 给定左右节点的二叉树构建

https://www.luogu.com.cn/problem/B3642

将根节点存在下标的位置,然后左右子节点就是RN和LN。

然后根节点的下标是1,那么其两个子节点就是BT1.RN,BT1.LN。

输入的时候只需要根据根节点的下标输入即可。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

struct Node{
    int LN,RN;
};
Node BT[1000005];
int n;

void preOrder(int i){
    //根左右
    cout<<i<<" ";
    //如果有左子树
    if(BT[i].LN) preOrder(BT[i].LN);
    if(BT[i].RN) preOrder(BT[i].RN);
}

void inOrder(int i){
    //左根右
    //如果有左子树
    if(BT[i].LN) inOrder(BT[i].LN);
    cout<<i<<" ";
    if(BT[i].RN) inOrder(BT[i].RN);
}

void postOrder(int i){
    //左右根
    //如果有左子树
    if(BT[i].LN) postOrder(BT[i].LN);
    if(BT[i].RN) postOrder(BT[i].RN);
    cout<<i<<" ";
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>BT[i].LN>>BT[i].RN;
    }
    preOrder(1);
    cout<<endl;
    inOrder(1);
    cout<<endl;
    postOrder(1);
    cout<<endl;
    
    
}

二叉树的恢复

前+中 中+后都是可以的。

模板 P1030

https://www.luogu.com.cn/problem/P1030

输出的第一个一定是根,而能确定根的无非是前后序。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
string inOrder,postOrder;
void findPreOrder(string in,string post){
    if(in.empty()) return;//如果string排完了就返回
    char root=post[post.size()-1];//取根 根一定来自前序后序
    cout<<root;//每次都输出自己,自己一定是子树的根 【根】
    //按照根切割子树,在前后序找到左右子树然后递归
    int t=in.find(root);//string是容器,可以用find
    findPreOrder(in.substr(0,t),post.substr(0,t));//【左】
    findPreOrder(in.substr(t+1),post.substr(t,post.size()-t-1));//【右】
}

void findPostOrder(string in,string pre){
    if(in.empty()) return ;
    char root=pre[0];
    int t=in.find(root);
    findPostOrder(in.substr(0,t),pre.substr(1,t));//【左】
    findPostOrder(in.substr(t+1),pre.substr(t+1));//【右】
    cout<<root;//【根】
}
int main(){
    cin>>inOrder>>postOrder;
    findPreOrder(inOrder,postOrder);
}

前序+后序的不唯一性 P1229

https://www.luogu.com.cn/problem/P1229

为什么前序+后序无法确定唯一的中序?

只有当二叉树中存在 "只有一个孩子的节点" 时,不确定性才会产生。

  • 如果一个节点有两个孩子(左、右),那么在前序和后序中,这两个孩子的相对位置和父子关系是确定的,无法互换。
  • 如果一个节点 只有一个孩子 ,我们无法仅凭前序和后序判断这个孩子是 左孩子 还是 右孩子

就是左右根和根左右,如果左右只有一个人,你不知道这个人是左还是右。

DCE CED这种情况可以唯一恢复。即要求包夹的部分是顺序。

而 GIH HIG则不可以,H包夹的部分是逆序就会出现不知道左右子树哪边为空的情况。

那么判定条件就是看前后序相邻的逆序对了。

先把s1(先序)反转,然后一个一个扫描长度为二的子串,如果在s2(后序)也有,那么就*2.

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

string s1,s2;
long long ans=1;
int main(){
    cin>>s1>>s2;
    reverse(s1.begin(),s1.end());
    for(int i=0;i<s1.size()-1;i++){
        if(int(s2.find(s1.substr(i,2)))!=-1){
            ans*=2;
        }
    }
    cout<<ans;
}

练习1 二叉树深度 P4913

https://www.luogu.com.cn/problem/P4913

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
struct Node{
    int RN,LN;
};
Node BT[1000005];
int n;
int Depth;
void dfs(int i,int d){
    if(i==0) return;//叶子节点
    Depth=max(Depth,d);
    dfs(BT[i].LN,d+1);
    dfs(BT[i].RN,d+1);
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>BT[i].LN>>BT[i].RN;
    }
    dfs(1,1);
    cout<<Depth;
    
    
}

二叉搜索树

二叉搜索树可以通过中序遍历:左根右 的次序完成从小到大的遍历

二叉搜索树的构建 递归建树

递归建树模板 P2171

https://www.luogu.com.cn/problem/P2171

cpp 复制代码
int buildBST(int cur,int i,int depth){
    if(cur==0){//如果当前节点是空的就放这里
        Depth=max(depth,Depth);//记录最大树高
        return i;
    }
    //否则 如果插入节点的值比当前节点的值大,向右递归
    if(BST[i].val>BST[cur].val){
        BST[cur].RN=buildBST(BST[cur].RN,i,depth+1);
        //这里用BST[cur].RN接受是因为最后一层时,插入后必须连接上他的父节点才行。
        //depth是继承上一个节点的,没有问题。
    }else{
        BST[cur].LN=buildBST(BST[cur].LN,i,depth+1);
    }
    return cur;
}
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

struct Node{
    int LN,RN;
    int val;
};
int n;
Node BST[300005];
int Depth;

int buildBST(int cur,int i,int depth){
    if(cur==0){//如果当前节点是空的就放这里
        Depth=max(depth,Depth);//记录最大树高
        return i;
    }
    //否则 如果插入节点的值比当前节点的值大,向右递归
    if(BST[i].val>BST[cur].val){
        BST[cur].RN=buildBST(BST[cur].RN,i,depth+1);
        //这里用BST[cur].RN接受是因为最后一层时,插入后必须连接上他的父节点才行。
        //depth是继承上一个节点的,没有问题。
    }else{
        BST[cur].LN=buildBST(BST[cur].LN,i,depth+1);
    }
    return cur;
}

void postOrder(int i){
    if(i==0){
        return;
    }
    postOrder(BST[i].LN);
    postOrder(BST[i].RN);
    cout<<BST[i].val<<endl;
}

int main(){
    cin>>n;
    int val;
    cin>>BST[1].val;
    Depth=1;
    for(int i=2;i<=n;i++){
        cin>>BST[i].val;
        buildBST(1,i,1);//递归建树
    }
    cout<<"deep="<<Depth<<endl;
    postOrder(1);
}