Below are two structurally identical solutions for checking if one binary tree is a subtree of another. Only the control-flow style differs.
class Solution {
fun isSubtree(root: TreeNode?, subRoot: TreeNode?): Boolean =
root != null && ( // guard once
root isIdenticalTo subRoot || // same tree here?
isSubtree(root.left, subRoot) || // …or in left branch?
isSubtree(root.right, subRoot) // …or in right branch?
)
private infix fun TreeNode?.isIdenticalTo(other: TreeNode?): Boolean =
(this == null && other == null) ||
(this != null && other != null &&
this.`val` == other.`val` &&
this.left isIdenticalTo other.left &&
this.right isIdenticalTo other.right)
}class Solution {
fun isSubtree(root: TreeNode?, subRoot: TreeNode?): Boolean {
if (root == null) return false
if (root isIdenticalTo subRoot) return true
return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot)
}
private infix fun TreeNode?.isIdenticalTo(other: TreeNode?): Boolean {
if (this == null && other == null) return true
if (this == null || other == null) return false
if (this.`val` != other.`val`) return false
return this.left isIdenticalTo other.left &&
this.right isIdenticalTo other.right
}
}| Axis | Expression-Oriented | Imperative |
|---|---|---|
| Primary construct | Boolean expression, one = body whose value is computed. |
Statements & returns with if (...) return .... |
| Null guard | Single guard (root != null && ...) wraps entire logic. |
First if (root == null) return false exits early. |
| Read flow | Evaluates entire logic as a formula. | Reads top-down like a checklist. |
| State/side-effects | Pure value expression. | Early returns organize logic into branches. |
| Debugging | Harder to break mid-expression. | Easy to insert logs and breakpoints. |
| Extensibility | Add new checks inside the expression. | Add new guard clauses explicitly. |
| Stylistic debt | Denser; needs clear formatting. | Verbose but very readable. |
| Mental model | Algebraic equation = value. | Checklist of rules. |
| Prefer Expression-Oriented when... | Prefer Imperative when... |
|---|---|
| Team is comfortable with functional idioms. | Team defaults to Java/Kotlin guard-clauses. |
| You want to match spec/pseudocode cleanly. | You expect heavy logging or condition handling. |
| You prefer minimal return paths. | You value step-by-step clarity. |
Both solutions run in O(n) time and O(h) space. Your choice of style is about clarity for the reader, not performance.
Write for the next person reading your code—especially if that's you six months from now.