思路
要检查一颗二叉树是否为二叉搜索树,或者二叉搜索树的镜像。
按照题意,就是把升序、降序的前序序列作为中序分别进行检查。
但是题目涵盖了重复值的节点。所以用unordered_map
容器再记录这些值的中序位置不太方便了。
- 题目说明了重复值必将出现在二叉搜索树的右子树(或二叉搜索树镜像的左子树)
- 所以在检查二叉搜索树的时候,在中序序列(升序排列)找到首次出现
val
的索引 - 对于二叉搜索树镜像,则在中序序列(降序排列)中找到**最后一次出现
val
**的索引 - 作为改进,搜索根的中序位置的时候可以使用二分进行加速。
代码
#include<bits/stdc++.h>
using namespace std;
struct TreeNode {
int val;
TreeNode*left, *right;
TreeNode(int v):val(v), left(nullptr), right(nullptr){};
};
vector<int> preorder, inorder;
bool flag = true;
TreeNode * build_tree(int preL, int preR, int inL, int inR, bool mirrorflag = false) {
if(preL > preR)
return nullptr;
int val = preorder[preL];
auto root = new TreeNode(val);
int inIdx = -1;
for(int i = inL; i <= inR; ++i) {
if(inorder[i] == val) {
inIdx = i;
if(! mirrorflag) // 检查二叉搜索树时找第一个,检查镜像时找最后一个
break;
}
}
// 如果树不合法则返回
if(inIdx == -1) {
flag = false;
return nullptr;
}
int n_left = inIdx - inL;
root->left = build_tree(preL + 1, preL + n_left, inL, inIdx - 1, mirrorflag);
root->right = build_tree(preL + n_left + 1, preR, inIdx + 1, inR, mirrorflag);
if(!flag)
return nullptr;
return root;
}
TreeNode * pre = nullptr;
void postOrder(TreeNode * root) {
if(!root)
return;
postOrder(root->left);
postOrder(root->right);
if(pre)
printf(" ");
printf("%d", root->val);
pre = root;
}
int main() {
int n;
cin >> n;
preorder.resize(n);
for(int i = 0; i < n; ++i)
cin >> preorder[i];
inorder = preorder;
sort(inorder.begin(), inorder.end());
auto root = build_tree(0, n - 1, 0, n - 1);
if(! root) { // 不是二叉搜索树,但有可能是二叉搜索树的镜像,其中序即为降序排列
reverse(inorder.begin(), inorder.end());
flag = true; // 重置合法标记
}
if(root || (root = build_tree(0, n - 1, 0, n - 1, true))) {
printf("YES\n");
postOrder(root);
printf("\n");
} else printf("NO\n");
}