语言: C++
题目描述 :
给定一个有 N 个节点的二叉树,每个节点都有一个不同于其他节点且处于 {1, ..., N} 中的值。
通过交换节点的左子节点和右子节点,可以翻转该二叉树中的节点。
考虑从根节点开始的先序遍历报告的 N 值序列。将这一 N 值序列称为树的行程。
(回想一下,节点的先序遍历意味着我们报告当前节点的值,然后先序遍历左子节点,再先序遍历右子节点。)
我们的目标是翻转最少的树中节点,以便树的行程与给定的行程 voyage 相匹配。
如果可以,则返回翻转的所有节点的值的列表。你可以按任何顺序返回答案。
如果不能,则返回列表 [-1]。
示例 1:
输入:root = [1,2], voyage = [2,1]
输出:[-1]
示例 2:输入:root = [1,2,3], voyage = [1,3,2]
输出:[1]
示例 3:输入:root = [1,2,3], voyage = [1,2,3]
输出:[ ]提示:
1 <= N <= 100
来源:力扣(LeetCode)
OJ链接:https://leetcode-cn.com/problems/flip-binary-tree-to-match-preorder-traversal
思路分析 :
关键点: 范围[1 - N] , 无重复节点, 前序遍历
思路比较简单, 既然没有重复节点, 就不用考虑判断过的节点是否需要重新判断一遍的问题. 例如下面的二叉树
在匹配 [1, 2, 3, 4, 5, 6, 7, 2, 3, 6, 5, 4, 7 ] 这个序列时, 只需要把根节点的左右孩子(蓝色圈)交换就可以了, 但在前序遍历二叉树的过程中, 遍历到2时并无法判断出是否要交换, 所以一直要遍历到6(红色圈), 此时发现与序列不匹配, 之前所有遍历过的祖先节点都有可能是满足条件的交换节点.  ̄へ ̄ 说了这么多, 本题不需要考虑这种情况
所以只需要在前序遍历二叉树的同时也顺序遍历给出的序列, 依次比较遍历到的节点与序列是否相等, 记录遍历的当前节点
的父节点, 如果当前节点与序列不相等, 则交换记录的父节点的两个左右孩子. 再从父节点开始往下遍历.
注意:
1.在交换左右孩子时, 只要有一个孩子为空, 肯定不匹配
2. 当交换后的还没相等, 肯定不匹配
class Solution {
bool Swap(TreeNode*& root) {
if (!root->left || !root->right) {//在交换左右孩子时, 只要有一个孩子为空, 肯定不匹配
return false;
}
TreeNode* tmp = root->left;
root->left = root->right;
root->right = tmp;
return true;
}
public:
vector<int> flipMatchVoyage(TreeNode* root, vector<int>& voyage) {
vector<int> res;
stack<TreeNode*> s;
bool flag = false;//标记是否出现不匹配的节点,如出现则置true,交换后相等则再置为false
TreeNode* f = root;
if (root->val != voyage[0]) {
res.push_back(-1);
return res;
}
int size = voyage.size();
for (int i = 1; root && i < size;) {
f = root;
if (root->right) s.push(root->right);
if (root->left) s.push(root->left);
if (s.empty()) {
root = nullptr;
}
else {
root = s.top();
s.pop();
//判断
if (root->val != voyage[i]) {
if (flag) {//交换后的还没相等, 肯定不匹配
res.clear();
res.push_back(-1);
return res;
}
flag = true;
if (Swap(f)) {
if (!s.empty())s.pop();
root = f;
}
else {
res.clear();
res.push_back(-1);
return res;
}
}
else {
if (flag) {
flag = false;
res.push_back(f->val);
}
++i;
}
}
}
return res;
}
};