与二叉树有关算法题

文章目录

  • [1. 新⼆叉树](#1. 新⼆叉树)
  • [2. 二叉树的遍历](#2. 二叉树的遍历)
  • [3. 二叉树的深度](#3. 二叉树的深度)
  • [4. 求先序排列](#4. 求先序排列)
  • [5. American Heritage](#5. American Heritage)
  • [6. P3884 [JLOI2009] ⼆叉树问题(存疑)](#6. P3884 [JLOI2009] ⼆叉树问题(存疑))

1. 新⼆叉树

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

  1. 建树:和常规的链式存储方式一致。
    因为结点是字符,所以可以直接用 ASCII 码值当做下标来使用。比如 'a' 直接映射成 97,l[97] 里面就存着 'a' 的左儿子,r[97] 里面就存着 'a' 的右儿子,以此类推,建立二叉树。
  2. 先序遍历:根左右。
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

int n;
const int N =300;
char l[N],r[N];

void dfs(char root)
{
    if(root=='*')
    {
        return;
    }
    cout<<root;
    dfs(l[root]);
    dfs(r[root]);
}

int main()
{
    cin>>n;
    char a,root;
    cin>>root;
    cin>>l[root]>>r[root];
    for(int i=2;i<=n;i++)
    {
        cin>>a;
        cin>>l[a]>>r[a];
    }
    dfs(root);
    
}

2. 二叉树的遍历

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


建树+dfs遍历即可!

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n;
const int N=1e6+10;
int l[N],r[N];

void dfs1(int root)
{
    if(root==0)
    {
        return;
    }
    cout<<root<<" ";
    dfs1(l[root]);
    dfs1(r[root]);
}
void dfs2(int root)
{
    if(root==0)
    {
        return;
    }
    dfs2(l[root]);
    cout<<root<<" ";
    dfs2(r[root]);
}
void dfs3(int root)
{
    if(root==0)
    {
        return;
    }
    dfs3(l[root]);
    dfs3(r[root]);
    cout<<root<<" ";
}


int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>l[i]>>r[i];
    }
   dfs1(1);
    cout<<endl;
    dfs2(1);
    cout<<endl;
    dfs3(1);
    
}

3. 二叉树的深度

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


⼆叉树的⾼度 = 1 + max(左⼦树的⾼度, 右⼦树的⾼度);

因此,可以递归解决。

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

const int N = 1e6 + 10;
int n;
int l[N], r[N];

int dfs(int root)
{
    if(root==0)
    {
        return 0;
    }
    return max(dfs(l[root]),dfs(r[root]))+1;
}


int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> l[i] >> r[i];
}
cout << dfs(1) << endl;
return 0;
}

4. 求先序排列

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


我们先⽤⼀个具体的例⼦模拟⼀下,在已知「中序」和「后序」的基础上,如何求「先序」:

  • s1:中序遍历字符串 (Inorder)
  • s2:后序遍历字符串 (Postorder)
  • 参数含义:
    • l1, r1:当前子树在 中序 (s1) 中的左右边界下标。
    • l2, r2:当前子树在 后序 (s2) 中的左右边界下标。

实例解析:
输入数据:

  • 中序 (s1):BADC (下标 0~3)
  • 后序 (s2):BDCA (下标 0~3)

目标: 输出前序遍历。
预期结果: ABCD (根A -> 左B -> 右C -> 右D? 不对,让我们一步步推导)

第一轮调用 (整棵树): dfs(0, 3, 0, 3)

  • 范围
    • 中序 s1[0...3] = "BADC"
    • 后序 s2[0...3] = "BDCA"
  • 找根节点
    • 后序遍历的最后一个元素一定是根。
    • s2[r2]s2[3] = 'A'
    • 代码执行 while(s1[p] != s2[r2]) p++;
      • pl1(0) 开始。
      • s1[0]='B' != 'A' -> p++
      • s1[1]='A' == 'A' -> 停止。
      • 此时 p = 1
  • 输出根cout << 'A'(当前输出:A)
  • 划分左右子树
    • 在中序 s1 中,p=1 ('A') 左边是左子树,右边是右子树。
    • 左子树 (中序): s1[0...0] ("B")。长度 = 1。
    • 右子树 (中序): s1[2...3] ("DC")。长度 = 2。
  • 递归调用
    1. 左子树递归dfs(l1, p-1, l2, l2+p-1-l1)
      • 参数:dfs(0, 0, 0, 0+1-1-0) -> dfs(0, 0, 0, 0)
    2. 右子树递归dfs(p+1, r1, l2+p-l1, r2-1)
      • 参数:dfs(2, 3, 0+1-0, 3-1) -> dfs(2, 3, 1, 2)

第二轮调用 (处理左子树): dfs(0, 0, 0, 0)

  • 范围
    • 中序 s1[0...0] = "B"
    • 后序 s2[0...0] = "B"
  • 找根节点
    • 后序最后一个 s2[0] = 'B'
    • while 循环:s1[0] ('B') == 'B',p 保持为 0
  • 输出根cout << 'B'(当前输出:AB)
  • 递归调用
    1. 左子树dfs(0, -1, ...) -> l1 > r1,直接 return
    2. 右子树dfs(1, 0, ...) -> l1 > r1,直接 return
  • 返回 到第一轮。

第三轮调用 (处理右子树): dfs(2, 3, 1, 2)

  • 范围
    • 中序 s1[2...3] = "DC" (注意:s1[2]='D', s1[3]='C')
    • 后序 s2[1...2] = "DC" (注意:s2[1]='D', s2[2]='C')
    • 解释:原后序是 BDCA,去掉根A和左子树B,剩下 DC 对应右子树。
  • 找根节点
    • 后序最后一个 s2[r2]s2[2] = 'C'
    • while 循环:
      • pl1(2) 开始。
      • s1[2]='D' != 'C' -> p++ (p=3)
      • s1[3]='C' == 'C' -> 停止。
      • 此时 p = 3
  • 输出根cout << 'C'(当前输出:ABC)
  • 划分左右子树
    • 在中序 s1 中,p=3 ('C') 左边是左子树,右边无。
    • 左子树 (中序): s1[2...2] ("D")。
    • 右子树 (中序): 空。
  • 递归调用
    1. 左子树递归dfs(2, 2, 1, 1+3-1-2) -> dfs(2, 2, 1, 1)
      • 计算细节:l2=1, p=3, l1=2. 新 r2 = 1 + 3 - 1 - 2 = 1.
    2. 右子树递归dfs(4, 3, ...) -> l1 > r1,直接 return

--- 第四轮调用 (处理右子树的左孩子): dfs(2, 2, 1, 1)

  • 范围
    • 中序 s1[2...2] = "D"
    • 后序 s2[1...1] = "D"
  • 找根节点
    • 后序最后一个 s2[1] = 'D'
    • while 循环:s1[2] == 'D',p = 2
  • 输出根cout << 'D'(当前输出:ABCD)
  • 递归调用:左右均为空,返回。

  • 中序验证 :左(B) -> 根(A) -> 右(D, C) => BADC (符合)
  • 后序验证 :左(B) -> 右(D, C) -> 根(A) => BDCA (符合)
  • 前序输出 :根(A) -> 左(B) -> 右(C, D) => ABCD (符合代码输出)
  1. 左子树的后序范围
    • 左子树的长度 = p - l1 (在中序中,根下标 p 减去 左边界 l1)。
    • 后序遍历中,左子树紧挨着起始位置 l2
    • 所以左子树在后序中的结束位置 = l2 + 长度 - 1 = l2 + (p - l1) - 1
    • 代码:l2 + p - 1 - l1
  2. 右子树的后序范围
    • 右子树在后序中的起始位置 = 左子树结束位置 + 1 = (l2 + p - l1 - 1) + 1 = l2 + p - l1
    • 右子树在后序中的结束位置 = 当前结束位置 - 1 (因为最后一个是根) = r2 - 1
    • 代码:l2 + p - l1, r2 - 1
      ⚠️:请自己边画边结合上面描述理解,其实很简单的!重点是一定要画!不能光看!
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
string s1,s2;
int n;
int l1,l2,r1,r2;

void dfs(int l1,int r1,int l2,int r2)
{
    //退出调节
    if(l1>r1)
    {
        return;
    }
    int p=l1;//用p记录根结点
    while(s1[p]!=s2[r2]) p++;
    cout<<s2[r2];
   //以p为分界点,左右子树分别再次bfs
    dfs(l1,p-1,l2,l2+p-1-l1);
    dfs(p+1,r1,l2+p-l1,r2-1);
}



int main()
{
    cin>>s1>>s2;
    l1=0,l2=0;
    r1=s1.size()-1,r2=s2.size()-1;
    dfs(l1,r1,l2,r2);
    return 0;
}

5. American Heritage

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


解法同第四题,如果第四题掌握了,那太简单了,不想多说,可以参考一下博主做这道题的时候画的分析图:

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

string s1,s2;//s1 中 s2 后

void dfs(int l1,int r1,int l2,int r2)
{
    if(l1>r1)  return;
    int p=l1;
    while(s1[p]!=s2[l2]) p++;
    dfs(l1,p-1,l2+1,l2+p-l1);
    dfs(p+1,r1,l2+p-l1+1,r2);
    cout<<s2[l2];

}




int main()
{
    cin>>s1>>s2;
    dfs(0,s1.size()-1,0,s2.size()-1);
    return 0;
}

6. P3884 [JLOI2009] ⼆叉树问题(存疑)

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


【解法】

深度:递归。

宽度:宽搜。

两点之间的距离:通过向上不断找父结点。第一个重叠的位置,就是两者的最近公共祖先。可以一边寻找,一边计算结果。

首先,由于它没有明确说明谁是左右孩子,所以只能用vector数组来存储,里面dfs的递归思想也是很常见,bfs就是用队列模拟找哪一层最多也不解释,最难的两点距离,本质就是求公共的祖先,用一个fa[N]数组存储父亲结点,然后把其中一个结点一直往上遍历直到根结点,然后存储中间遍历的所有结点,然后让另一个结点往上爬,遇到第一个中间遍历过的结点就行了

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n;
const int N=110;
int fa[N],dist[N];
vector<int> vc[N];

int dfs(int root)
{
    int ret=0;
    for(auto e:vc[root])
    {
        ret=max(ret,dfs(e));
    }
    return ret+1;
}

int bfs(int root)
{
    queue<int> q;
    int ret=0;
    q.push(root);
    while(q.size())
    {
        int num=q.size();
        ret=max(ret,num);
        while(num--)
        {
            for(auto e:vc[q.front()])
            {
                q.push(e);
            }
            q.pop();
        }
    }
    return ret;
}


int main()
{
    cin>>n;
    int u,v;
    for(int i=1;i<n;i++)
    {
        cin>>u>>v;
        vc[u].push_back(v);
        fa[v]=u;
    }
    cin>>u>>v;
    cout<<dfs(1)<<endl;
    cout<<bfs(1)<<endl;
    int x=u;
    while(x!=1)
    {
        dist[fa[x]]=dist[x]+1;
        x=fa[x];
    }
  int y=v;
 int len = 0;
 //⚠️:我认为这个逻辑是有问题的 主播如果最后输入 3 7 明眼人看距离都是1 但是输出是4 奇怪的是最后居然特喵AC了!!主播不是很懂 有懂的希望留言交流谢谢!
 while(y != 1 && dist[y] == 0)
 {
   len++;
   y = fa[y];
}
    cout << dist[y] * 2 + len << endl;
    
}
相关推荐
️是782 小时前
信息奥赛一本通—编程启蒙(3346:【例60.3】 找素数)
数据结构·c++·算法
captain3762 小时前
map和set
数据结构·算法
qq_416018722 小时前
实时数据可视化库
开发语言·c++·算法
格林威2 小时前
工业相机参数解析:曝光时间与运动模糊的“生死博弈”
c++·人工智能·数码相机·opencv·算法·计算机视觉·工业相机
2401_873204652 小时前
C++中的策略模式进阶
开发语言·c++·算法
xushichao19892 小时前
C++中的职责链模式实战
开发语言·c++·算法
大鹏说大话2 小时前
数据库查询优化全攻略:从索引设计到架构演进
算法
小O的算法实验室2 小时前
2025年IEEE TETCI SCI2区,一种用于二次无约束二进制优化的协同神经动力学算法,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
2301_818419012 小时前
C++中的协程编程
开发语言·c++·算法