0Pricing
Coding Interview Prep · บทเรียน

เส้นผ่านศูนย์กลาง ความสูง และต้นไม้สมดุล

คำนวณเส้นผ่านศูนย์กลางและความสูงของต้นไม้ด้วยการทำ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. คลาส TreeNode และ BFS แบบเรียงตามระดับ
  2. DFS แบบลำดับกลาง ก่อน และหลัง
  3. เส้นผ่านศูนย์กลาง ความสูง และต้นไม้สมดุล
  4. ผลรวมเส้นทางและบรรพบุรุษร่วมที่ใกล้ที่สุด
← กลับไปที่ Coding Interview Prep