The invariant is that each node will represent a range of valid values. For a binary tree, one end is unbounded. As you go down you partition ranges.
Is Valid BST
bool isValidBST(TreeNode* root) {
const long long inf = 1e18;
auto f = [&](auto&& f, TreeNode* root, long long lo, long long hi){
if(!root) return true;
if(lo >= root -> val || hi <= root -> val) return false;
return f(f,root->left,lo, root -> val) && f(f, root->right, root->val, hi);
};
return f(f, root, -inf, inf);
}Recover BST from pre-order
note that it’ll be unique if the elements are unique
pre-order traversal is usual dfs like where you print on entry
Start with an inf right bound and keep on adding to left if we are withing bound, else returning to parent.
TreeNode* bstFromPreorder(vector<int>& pre) {
int idx = 0;
// only -inf, hi) is in range
auto f = [&](auto&& f, int hi) -> TreeNode* {
if(idx == (int)pre.size() || pre[idx] >= hi) return nullptr;
TreeNode* root = new TreeNode(pre[idx++]);
root -> left = f(f, root -> val);
root -> right = f(f, hi);
return root;
};
return f(f, (int)1e9);
}