You can do a depth-first search over the tree without needing to cache the minimum and maximum values for each subtree in a single pass, but you have to be careful, as comparing the values between and parent and its children is not enough. For example, in the tree:
(10
(7
nil
(20 nil nil)
)
nil
)
The left child (7) of the root (10) satisfy the inequalty (7 <= 10), as does the right child (20) of 7 (20 >= 7). However, the tree is not a BST (Binary Search Tree), because 20 should not be in the left subtree of 10.
To fix this, you may do a traversal passing to extra arguments specifying the valid interval for the subtree.
// The valid interval for the subtree root's value is (lower_bound, upper_bound).
bool IsBST(const node_t* tree, int lower_bound, int upper_bound) {
if (tree == NULL) return true; // An empty subtree is OK.
if (tree->value <= lower_bound) return false; // Value in the root is too low.
if (tree->value >= upper_bound) return false; // Value in the root is too high.
// Values at the left subtree should be strictly lower than tree->value
// and be inside the root valid interval.
if (!IsBST(tree->left, lower_bound, tree->value))
return false;
// Values at the left subtree should be strictly greater than tree->value
// and be inside the root valid interval.
if (!IsBST(tree->right, tree->value, upper_bound))
return false;
// Everything is OK, it is a valid BST.
return true;
}
Notice that by keeping the original valid interval, the function will detect that 20 is invalid at that position, as it is not inside (7, 10). The first call should be done with an infinite interval, like:
IsBST(tree, INT_MIN, INT_MAX);
Hope this helps.