为了账号安全,请及时绑定邮箱和手机立即绑定

洛谷P3365 改造二叉树:从问题分析到代码实现

标签:
C++

https://img1.sycdn.imooc.com/43cedb680870805209240765.jpg

一、问题分析

题目要求我们计算将二叉树修改为二叉搜索树(BST)所需的最少修改次数。二叉搜索树的性质是:对于任意节点,其左子树所有节点的值都小于该节点的值,右子树所有节点的值都大于该节点的值。

二、解题思路


  1. 中序遍历序列‌:BST的中序遍历结果是一个严格递增序列

  2. 问题转化‌:将原二叉树的中序遍历序列转换为严格递增序列所需的最少修改次数

  3. 最长递增子序列(LIS)‌:最少修改次数 = 序列长度 - 最长递增子序列长度

三、C++代码实现

#include <iostream>
#include <vector>
#include <algorithm>
using namespACe std;

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

// 构建二叉树
TreeNode* buildTree(int n, const vector<int>& vals, const vector<pair<int, int>>& edges) {
    vector<TreeNode*> nodes(n + 1);
    for (int i = 1; i <= n; ++i) {
        nodes[i] = new TreeNode(vals[i - 1]);
    }
    
    for (int i = 2; i <= n; ++i) {
        int fa = edges[i - 2].first;
        int ch = edges[i - 2].second;
        if (ch == 0) {
            nodes[fa]->left = nodes[i];
        } else {
            nodes[fa]->right = nodes[i];
        }
    }
    return nodes[1];
}

// 中序遍历收集节点值
void inorder(TreeNode* root, vector<int>& seq) {
    if (!root) return;
    inorder(root->left, seq);
    seq.push_back(root->val);
    inorder(root->right, seq);
}

// 计算最长递增子序列长度
int lengthOfLIS(vector<int>& nums) {
    vector<int> dp;
    for (int num : nums) {
        auto it = lower_bound(dp.begin(), dp.end(), num);
        if (it == dp.end()) {
            dp.push_back(num);
        } else {
            *it = num;
        }
    }
    return dp.size();
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    
    vector<int> vals(n);
    for (int i = 0; i < n; ++i) {
        cin >> vals[i];
    }
    
    vector<pair<int, int>> edges(n - 1);
    for (int i = 0; i < n - 1; ++i) {
        cin >> edges[i].first >> edges[i].second;
    }
    
    TreeNode* root = buildTree(n, vals, edges);
    vector<int> seq;
    inorder(root, seq);
    
    int lis_len = lengthOfLIS(seq);
    cout << n - lis_len << endl;
    
    return 0;
}

四、 代码详解

数据结构定义

我们首先定义树节点的结构:

struct TreeNode {    int val;
    TreeNode *left;
    TreeNode *right;    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}};

2 构建二叉树

根据输入构建二叉树:

TreeNode* buildTree(int n, const vector<int>& vals, const vector<pair<int, int>>& edges) {    vector<TreeNode*> nodes(n + 1);    for (int i = 1; i <= n; ++i) {
        nodes[i] = new TreeNode(vals[i - 1]);
    }    
    for (int i = 2; i <= n; ++i) {        int fa = edges[i - 2].first;        int ch = edges[i - 2].second;        if (ch == 0) {
            nodes[fa]->left = nodes[i];
        } else {
            nodes[fa]->right = nodes[i];
        }
    }    return nodes[1];}

3 中序遍历

收集中序遍历序列:

void inorder(TreeNode* root, vector<int>& seq) {    if (!root) return;    inorder(root->left, seq);
    seq.push_back(root->val);    inorder(root->right, seq);}

4 计算最长递增子序列

使用贪心算法计算LIS长度:

int lengthOfLIS(vector<int>& nums) {
    vector<int> dp;    for (int num : nums) {        auto it = lower_bound(dp.begin(), dp.end(), num);        if (it == dp.end()) {
            dp.push_back(num);
        } else {
            *it = num;
        }
    }    return dp.size();}

五、总结

通过将问题转化为中序遍历序列的最长递增子序列问题,我们能够高效地计算出将任意二叉树修改为BST所需的最少修改次数。这种方法结合了树遍历和动态规划的思想,展示了算法设计中问题转化的重要性。

来源:洛谷P3365 改造二叉树:从问题分析到代码实现


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消