输入:一棵二叉树根节点 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;
}
};

被折叠的 条评论
为什么被折叠?



