ค่าที่น้อยที่สุดลำดับ k ผลรวมช่วง และ BST เป็นอาร์เรย์เรียงลำดับ
ใช้ประโยชน์จากการท่องลำดับกลางที่เรียงแล้วเพื่อหาค่าที่น้อยที่สุดลำดับ k ในเวลา O(k) และหาผลรวมค่าในช่วงในเวลา O(log n + k)
ค่าที่น้อยที่สุดลำดับ k ผลรวมช่วง และ BST เป็นอาร์เรย์เรียงลำดับ เป็นบทเรียน DSA Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน DSA Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส DSA Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
องค์ประกอบลำดับที่ k ที่น้อยที่สุดใน BST
องค์ประกอบลำดับที่ k ที่น้อยที่สุดใน BST (LeetCode #230) เป็นโจทย์ยอดนิยมที่ใช้ประโยชน์โดยตรงจากการท่องต้นไม้แบบอินออร์เดอร์ที่เรียงลำดับ เนื่องจากอินออร์เดอร์จะเยี่ยมชมโหนดตามลำดับจากน้อยไปมาก เราจึงเพียงนับโหนดระหว่างการท่องต้นไม้ แล้วคืนค่าของโหนดเมื่อจำนวนนับถึง k ใช้เวลา O(h + k) โดย h คือความสูงของต้นไม้ ซึ่งเป็นระยะทางไปยังโหนดซ้ายสุด และ k คือจำนวนขั้นในการท่องแบบอินออร์เดอร์
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def kth_smallest(root, k):
count = [0]
result = [None]
def inorder(node):
if not node or result[0] is not None:
return
inorder(node.left)
count[0] += 1
if count[0] == k:
result[0] = node.val
return
inorder(node.right)
inorder(root)
return result[0]
root = TreeNode(3)
root.left = TreeNode(1)
root.right = TreeNode(4)
root.left.right = TreeNode(2)
print(kth_smallest(root, 1)) # 1
print(kth_smallest(root, 2)) # 2ค่าที่ k น้อยที่สุด: แบบวนซ้ำด้วยสแตก
เวอร์ชันแบบวนซ้ำใช้รูปแบบอินออร์เดอร์ด้วยสแตกที่จัดการอย่างชัดเจน ให้ใส่โหนดด้านซ้ายลงในสแตกจนกว่าจะพบค่าว่าง จากนั้นนำโหนดออกและเพิ่มจำนวนนับ เมื่อจำนวนนับถึง k ให้คืนค่าของโหนดปัจจุบัน วิธีนี้หลีกเลี่ยงขีดจำกัดการเรียกซ้ำของ Python สำหรับต้นไม้ที่ลึกมาก และมีความซับซ้อนด้านเวลา O(h + k) และด้านพื้นที่ O(h) เช่นเดียวกัน ผู้สัมภาษณ์มักขอให้เขียนเวอร์ชันแบบวนซ้ำต่อจากเวอร์ชันแบบเรียกซ้ำ
def kth_smallest_iterative(root, k):
stack = []
curr = root
count = 0
while curr or stack:
while curr: # go as far left as possible
stack.append(curr)
curr = curr.left
curr = stack.pop() # process node
count += 1
if count == k:
return curr.val
curr = curr.right # move to right subtree
return -1 # k out of range
root = TreeNode(5)
root.left = TreeNode(3)
root.right = TreeNode(6)
root.left.left = TreeNode(2)
root.left.right = TreeNode(4)
root.left.left.left = TreeNode(1)
print(kth_smallest_iterative(root, 3)) # 3องค์ประกอบลำดับที่ k ที่มากที่สุดใน BST
องค์ประกอบลำดับที่ k ที่มากที่สุด ใช้การท่องต้นไม้แบบอินออร์เดอร์ย้อนกลับ (ขวา → ราก → ซ้าย) ซึ่งจะเยี่ยมชมโหนดตามลำดับจากมากไปน้อย ให้นับไป k ขั้น แล้วคืนค่าของโหนดปัจจุบัน วิธีนี้สมมาตรกับการค้นหาองค์ประกอบลำดับที่ k ที่น้อยที่สุด และใช้เวลา O(h + k) อีกทางเลือกหนึ่งคือคำนวณ kth_smallest(root, total_count - k + 1) หากทราบขนาดของต้นไม้ แต่วิธีอินออร์เดอร์ย้อนกลับมีความกระชับกว่า
def kth_largest(root, k):
count = [0]
result = [None]
def reverse_inorder(node):
if not node or result[0] is not None:
return
reverse_inorder(node.right) # visit LARGER values first
count[0] += 1
if count[0] == k:
result[0] = node.val
return
reverse_inorder(node.left)
reverse_inorder(root)
return result[0]
root = TreeNode(3)
root.left = TreeNode(1)
root.right = TreeNode(4)
root.left.right = TreeNode(2)
print(kth_largest(root, 1)) # 4 (largest)
print(kth_largest(root, 2)) # 3 (2nd largest)ผลรวมตามช่วงของ BST
ผลรวมตามช่วงของ BST (LeetCode #938) ขอให้หาผลรวมของค่าทั้งหมดใน [low, high] ให้ใช้คุณสมบัติของ BST เพื่อตัดกิ่งที่ไม่จำเป็นออก หากค่าของโหนดปัจจุบันน้อยกว่าขอบเขตล่าง ต้นไม้ย่อยด้านซ้ายทั้งหมดก็อยู่ต่ำกว่าขอบเขตล่างเช่นกัน จึงข้ามต้นไม้ย่อยนั้นได้ หากค่าปัจจุบันมากกว่าขอบเขตบน ให้ข้ามต้นไม้ย่อยด้านขวา วิธีนี้ตัดกิ่งออกได้จำนวนมาก และมีประสิทธิภาพกว่าการสแกนอินออร์เดอร์ทั้งหมด
def range_sum_bst(root, low, high):
if not root:
return 0
total = 0
if low <= root.val <= high:
total += root.val
if root.val > low: # left subtree might have values >= low
total += range_sum_bst(root.left, low, high)
if root.val < high: # right subtree might have values <= high
total += range_sum_bst(root.right, low, high)
return total
root = TreeNode(10)
root.left = TreeNode(5)
root.right = TreeNode(15)
root.left.left = TreeNode(3)
root.left.right = TreeNode(7)
root.right.right = TreeNode(18)
print(range_sum_bst(root, 7, 15)) # 7+10+15 = 32นับโหนดในช่วง
การนับโหนดในช่วง [low, high] ใช้ตรรกะการตัดกิ่งแบบเดียวกัน อีกทางเลือกหนึ่งคือใช้ bisect_left/bisect_right กับอาร์เรย์อินออร์เดอร์ แต่การท่อง BST โดยตรงใช้เวลา O(log n + k) ในขณะที่การแปลงเป็นอาร์เรย์ก่อนจะใช้เวลา O(n) เสมอ ให้เลือกการท่องโดยตรง เว้นแต่คุณต้องตอบคำถามเกี่ยวกับช่วงจำนวนมาก ซึ่งในกรณีนั้น การสร้าง BST ที่เพิ่มข้อมูลจำนวนโหนดในต้นไม้ย่อยจะช่วยให้ตอบแต่ละคำถามได้ในเวลา O(log n)
def count_range(root, low, high):
if not root:
return 0
count = 0
if low <= root.val <= high:
count += 1
if root.val > low:
count += count_range(root.left, low, high)
if root.val < high:
count += count_range(root.right, low, high)
return count
root = TreeNode(10)
root.left = TreeNode(5)
root.right = TreeNode(15)
root.left.left = TreeNode(3)
root.left.right = TreeNode(7)
root.right.right = TreeNode(18)
print(count_range(root, 6, 15)) # 7, 10, 15 = 3BST เป็นอาร์เรย์เรียงลำดับ (อัลกอริทึมฉบับเต็ม)
การแปลง BST เป็นอาร์เรย์เรียงลำดับใช้เวลา O(n) และพื้นที่ O(n) ให้ท่องต้นไม้แบบอินออร์เดอร์ แล้วใช้ append เพื่อเพิ่มค่าแต่ละค่าลงในอาร์เรย์ นี่คือจุดเริ่มต้นของโจทย์หลายขั้นตอน เช่น 'ผสาน BST สองต้น' 'ค้นหาค่ามัธยฐานของ BST' หรือ 'ตรวจสอบว่า BST สองต้นมีลำดับอินออร์เดอร์เหมือนกันหรือไม่' อาร์เรย์ผลลัพธ์รองรับการเข้าถึงด้วยดัชนีในเวลา O(1) การค้นหาแบบทวิภาค และเทคนิคตัวชี้สองตัว ซึ่ง BST เองไม่สามารถรองรับได้โดยตรง
def bst_to_sorted(root):
result = []
def inorder(node):
if not node:
return
inorder(node.left)
result.append(node.val)
inorder(node.right)
inorder(root)
return result
root = TreeNode(5)
root.left = TreeNode(3)
root.right = TreeNode(8)
root.left.left = TreeNode(1)
root.left.right = TreeNode(4)
root.right.left = TreeNode(6)
root.right.right = TreeNode(9)
print(bst_to_sorted(root)) # [1, 3, 4, 5, 6, 8, 9]
# Binary search on the resulting sorted array:
import bisect
arr = bst_to_sorted(root)
print(bisect.bisect_left(arr, 6)) # 4 (index of 6)BST แบบเพิ่มข้อมูล: ขนาดทรีย่อย
BST แบบเพิ่มข้อมูล จะจัดเก็บข้อมูลเพิ่มเติมในแต่ละโหนด เช่น ขนาดของทรีย่อยของโหนดนั้น เมื่อมีขนาดทรีย่อย การหา kth-smallest จะใช้เวลา O(log n): ในแต่ละโหนด หากขนาดทรีย่อยด้านซ้ายเท่ากับ k-1 โหนดปัจจุบันคือคำตอบ หากขนาดด้านซ้าย >= k ให้เรียกซ้ำทางซ้าย มิฉะนั้นให้ลบค่าออกแล้วเรียกซ้ำทางขวา โครงสร้างข้อมูลนี้เป็นเบื้องหลังของต้นไม้สถิติลำดับที่ใช้ในการเขียนโปรแกรมแข่งขัน
class AugNode:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
self.size = 1 # subtree size
def get_size(node):
return node.size if node else 0
def update_size(node):
if node:
node.size = 1 + get_size(node.left) + get_size(node.right)
def kth_smallest_aug(root, k):
left_size = get_size(root.left)
if k == left_size + 1:
return root.val # current node is kth
elif k <= left_size:
return kth_smallest_aug(root.left, k)
else:
return kth_smallest_aug(root.right, k - left_size - 1)
print('Augmented BST: O(log n) kth smallest with subtree sizes')ค้นหาค่าทั้งหมดใน BST ระหว่างสองโหนด
หากต้องการคืนค่าทั้งหมดที่อยู่ระหว่างสองโหนด p และ q โดยไม่รวมปลายทั้งสอง (เมื่อ p.val < q.val) ให้ผสานการท่องตามลำดับ inorder กับการตัดกิ่งตามช่วง: เริ่มเก็บค่าเมื่อผ่าน p.val และหยุดหลัง q.val วิธีนี้เป็นการขยายแนวคิดของผลรวมช่วง และให้ลำดับที่เรียงแล้วระหว่างค่าที่ใช้ค้นหาทั้งสองค่าในเวลา O(h + k)
def values_between(root, low, high):
result = []
def inorder(node):
if not node:
return
if node.val > low: # might be values > low on left
inorder(node.left)
if low < node.val < high: # strictly between
result.append(node.val)
if node.val < high: # might be values < high on right
inorder(node.right)
inorder(root)
return result
root = TreeNode(10)
root.left = TreeNode(5)
root.right = TreeNode(15)
root.left.left = TreeNode(3)
root.left.right = TreeNode(7)
root.right.left = TreeNode(12)
root.right.right = TreeNode(18)
print(values_between(root, 6, 15)) # [7, 10, 12]มัธยฐานของ BST
มัธยฐานของ BST คือค่าตรงกลางของการท่องตามลำดับ inorder สำหรับ n โหนด มัธยฐานอยู่ที่ดัชนี n // 2 (เริ่มนับดัชนีจาก 0) คุณอาจเก็บอาร์เรย์ที่เรียงครบทั้งชุดแล้วเข้าถึงค่าตามดัชนี หรือใช้สองรอบ: รอบแรกนับโหนด n โหนด จากนั้นท่องตามลำดับอีกครั้งและหยุดที่โหนดลำดับที่ n // 2 อีกทางเลือกหนึ่งคือใช้ kth-smallest โดยกำหนดให้ k = n // 2 + 1
def count_nodes(root):
if not root:
return 0
return 1 + count_nodes(root.left) + count_nodes(root.right)
def median_of_bst(root):
n = count_nodes(root)
if n == 0:
return None
k = n // 2 + 1 # (n+1)/2-th element for odd, n/2+1-th for even
return kth_smallest(root, k)
def kth_smallest(root, k):
count = [0]; result = [None]
def inorder(node):
if not node or result[0] is not None: return
inorder(node.left)
count[0] += 1
if count[0] == k: result[0] = node.val; return
inorder(node.right)
inorder(root); return result[0]
root = TreeNode(5)
root.left = TreeNode(3)
root.right = TreeNode(8)
root.left.left = TreeNode(1)
root.left.right = TreeNode(4)
print(median_of_bst(root)) # 4 (middle of [1,3,4,5,8])ค้นหา k ค่าที่ใกล้เป้าหมายที่สุด
ค้นหา k ค่าใน BST ที่ใกล้กับเป้าหมายที่สุด วิธีใช้ตัวชี้สองตัวคือแปลงเป็นอาร์เรย์ที่เรียงแล้วและใช้หน้าต่างเลื่อนขนาด k อีกทางเลือกหนึ่งคือใช้ฮีปสูงสุดขนาด k โดยใส่ระยะห่างเข้าไปแล้วนำค่าออกเมื่อขนาดเกิน k วิธีใช้อาร์เรย์ที่เรียงแล้วใช้เวลา O(n) และเรียบง่าย ส่วนวิธีใช้ฮีปใช้เวลา O(n log k) แต่ทำงานได้ในบริบทที่ประมวลผลแบบสตรีม
import heapq
def closest_k_values(root, target, k):
# Collect sorted values
arr = []
def inorder(node):
if not node: return
inorder(node.left)
arr.append(node.val)
inorder(node.right)
inorder(root)
# Two-pointer sliding window of size k
left, right = 0, k - 1
while right < len(arr) - 1:
if abs(arr[left] - target) <= abs(arr[right + 1] - target):
break # left is closer, don't advance
left += 1
right += 1
return arr[left:right + 1]
root = TreeNode(4)
root.left = TreeNode(2)
root.right = TreeNode(5)
root.left.left = TreeNode(1)
root.left.right = TreeNode(3)
print(closest_k_values(root, 3.7, 2)) # [3, 4]ใช้ประโยชน์จากคุณสมบัติของลำดับค่าถัดไป
ปัญหา BST จำนวนมากลดรูปเหลือการค้นหาค่าถัดไปหรือค่าก่อนหน้าในลำดับที่เรียงแล้ว ซึ่งเป็นการดำเนินการที่ใช้เวลา O(log n) เมื่อใช้การนำทางใน BST ตัววนซ้ำที่เราสร้างไว้ก่อนหน้านี้ให้ค่าถัดไปในเวลาเฉลี่ยตัดจำหน่าย O(1) เมื่อผสานความรู้เกี่ยวกับ kth-smallest ผลรวมช่วง และค่าที่ใกล้ที่สุด คุณจะสามารถแก้ปัญหา BST ส่วนใหญ่ในการสัมภาษณ์ได้ด้วยการถามว่า “การเรียงลำดับของการท่องแบบ inorder ช่วยทำให้เรื่องนี้ง่ายขึ้นอย่างไร” รูปแบบระดับแนวคิดนี้คือเข็มทิศสำหรับแก้ปัญหา BST ของคุณ
# Meta-pattern for BST problems:
# Step 1: What sorted-order property does this exploit?
# Step 2: Is in-order (ascending) or reverse in-order (descending) needed?
# Step 3: Can I prune using BST ordering to avoid O(n) scan?
# Quick reference:
# kth smallest -> in-order, stop at kth node
# kth largest -> reverse in-order, stop at kth node
# range sum -> in-order + BST pruning
# closest value -> walk toward target, track best
# median -> kth with k = n//2+1
# sorted array -> full in-order
# validate -> in-order prev check or min/max bounds
print('Sorted in-order is the universal BST problem tool')ตรวจสอบความเข้าใจ
ทดสอบความเข้าใจเกี่ยวกับแนวคิดโครงสร้างข้อมูลและอัลกอริทึม — การเตรียมตัวสัมภาษณ์เขียนโปรแกรมจากบทเรียนนี้
สรุปบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้: การหาค่าที่เล็กที่สุดและใหญ่ที่สุดลำดับที่ k โดยใช้การท่องตามลำดับและการท่องย้อนกลับตามลำดับในเวลา O(h+k), ผลรวมช่วงโดยใช้การตัดกิ่งของ BST สำหรับการค้นหาช่วงอย่างมีประสิทธิภาพ และการแปลง BST เป็นอาร์เรย์ที่เรียงแล้วเพื่อใช้เป็นพื้นฐานของอัลกอริทึมที่ทำงานกับอาร์เรย์ ต่อไปเราจะศึกษาเรื่องฮีปและคิวลำดับความสำคัญ
คำถามที่พบบ่อย
บทเรียน “ค่าที่น้อยที่สุดลำดับ k ผลรวมช่วง และ BST เป็นอาร์เรย์เรียงลำดับ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ค่าที่น้อยที่สุดลำดับ k ผลรวมช่วง และ BST เป็นอาร์เรย์เรียงลำดับ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส DSA Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส DSA Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ค่าที่น้อยที่สุดลำดับ k ผลรวมช่วง และ BST เป็นอาร์เรย์เรียงลำดับ”
ใช้ประโยชน์จากการท่องลำดับกลางที่เรียงแล้วเพื่อหาค่าที่น้อยที่สุดลำดับ k ในเวลา O(k) และหาผลรวมค่าในช่วงในเวลา O(log n + k) คุณปฏิบัติ DSA Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน DSA Interview Prep หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน DSA Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “ค่าที่น้อยที่สุดลำดับ k ผลรวมช่วง และ BST เป็นอาร์เรย์เรียงลำดับ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน DSA Interview Prep นี้ได้ไหม
ได้ บทเรียน DSA Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การแทรกและค้นหาใน BST
- การลบใน BST: สามกรณี
- ตรวจสอบ BST และคุณสมบัติลำดับกลาง
- ค่าที่น้อยที่สุดลำดับ k ผลรวมช่วง และ BST เป็นอาร์เรย์เรียงลำดับ