003 — Binary Tree Preorder Traversal¶
Source¶
- Original source: Classic tree traversal exercise
- Platform: LeetCode — https://leetcode.com/problems/binary-tree-preorder-traversal/
- Topic: trees
- Difficulty: Easy
- Pattern: Traversal order
- Basic idea inherited from: Inorder, visit-মুহূর্ত সরানো — value নেওয়ার লাইন একদম উপরে তুলে দাও
1. Problem in simple words¶
তোমাকে একটা binary tree-র root দেওয়া আছে। সব value একটা list-এ ফেরত দাও — preorder ক্রমে, মানে Node → Left → Right।
প্রতিটা node-এ: আগে নিজের value নাও, তারপর পুরো বাঁ-দিক, তারপর পুরো ডান-দিক। parent সবসময় তার সব descendant-এর আগে আসে।
2. Which basic idea does this inherit from?¶
এটা inorder-এরই যমজ ভাই (এই tracker-এর #2)। একই recursion কঙ্কাল, শুধু value নেওয়ার লাইনটা মাঝখান থেকে একদম উপরে তুলে দেওয়া হয়েছে — তাই node তার child-দের আগে visit হয়। "visit-মুহূর্ত সরানো" — এটাই তিন DFS traversal-এর মধ্যে পার্থক্য।
3. What is the hidden math or logic?¶
লুকানো logic আবার একটা ক্রমের সংজ্ঞা:
এর একটা গুরুত্বপূর্ণ ধর্ম: parent সবসময় আগে লেখা হয় বলে preorder output থেকে tree-টা top-down আবার বানানো যায়। এজন্যই preorder হলো copy / serialize-এর স্বাভাবিক ক্রম (traversal-patterns.md দেখো)।
4. Which data structure is needed?¶
একটা output list আর recursion call stack। recursion-এর বদলে চাইলে একটা explicit stack (../../04-stack-and-queue/) — root push করো, pop করে visit করো, তারপর RIGHT তারপর LEFT push করো (যাতে left আগে pop হয়)।
5. Why this data structure fits¶
Tree self-similar, তাই recursion প্রতিটা subtree আলাদা সামলায়। preorder-এ আমরা node-এ পৌঁছেই (on ARRIVAL) value নিই, তাই কোনো জিনিস "মনে রেখে পরে নেওয়ার" ঝামেলা নেই — পৌঁছালাম, লিখলাম, নিচে নামলাম। stack/recursion শুধু "এরপর কোন দিক বাকি" ধরে রাখে।
6. Real-life analogy¶
একটা বইয়ের table of contents ভাবো। আগে chapter-এর শিরোনাম লেখা হয়, তারপর তার সব section, তারপর পরের chapter। তুমি একটা heading-এ পৌঁছেই সেটা লিখে ফেলো, তারপর তার ভেতরে ঢোকো। এটাই preorder — "আগে আমি, তারপর আমার ভেতরের সব।"
7. Visual explanation¶
★ মানে value নেওয়া হলো — preorder-এ সেটা node-এ পৌঁছানোমাত্রই (নিচে নামার আগে):
8. Example input and output¶
Input : root = [1,null,2,3]
Output: [1, 2, 3]
Input : root = [] -> Output: [] (খালি tree)
Input : root = [1] -> Output: [1] (একটা node)
9. Brute force thinking¶
Traversal নিজেই কাজ, তাই brute force বলতে iterative explicit-stack version — recursion ছাড়া হাতে stack চালানো।
1) root-কে stack-এ push করো
2) stack থেকে pop করো → value নাও
3) আগে RIGHT, তারপর LEFT push করো (যাতে left আগে pop হয়)
4) stack খালি না হওয়া পর্যন্ত ধাপ 2 চালাও
10. Why brute force is slow¶
Iterative version-ও O(n), slow না — কিন্তু "right তারপর left push" উল্টো বোধ হয়, ভুল হওয়া সহজ। recursion সেই subtlety নিজেই সামলায়, code পরিষ্কার থাকে। iterative লাগে শুধু খুব গভীর tree-তে Python-এর recursion limit এড়াতে।
11. Key observation¶
মূল observation: inorder-এর তুলনায় শুধু একটা লাইন উপরে উঠেছে — append এখন দুই recursive call-এর আগে। তাই node তার বংশধরদের আগে লেখা হয়। ক্রম বদলানো মানে শুধু এই এক লাইন সরানো।
12. Optimized intuition¶
তিন লাইনের recursion: এই node-এর value append করো, বাঁয়ে নামো, ডানে নামো। None-এ চুপচাপ ফিরে যাও। একবার tree ঘোরা — প্রতিটা node পৌঁছানোমাত্র লেখা হয়।
13. Thinking tweak¶
মোচড়: "preorder মানে কী" আলাদা করে মুখস্থ না করে ভাবো — "inorder-এর append লাইনটা টেনে একদম উপরে তুলে দিলাম।" তিন traversal = একটাই কঙ্কাল, append-লাইনের তিনটা অবস্থান (উপরে / মাঝে / নিচে)।
14. Step-by-step dry run¶
Input [1,null,2,3] (tree: 1-এর right 2, 2-এর left 3):
visit(1): পৌঁছেই append1। out = [1]।visit(1.left = None): চুপচাপ ফিরল।visit(1.right = 2): append2। out = [1, 2]।visit(2.left = 3): append3। out = [1, 2, 3]। left/right None → ফিরল।- সব শেষ → [1, 2, 3]।
15. Python solution¶
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(values):
"""level-order list (None = missing child) থেকে tree বানায়।"""
if not values:
return None
root = TreeNode(values[0])
q = deque([root])
i = 1
while q and i < len(values):
node = q.popleft()
if i < len(values) and values[i] is not None:
node.left = TreeNode(values[i])
q.append(node.left)
i += 1
if i < len(values) and values[i] is not None:
node.right = TreeNode(values[i])
q.append(node.right)
i += 1
return root
def preorder_traversal(root):
out = []
def visit(node):
if node is None: # base case: খালি subtree
return
out.append(node.val) # 1) পৌঁছেই নিজের value (on ARRIVAL)
visit(node.left) # 2) তারপর পুরো বাঁ-দিক
visit(node.right) # 3) তারপর পুরো ডান-দিক
visit(root)
return out
# ---- tests ----
assert preorder_traversal(build_tree([1, None, 2, 3])) == [1, 2, 3]
assert preorder_traversal(build_tree([])) == []
assert preorder_traversal(build_tree([1])) == [1]
assert preorder_traversal(build_tree([1, 2, 3, 4, 5])) == [1, 2, 4, 5, 3]
print("সব test pass!")
16. Line-by-line code explanation¶
out = []— value জমানোর জায়গা।if node is None: return— base case; খালি subtree থেকে কিছু যোগ হয় না।out.append(node.val)— মূল চাল: node-এ পৌঁছেই, নিচে নামার আগে value নাও।visit(node.left)/visit(node.right)— তারপর বাঁ, তারপর ডান।return out— পুরো preorder sequence।
17. Output walkthrough¶
[1,null,2,3] → [1, 2, 3] (dry run-এর সাথে মেলে)। খালি tree → []; একক node → [1]; [1,2,3,4,5] → [1,2,4,5,3] (ছবির সাথে মেলে)। সব assert pass, শেষে print: সব test pass!।
18. Time complexity¶
O(n) — প্রতিটা node ঠিক একবার visit ও append।
19. Space complexity¶
O(n) output list-এর জন্য; recursion stack আলাদাভাবে O(h) (balanced-এ O(log n), chain-এ O(n))।
20. Common mistakes¶
appendলাইন নিচে রেখে ভুল করে inorder/postorder বানিয়ে ফেলা।- iterative version-এ left আর right push-এর ক্রম উল্টে দেওয়া (right আগে push করতে হয়)।
- base case বাদ দিয়ে
Noneছোঁয়া।
21. Which basic problem this inherits from¶
ভিত্তি: Inorder Traversal (#2) — একই কঙ্কাল, append-লাইন উপরে তোলা। সাথে traversal-patterns.md-র preorder sketch, যেখানে দেখানো হয় কেন preorder = copy/serialize-এর ক্রম।
22. Similar harder problems¶
- Construct Binary Tree from Preorder and Inorder Traversal (preorder = top-down root, inorder দিয়ে split) — https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ (এই tracker-এর #21)
- Serialize and Deserialize Binary Tree (preorder + None marker) — https://leetcode.com/problems/serialize-and-deserialize-binary-tree/ (#24)
- Binary Tree Postorder Traversal (append-লাইন সবার নিচে) — https://leetcode.com/problems/binary-tree-postorder-traversal/ (#4)
23. Pattern learned¶
DFS visit-order — arrival version: value নাও node-এ পৌঁছানোমাত্র, তাই parent আগে। এই top-down ধর্মই tree clone, serialize, আর "decision উপর থেকে নিচে পাঠানো" সব problem-এর ভিত্তি।
24. Final summary¶
ভবিষ্যতের আমাকে: "preorder = Node, Left, Right — পৌঁছেই value নাও, parent আগে।" copy বা serialize-এর কথা শুনলেই preorder মনে করবে। আর তিন traversal গুলিয়ে গেলে মনে রেখো: একটাই কঙ্কাল, append-লাইনের অবস্থানই সব।
মনে রাখার নিয়ম (legal): সব নিজের ভাষায় লেখা; problem statement copy করা হয়নি; official link শুধু attribution-এর জন্য; "asked by Google/Amazon" দাবি নেই — এটা একটা common interview pattern।