เส้นผ่านศูนย์กลาง ความสูง และต้นไม้สมดุล
คำนวณเส้นผ่านศูนย์กลางและความสูงของต้นไม้ด้วยการทำ DFS รอบเดียว โดยใช้ตัวช่วยที่คืนค่าทั้งสองค่า แล้วตรวจสอบว่าต้นไม้สมดุลด้านความสูงหรือไม่
เส้นผ่านศูนย์กลาง ความสูง และต้นไม้สมดุล เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
height ของต้นไม้ทวิภาค
height (หรือความลึกสูงสุด) ของต้นไม้ทวิภาคคือความยาวของเส้นทางที่ยาวที่สุดจากรากไปยังโหนดใบใด ๆ โดยคำนวณแบบเรียกซ้ำได้ดังนี้: height ของโหนดใด ๆ คือ 1 + max(height(left), height(right)) โดยมีกรณีฐานเป็น 0 สำหรับโหนดค่าว่าง การคำนวณแบบโพสต์ออร์เดอร์นี้เป็นพื้นฐานสำคัญ — height เป็นองค์ประกอบตั้งต้นของเส้นผ่านศูนย์กลาง การตรวจสอบความสมดุล และการหมุนต้นไม้ AVL
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def height(root):
if not root:
return 0
return 1 + max(height(root.left), height(root.right))
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
root.left.left.left = TreeNode(6)
print(height(root)) # 4เส้นผ่านศูนย์กลาง: เส้นทางที่ยาวที่สุด
เส้นผ่านศูนย์กลางของต้นไม้ทวิภาคคือความยาวของเส้นทางที่ยาวที่สุดระหว่างโหนดสองโหนดใด ๆ (เส้นทางอาจผ่านรากหรือไม่ผ่านก็ได้) ความยาวของเส้นทางวัดเป็น เส้นเชื่อม สำหรับโหนดใด ๆ เส้นผ่านศูนย์กลางที่ผ่านโหนดนั้นมีค่าเท่ากับ height(left) + height(right) ส่วนเส้นผ่านศูนย์กลางโดยรวมคือค่าที่มากที่สุดของค่าดังกล่าวในทุกโหนดของต้นไม้
def diameter_of_binary_tree(root):
max_diameter = [0] # use list to allow closure mutation
def dfs(node):
if not node:
return 0
left_h = dfs(node.left)
right_h = dfs(node.right)
# Diameter through this node
max_diameter[0] = max(max_diameter[0], left_h + right_h)
return 1 + max(left_h, right_h) # height for parent
dfs(root)
return max_diameter[0]
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
print(diameter_of_binary_tree(root)) # 3การค้นหาเส้นผ่านศูนย์กลางด้วย DFS รอบเดียว
วิธีตรงไปตรงมาจะเรียก height() ที่ทุกโหนด ทำให้ใช้เวลา O(n²) สำหรับต้นไม้สมดุล วิธีที่มีประสิทธิภาพสูงสุดคือคำนวณ height และอัปเดตเส้นผ่านศูนย์กลางในการ ทำ DFS รอบเดียว แนวคิดสำคัญคือฟังก์ชันเรียกซ้ำ dfs() ทำหน้าที่ สองอย่างพร้อมกัน: คืนค่า height ให้โหนดแม่ และอัปเดตค่าเส้นผ่านศูนย์กลางสูงสุดโดยรวมเป็นผลข้างเคียง รูปแบบโพสต์ออร์เดอร์ที่มีสองหน้าที่นี้ปรากฏในโจทย์เกี่ยวกับต้นไม้อีกมากมาย
# O(n^2) NAIVE: recomputes height for every node
def diameter_naive(root):
if not root:
return 0
through_root = height(root.left) + height(root.right)
in_left = diameter_naive(root.left)
in_right = diameter_naive(root.right)
return max(through_root, in_left, in_right)
# O(n) OPTIMAL: single DFS pass (shown in previous scene)
# The naive version is O(n^2) because height() is O(n)
# and it is called for every node.
print('Naive: O(n^2) | Optimal single-pass: O(n)')การตรวจสอบต้นไม้ทวิภาคสมดุล
ต้นไม้ทวิภาคจะ สมดุลตาม height หาก height ของต้นไม้ย่อยด้านซ้ายและขวาของทุกโหนดแตกต่างกันไม่เกินหนึ่ง วิธีแบบใช้กำลังคำนวณตรง ๆ จะเรียก height() ที่ทุกโหนด ทำให้ใช้เวลา O(n²) วิธีที่มีประสิทธิภาพสูงสุดใช้เทคนิคการประมวลผลรอบเดียวแบบเดียวกัน โดยคืนค่า -1 เป็นค่าพิเศษแทน «ไม่สมดุล» แล้วส่งค่านี้ขึ้นไป และหยุดประมวลผลทันทีที่พบโหนดไม่สมดุล
def is_balanced(root):
def check(node):
if not node:
return 0
left = check(node.left)
if left == -1:
return -1 # propagate early exit
right = check(node.right)
if right == -1:
return -1
if abs(left - right) > 1:
return -1 # unbalanced here
return 1 + max(left, right) # height if balanced
return check(root) != -1
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.left.left = TreeNode(5) # too deep on left
print(is_balanced(root)) # Falseรูปแบบการคืนค่าพิเศษแทนสถานะ
การคืนค่า ค่าพิเศษแทนสถานะ (-1 สำหรับกรณีไม่สมดุล หรือทูเพิลพิเศษ) เป็นรูปแบบที่ใช้บ่อยเมื่อฟังก์ชันช่วยของ DFS ต้องส่งข้อมูลสองประเภท ได้แก่ ผลลัพธ์ที่คำนวณได้ และข้อมูลว่ามีการละเมิดข้อจำกัดหรือไม่ แทนที่จะโยนข้อยกเว้นหรือใช้ตัวบ่งชี้ส่วนกลาง ให้เข้ารหัสข้อผิดพลาดไว้ในชนิดค่าที่คืน วิธีนี้ชัดเจน ไม่ต้องใช้สถานะส่วนกลาง และทำงานร่วมกับฟังก์ชันช่วยแบบเรียกซ้ำอื่น ๆ ได้อย่างเป็นธรรมชาติ
# General pattern: return (is_valid, computed_value)
def balanced_height(node):
if not node:
return True, 0
left_ok, left_h = balanced_height(node.left)
if not left_ok:
return False, 0 # short-circuit
right_ok, right_h = balanced_height(node.right)
if not right_ok:
return False, 0
balanced = abs(left_h - right_h) <= 1
return balanced, 1 + max(left_h, right_h)
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
ok, h = balanced_height(root)
print(ok, h) # True 2เส้นผ่านศูนย์กลางในรูปจำนวนโหนดเทียบกับจำนวนเส้นเชื่อม
โปรดระวังข้อความของโจทย์: LeetCode #543 วัดเส้นผ่านศูนย์กลางเป็น เส้นเชื่อม ขณะที่บางโจทย์วัดเป็น โหนด หากต้องการนับจำนวนโหนด เส้นผ่านศูนย์กลางที่ผ่านโหนดหนึ่งมีค่าเป็น height(left) + height(right) + 1 (บวก 1 สำหรับตัวโหนดเอง) หากต้องการนับจำนวนเส้นเชื่อม ให้ตัด +1 ออก ควรสอบถามผู้สัมภาษณ์ให้ชัดเจนเรื่องนี้ก่อนเขียนโค้ดเสมอ
def diameter_in_nodes(root):
max_path = [0]
def dfs(node):
if not node:
return 0
left_h = dfs(node.left)
right_h = dfs(node.right)
# Path through this node in NODE count
nodes_through = left_h + right_h + 1
max_path[0] = max(max_path[0], nodes_through)
return 1 + max(left_h, right_h)
dfs(root)
return max_path[0]
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
print(diameter_in_nodes(root)) # 4 nodes: 4-2-1-3 or 5-2-1-3ผลรวมเส้นทาง: เส้นทางใด ๆ จากรากถึงใบ
โจทย์ ผลรวมเส้นทางถามว่า มีเส้นทางใด ๆ จากรากถึงใบที่มีผลรวมเท่ากับค่าเป้าหมายหรือไม่ ให้ใช้ DFS และลบค่าของโหนดปัจจุบันออกจากค่าเป้าหมายขณะเดินลงไป เมื่อถึงโหนดใบ ให้ตรวจสอบว่าเป้าหมายที่เหลืออยู่เท่ากับค่าของโหนดใบหรือไม่ นี่คือ DFS แบบพรีออร์เดอร์ที่ส่ง ผลรวมที่เหลืออยู่เป็นพารามิเตอร์ และเป็นตัวอย่างคลาสสิกของการเรียกซ้ำจากบนลงล่าง
def has_path_sum(root, target):
if not root:
return False
# Leaf node: check if we've exactly hit the target
if not root.left and not root.right:
return root.val == target
remaining = target - root.val
return (has_path_sum(root.left, remaining) or
has_path_sum(root.right, remaining))
root = TreeNode(5)
root.left = TreeNode(4)
root.right = TreeNode(8)
root.left.left = TreeNode(11)
root.left.left.left = TreeNode(7)
root.left.left.right = TreeNode(2)
print(has_path_sum(root, 22)) # True: 5+4+11+2=22ผลรวมเส้นทางสูงสุด (รูปแบบยาก)
ผลรวมเส้นทางสูงสุด (LeetCode #124) ยากขึ้นอย่างมาก: เส้นทางสามารถเริ่มต้นและสิ้นสุดที่โหนดใดก็ได้ ไม่จำเป็นต้องเป็นเส้นทางจากรากถึงใบ และค่าต่าง ๆ อาจเป็นลบ ที่แต่ละโหนด ให้พิจารณาสี่ทางเลือก ได้แก่ ตัวโหนดเอง โหนดบวกแขนงซ้าย โหนดบวกแขนงขวา หรือโหนดบวกแขนงทั้งสอง มีเพียงสามตัวเลือกแรกที่สามารถต่อขึ้นไปยังโหนดแม่ได้ ส่วนตัวเลือกที่สี่เป็นค่าผู้สมัครสุดท้ายสำหรับค่าสูงสุดโดยรวม
def max_path_sum(root):
max_sum = [float('-inf')]
def gain(node):
if not node:
return 0
# Only take positive contributions
left = max(gain(node.left), 0)
right = max(gain(node.right), 0)
# Best path through this node (can't go both ways upward)
max_sum[0] = max(max_sum[0], node.val + left + right)
# Return the best single-branch gain for parent
return node.val + max(left, right)
gain(root)
return max_sum[0]
root = TreeNode(-10)
root.left = TreeNode(9)
root.right = TreeNode(20)
root.right.left = TreeNode(15)
root.right.right = TreeNode(7)
print(max_path_sum(root)) # 42: 15+20+7ต้นไม้ AVL และการปรับสมดุลด้วยตัวเอง
ต้นไม้ AVLคือ BST ที่รักษาคุณสมบัติสมดุลตาม height ด้วยการหมุนหลังการแทรกและการลบ แต่ละโหนดเก็บ ตัวประกอบสมดุล (height(right) - height(left)) ซึ่งต้องอยู่ใน {-1, 0, 1} เมื่อเกิดการละเมิด การหมุนหนึ่งครั้งหรือสองครั้งจะคืนความสมดุลได้ในเวลา O(1) ทำให้ height โดยรวมอยู่ที่ O(log n) และรับประกันว่าการดำเนินการทั้งหมดใช้เวลา O(log n)
# Balance factor = height(right) - height(left)
# AVL invariant: balance factor in {-1, 0, 1} for every node
# Four violation types and their fixes:
# LL (left-heavy left child): single right rotation
# RR (right-heavy right child): single left rotation
# LR (right-heavy left child): left rotate child, then right rotate root
# RL (left-heavy right child): right rotate child, then left rotate root
# Knowing this is enough for interviews; you rarely implement
# full AVL in an interview but must discuss the concept.
print('AVL maintains O(log n) height via rotations')การตรวจสอบต้นไม้สมมาตร
ต้นไม้ทวิภาคจะ สมมาตร หากเป็นภาพสะท้อนของตัวเอง ให้ตรวจสอบแบบเรียกซ้ำ: ต้นไม้จะสมมาตรก็ต่อเมื่อสำหรับโหนดแต่ละคู่ที่สอดคล้องกันคนละด้านของแกน โหนดทั้งสองมีค่าเท่ากัน และต้นไม้ย่อยของทั้งสองเป็นภาพสะท้อนกัน ให้กำหนดฟังก์ชันช่วย is_mirror(left, right) เพื่อตรวจสอบว่า: ทั้งคู่เป็นค่าว่าง (ถูกต้อง) มีโหนดหนึ่งเป็นค่าว่าง (ไม่ถูกต้อง) หรือค่าทั้งสองเท่ากันและต้นไม้ย่อยด้านในกับด้านนอกเป็นภาพสะท้อนกัน
def is_symmetric(root):
def is_mirror(left, right):
if not left and not right:
return True
if not left or not right:
return False
return (left.val == right.val and
is_mirror(left.left, right.right) and
is_mirror(left.right, right.left))
return is_mirror(root.left, root.right)
sym = TreeNode(1)
sym.left = TreeNode(2)
sym.right = TreeNode(2)
sym.left.left = TreeNode(3)
sym.right.right = TreeNode(3)
print(is_symmetric(sym)) # True
nosym = TreeNode(1)
nosym.left = TreeNode(2)
nosym.right = TreeNode(2)
nosym.left.right = TreeNode(3)
print(is_symmetric(nosym)) # Falseการผสานข้อสังเกตเรื่องความสูงและเส้นผ่านศูนย์กลาง
รูปแบบโพสต์ออร์เดอร์ในรอบเดียว ซึ่งฟังก์ชันผู้ช่วยส่งคืนความสูงพร้อมกับปรับปรุงผลลัพธ์ส่วนกลางไปพร้อมกัน สามารถนำกลับมาใช้กับปัญหาได้หลากหลาย เช่น เส้นผ่านศูนย์กลาง ผลรวมเส้นทางสูงสุด การตรวจสอบความสมดุล การนับโหนดที่มีคุณสมบัติดี และอื่น ๆ ควรถามตัวเองเสมอว่า 'โหนดแม่ต้องการข้อมูลอะไรจากโหนดลูกแต่ละโหนด' นั่นคือค่าที่ส่งคืน และ 'การคำนวณใดเป็นการคำนวณเฉพาะที่โหนดนี้' นั่นคือสิ่งที่ใช้ปรับปรุงคำตอบส่วนกลาง การแยกองค์ประกอบเช่นนี้คือทักษะสำคัญสำหรับปัญหาเกี่ยวกับต้นไม้ที่ซับซ้อน
# Reusable template for post-order dual-purpose DFS:
def tree_problem(root):
result = [float('-inf')] # or 0 depending on problem
def dfs(node):
if not node:
return 0 # base return (height, count, etc.)
left_val = dfs(node.left)
right_val = dfs(node.right)
# --- Update global result using both children ---
candidate = left_val + right_val # example: diameter
result[0] = max(result[0], candidate)
# --- Return info needed by PARENT ---
return 1 + max(left_val, right_val) # example: height
dfs(root)
return result[0]
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
print(tree_problem(root)) # diameter = 2ตรวจสอบความเข้าใจ
ทดสอบความเข้าใจแนวคิดโครงสร้างข้อมูลและอัลกอริทึม — การเตรียมตัวสัมภาษณ์การเขียนโปรแกรมจากบทเรียนนี้
ทบทวนบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้: การคำนวณความสูง โดยใช้ DFS แบบเรียกซ้ำในลำดับโพสต์ออร์เดอร์ การคำนวณเส้นผ่านศูนย์กลาง ในการท่องเพียงรอบเดียวที่ใช้เวลา O(n) ด้วยฟังก์ชันผู้ช่วย DFS ที่มีสองหน้าที่ และ การตรวจสอบความสมดุล ด้วยตัวบ่งชี้สำหรับออกก่อนกำหนด บทถัดไปเราจะจัดการกับปัญหาผลรวมเส้นทางและบรรพบุรุษร่วมที่ใกล้ที่สุด
คำถามที่พบบ่อย
บทเรียน “เส้นผ่านศูนย์กลาง ความสูง และต้นไม้สมดุล” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “เส้นผ่านศูนย์กลาง ความสูง และต้นไม้สมดุล” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “เส้นผ่านศูนย์กลาง ความสูง และต้นไม้สมดุล”
คำนวณเส้นผ่านศูนย์กลางและความสูงของต้นไม้ด้วยการทำ DFS รอบเดียว โดยใช้ตัวช่วยที่คืนค่าทั้งสองค่า แล้วตรวจสอบว่าต้นไม้สมดุลด้านความสูงหรือไม่ คุณปฏิบัติ Coding Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Coding Interview Prep หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Coding Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “เส้นผ่านศูนย์กลาง ความสูง และต้นไม้สมดุล” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Coding Interview Prep นี้ได้ไหม
ได้ บทเรียน Coding Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- คลาส TreeNode และ BFS แบบเรียงตามระดับ
- DFS แบบลำดับกลาง ก่อน และหลัง
- เส้นผ่านศูนย์กลาง ความสูง และต้นไม้สมดุล
- ผลรวมเส้นทางและบรรพบุรุษร่วมที่ใกล้ที่สุด