LC.2471 | 逐层排序二叉树所需的最少操作数目 |树 | BFS + 最小交换次数

输入:一棵二叉树根节点 root,节点值互不相同

要求:按层序从上到下,使每一层节点值变为升序,每一步允许交换同一层任意两个节点的值

输出:最少交换次数


思路:本题表面上是二叉树层序遍历的应用,但实质难点在于如何计算让数组有序的最小交换次数。层序遍历只是获取数据的手段,真正的核心在于对每一层数据的处理。

1. 总体流程
  • 层序遍历:利用队列获取二叉树每一层的所有节点值。

  • 处理每一层

    • 将该层数组拷贝一份并排序,得到“目标状态”。

    • 计算将原数组变为目标数组所需的最小交换次数。

  • 累加结果:将所有层的交换次数相加即为最终答案。

2. 核心算法:图论模型(置换环)

我们可以将这一层数组的排序问题转化为图论问题:

  • 节点:当前位置上的数字。

  • 连线关系:如果数字当前在位置A,但排序后它应该在位置B,这就构成了一条从A指向B的有向边。

  • 环的形成:顺着这些边走,最终一定会回到起点,形成一个闭环。

结论:对于一个长度为k的环,我们恰好需要k-1次交换就可以让环内所有元素归位。

3. 深度证明:为什么是 k-1 次?

为了严谨地说明“为什么k-1是最优解”,我们需要从两个角度证明:能做到(充分性)不能更少(必要性)

证明 1:充分性(为什么 k-1 次一定能搞定?)

  • 在环中任选一个位置 i,将其当前持有的数字交换到它原本应该在的位置。

  • 效果:这一次交换后,数字回到了正确位置(即退出了环),原本长度为k的环,剩下了k-1个错位元素,形成了一个长度为k-1的新环。

  • 推导

    • 每做 1 次交换,环的长度就减 1。

    • 当执行了k-1次交换后,环的长度变为 1(即只剩最后一个元素),该元素因其他位置已满,必然自动归位。

    • 得证:k-1 次交换足以完成任务。

证明 2:必要性(为什么不可能更少?)

  • 初始状态:我们要解决的是 1 个长度为 k 的大环(错位状态)。

  • 目标状态:排序完成意味着每个人都“自成一环”(即 k 个长度为 1 的小环)。

  • 交换的本质:在同一个环内交换任意两个节点,其数学本质是将这个环一分为二(即环的数量 +1)。我们不可能通过一次交换将一个环拆成三个或更多。

  • 计算下界

    • 我们需要让环的数量从 1 增加到 k

    • 中间的缺口是k - 1个环。

    • 因为每次操作最多只能贡献 1 个新环,所以我们至少需要k-1次操作才能填补这个缺口。

    • 得证:少于k-1次无法达到目标状态。

这一题感觉还是稍显复杂,感觉目前以记住结论为主,进阶之后再尝试做困难题 LC.765. 情侣牵手


复杂度:

        时间复杂度:O(Nlog N)

        空间复杂度:O(N)


class Solution {
public:
    int minimumOperations(TreeNode* root) {
        int total_ops = 0;
        if (!root) return 0;
        queue<TreeNode*> q;
        q.push(root);

        while (!q.empty()) {
            int size = q.size();
            vector<int> level_vals;
            for (int i = 0; i < size; ++i) {
                TreeNode* node = q.front();
                q.pop();
                
                level_vals.push_back(node->val);
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }

            total_ops += countlen(level_vals);
        }

        return total_ops;
    }

    int countlen(vector<int>& n) {
        int ans = 0;
        vector<int> t = n;
        sort(t.begin(), t.end());
        
        map<int, int> origingreater; 
        map<int, int> originmark;    

        for (int i = 0; i < n.size(); i++) {
            originmark[n[i]] = -1; 
        }
        for (int i = 0; i < t.size(); i++) {
            origingreater[t[i]] = i; 
        }

        for (int i = 0; i < n.size(); i++) {
            if (originmark[n[i]] != -1) {
                continue; 
            }
            
            int curr_idx = i;
            int len = 0;
            
            while (originmark[n[curr_idx]] == -1) {
                originmark[n[curr_idx]] = 1; 
                len++;
                curr_idx = origingreater[n[curr_idx]]; 
            }
            
            if (len > 1) {
                ans += len - 1; 
            }
        }
        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值