สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในฮิสโตแกรม
ใช้สแตกโมโนโทนิกติดตามขอบเขตด้านซ้าย และคำนวณพื้นที่สูงสุดของสี่เหลี่ยมผืนผ้าที่อยู่ภายในฮิสโตแกรมได้ในรอบเดียว
สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในฮิสโตแกรม เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
ปัญหา: สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในฮิสโตแกรม
ปัญหา สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในฮิสโตแกรม (LeetCode 84) กำหนดอาร์เรย์ของจำนวนเต็มไม่ติดลบที่แทนความสูงของแท่งในฮิสโตแกรม โดยแท่งแต่ละแท่งมีความกว้าง 1 ให้หาพื้นที่ของสี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดซึ่งสร้างได้ภายในฮิสโตแกรม สี่เหลี่ยมผืนผ้าต้องครอบคลุมแท่งที่อยู่ติดกัน และความสูงของสี่เหลี่ยมผืนผ้าถูกจำกัดด้วยแท่งที่สั้นที่สุดที่ครอบคลุม
วิธีตรวจสอบทุกกรณี: สำหรับคู่ (i, j) ทุกคู่ ให้คำนวณความสูงต่ำสุดในช่วง [i, j] แล้วคูณด้วย (j - i + 1) วิธีนี้ใช้เวลา O(n³) หรือ O(n²) หากคำนวณค่าต่ำสุดไว้ล่วงหน้า ซึ่งช้าเกินไป วิธีใช้สแตกแบบโมโนโทนิกทำงานในเวลา O(n)
# Example: heights = [2, 1, 5, 6, 2, 3]
# Rectangles:
# width=1, height=6 at index 3 => area=6
# width=2, height=5 at indices 2-3 => area=10 (maximum!)
# width=6, height=1 across all => area=6
# width=3, height=2 at indices 2-4 => area=6
heights = [2, 1, 5, 6, 2, 3]
print('Heights:', heights)
print('Expected max area: 10 (bars of height 5 and 6, width 2)')
# Brute force for small inputs:
def brute_force(heights):
n = len(heights)
max_area = 0
for i in range(n):
min_h = heights[i]
for j in range(i, n):
min_h = min(min_h, heights[j])
max_area = max(max_area, min_h * (j - i + 1))
return max_area
print('Brute force answer:', brute_force(heights)) # 10แนวคิดสำคัญ: อะไรเป็นตัวจำกัดสี่เหลี่ยมผืนผ้าของแท่งแต่ละแท่ง
สำหรับแท่ง i แต่ละแท่งที่มีความสูง h สี่เหลี่ยมผืนผ้าที่แท่งนั้นสามารถเป็น แท่งที่ต่ำที่สุด ได้จะขยายไปทางซ้ายจนถึงแท่งแรกที่สั้นกว่า h และขยายไปทางขวาจนถึงแท่งแรกที่สั้นกว่า h ความกว้างคือ right_boundary - left_boundary - 1 และพื้นที่คือ h × width
มุมมองนี้เปลี่ยนรูปแบบของปัญหา: สำหรับแท่งแต่ละแท่ง ให้ค้นหาสมาชิกก่อนหน้าที่มีค่าน้อยกว่า (PSE) และสมาชิกถัดไปที่มีค่าน้อยกว่า (NSE) ซึ่งเป็นสิ่งที่สแตกแบบเพิ่มขึ้นคำนวณได้พอดี ทันทีที่เรา pop แท่ง i (เพราะพบแท่งที่สั้นกว่า) แท่งปัจจุบันคือ NSE ของแท่งนั้น และสมาชิกบนสุดของสแตกหลังจาก pop คือ PSE
heights = [2, 1, 5, 6, 2, 3]
n = len(heights)
# Find PSE and NSE for each bar
pse = [-1] * n # index of previous smaller element
nse = [n] * n # index of next smaller element (default: beyond array)
# PSE
stack = []
for i in range(n):
while stack and heights[stack[-1]] >= heights[i]:
stack.pop()
pse[i] = stack[-1] if stack else -1
stack.append(i)
# NSE
stack = []
for i in range(n - 1, -1, -1):
while stack and heights[stack[-1]] >= heights[i]:
stack.pop()
nse[i] = stack[-1] if stack else n
stack.append(i)
max_area = 0
for i in range(n):
width = nse[i] - pse[i] - 1
area = heights[i] * width
print(f'Bar {i} (h={heights[i]}): PSE={pse[i]}, NSE={nse[i]}, width={width}, area={area}')
max_area = max(max_area, area)
print('Max area:', max_area)วิธีแก้แบบวนรอบเดียวด้วยสแตกแบบโมโนโทนิก
วิธีแก้แบบสองรอบข้างต้นใช้ได้ แต่สามารถรวมเป็นการวนรอบเดียวได้ ประมวลผลแท่งจากซ้ายไปขวาด้วยสแตกแบบเพิ่มขึ้น เมื่อแท่ง i สั้นกว่าสมาชิกบนสุดของสแตก ให้ pop สมาชิกบนสุดนั้นออก ความสูงของแท่งที่ถูก pop คือความสูงของสี่เหลี่ยมผืนผ้า ขอบเขตขวาคือ i และขอบเขตซ้ายคือสมาชิกบนสุดใหม่ของสแตก + 1
เคล็ดลับมาตรฐานคือ append ค่าเฝ้ายาม 0 ต่อท้าย heights วิธีนี้ทำให้แท่งทั้งหมดถูก pop ออกจากสแตกเมื่อจบ แม้จะไม่มีแท่งที่สั้นกว่าปรากฏขึ้นตามธรรมชาติ หากไม่มีค่าเฝ้ายาม คุณจะต้องมีขั้นตอนทำความสะอาดสมาชิกที่เหลือในสแตกหลังจบลูป
def largest_rectangle(heights):
stack = [] # monotonic increasing: indices of bars
max_area = 0
heights = heights + [0] # sentinel: forces all bars to be popped
for i, h in enumerate(heights):
while stack and heights[stack[-1]] > h:
height = heights[stack.pop()] # height of the rectangle
width = i if not stack else i - stack[-1] - 1 # left boundary
max_area = max(max_area, height * width)
stack.append(i)
return max_area
print(largest_rectangle([2, 1, 5, 6, 2, 3])) # 10
print(largest_rectangle([2, 4])) # 4
print(largest_rectangle([1, 1])) # 2
print(largest_rectangle([0, 9])) # 9
print(largest_rectangle([6, 7, 5, 2, 4, 5, 9, 3])) # 16การติดตามอัลกอริทึมแบบวนรอบเดียว
ให้ติดตาม [2, 1, 5, 6, 2, 3, 0] (มีค่าเฝ้ายาม) ทีละขั้นตอน:
- i=0, h=2: ใส่ 0 สแตก: [0]
- i=1, h=1: pop 0 (h=2, width=1, area=2) สแตกว่าง แล้วใส่ 1 สแตก: [1]
- i=2, h=5: 5>1 ให้ใส่ 2 สแตก: [1,2]
- i=3, h=6: 6>5 ให้ใส่ 3 สแตก: [1,2,3]
- i=4, h=2: pop 3 (h=6,width=4-2-1=1,area=6), pop 2 (h=5,width=4-1-1=2,area=10★), 2>1 จึงหยุด ใส่ 4 สแตก: [1,4]
- i=5, h=3: 3>2 ให้ใส่ 5 สแตก: [1,4,5]
- i=6, ค่าเฝ้ายาม h=0: pop ทั้งหมดเพื่อคำนวณพื้นที่...
def largest_rectangle_trace(heights):
stack = []
max_area = 0
hs = heights + [0]
for i, h in enumerate(hs):
while stack and hs[stack[-1]] > h:
top = stack.pop()
w = i if not stack else i - stack[-1] - 1
area = hs[top] * w
print(f' Pop bar {top} (h={hs[top]}): width={w}, area={area}', end='')
if area > max_area:
max_area = area
print(' *** NEW MAX ***', end='')
print()
print(f'i={i} h={h}: push {i}, stack={[hs[s] for s in stack + [i]]}')
stack.append(i)
print(f'Max area: {max_area}')
return max_area
largest_rectangle_trace([2, 1, 5, 6, 2, 3])การคำนวณความกว้าง: เหตุใดจึงเป็น i - stack[-1] - 1
เมื่อเรา pop แท่ง j ออกจากสแตก เราทราบว่า ขอบเขตขวาของสี่เหลี่ยมผืนผ้าของ j คือ i (แท่งแรกทางขวาที่สั้นกว่า j) ขอบเขตซ้ายคือแท่งที่อยู่ถัดลงมาจาก j ในสแตกหลังจาก pop ให้เรียกแท่งนั้นว่า k ดังนั้นความกว้างจึงเป็น i - k - 1 (แท่งตั้งแต่ k+1 ถึง i-1 โดยนับรวมปลายทั้งสองด้าน)
หากสแตกว่างหลังจาก pop สี่เหลี่ยมผืนผ้าของ j จะขยายไปจนถึงขอบซ้ายสุด ความกว้างจะเป็นเพียง i (ดัชนี 0 ถึง i-1 ซึ่งล้วนมีความสูงอย่างน้อยเท่ากับ heights[j]) นี่คือกรณีพิเศษ width = i if not stack else i - stack[-1] - 1
# Illustrating left/right boundary logic
heights = [1, 3, 5, 2]
# After processing with stack:
# When we pop bar 2 (h=5) at i=3 (h=2):
# stack after pop = [0, 1] => left boundary = 1+1=2, right=3-1=2 => width=1
# When we pop bar 1 (h=3) at i=3 (h=2):
# stack after pop = [0] => left boundary = 0+1=1, right=3-1=2 => width=2
# etc.
def compute_boundaries(heights):
hs = heights + [0]
stack = []
for i, h in enumerate(hs):
while stack and hs[stack[-1]] > h:
top = stack.pop()
if stack:
left = stack[-1] + 1
width = i - stack[-1] - 1
else:
left = 0
width = i
print(f'Bar {top} (h={hs[top]}): extends from {left} to {i-1}, width={width}')
stack.append(i)
compute_boundaries([2, 1, 5, 6, 2, 3])สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในเมทริกซ์ไบนารี
สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุด (LeetCode 85) ขยายปัญหาฮิสโตแกรมไปสู่เมทริกซ์ไบนารีสองมิติ สำหรับแต่ละแถว ให้คำนวณความสูงของเลข 1 ที่ต่อเนื่องกันเหนือแต่ละเซลล์ วิธีนี้จะสร้างฮิสโตแกรมสำหรับแถวนั้น จากนั้นใช้อัลกอริทึมหาสี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในฮิสโตแกรมกับฮิสโตแกรมของแต่ละแถว ค่าสูงสุดโดยรวมจากทุกแถวคือคำตอบ
วิธีนี้ลดปัญหาสองมิติให้เป็นปัญหาฮิสโตแกรมหนึ่งมิติที่ทำซ้ำ n ครั้ง ความซับซ้อนด้านเวลาคือ O(m × n) สำหรับเมทริกซ์ที่มี m แถวและ n คอลัมน์ โดยทำหนึ่งรอบกับฮิสโตแกรมต่อแถว และแต่ละรอบใช้เวลา O(n)
def maximal_rectangle(matrix):
if not matrix or not matrix[0]:
return 0
n = len(matrix[0])
heights = [0] * n
max_area = 0
def hist_max_area(h):
stack, area = [], 0
for i, hh in enumerate(h + [0]):
while stack and h[stack[-1]] > hh:
top = stack.pop()
w = i if not stack else i - stack[-1] - 1
area = max(area, h[top] * w)
stack.append(i)
return area
for row in matrix:
for j in range(n):
heights[j] = heights[j] + 1 if row[j] == '1' else 0
max_area = max(max_area, hist_max_area(heights[:]))
return max_area
matrix = [['1','0','1','0','0'],
['1','0','1','1','1'],
['1','1','1','1','1'],
['1','0','0','1','0']]
print(maximal_rectangle(matrix)) # 6กรณีขอบในปัญหาฮิสโตแกรม
กรณีขอบสำคัญที่ต้องจัดการ:
- ความสูงเท่ากันทั้งหมด: อาร์เรย์ทั้งชุดเป็นสี่เหลี่ยมผืนผ้าเดียว คำตอบ = n × height
- เพิ่มขึ้นอย่างต่อเนื่อง: จะไม่มีการ pop จนกว่าจะถึงค่าเฝ้ายาม พื้นที่ของแท่งสุดท้ายมีค่ามากที่สุด
- มีแท่งเดียว: คำตอบ = height[0]
- แท่งที่มีความสูง 0: แท่งเหล่านี้ทำหน้าที่เป็นค่าเฝ้ายามตามธรรมชาติ และแบ่งฮิสโตแกรมออกเป็นส่วนอิสระจากกัน
ค่าเฝ้ายาม (append 0) ที่ท้ายอาร์เรย์จัดการกรณีที่ความสูงเพิ่มขึ้นอย่างต่อเนื่องด้วยการบังคับให้แท่งที่เหลือทั้งหมดถูก pop เมื่อจบ หากไม่มีค่าเฝ้ายาม คุณจะต้องมีลูปแยกต่างหากเพื่อทำความสะอาดหลังการวนรอบหลัก
def largest_rectangle(heights):
stack = []
max_area = 0
heights = heights + [0]
for i, h in enumerate(heights):
while stack and heights[stack[-1]] > h:
top = stack.pop()
w = i if not stack else i - stack[-1] - 1
max_area = max(max_area, heights[top] * w)
stack.append(i)
return max_area
# Edge cases
print(largest_rectangle([5, 5, 5, 5])) # 20 (all same)
print(largest_rectangle([1, 2, 3, 4, 5])) # 9 (increasing: 3*3)
print(largest_rectangle([5, 4, 3, 2, 1])) # 9 (decreasing: 3*3)
print(largest_rectangle([5])) # 5 (single bar)
print(largest_rectangle([0, 0, 0])) # 0 (all zero)
print(largest_rectangle([3, 0, 3])) # 3 (zero splits)ทางเลือกแบบแบ่งและพิชิต
ปัญหาฮิสโตแกรมยังแก้ได้ด้วยวิธี แบ่งและพิชิต: แบ่งที่แท่งซึ่งมีความสูงต่ำสุด แก้ปัญหาแต่ละครึ่งด้วยการเรียกซ้ำ แล้วเปรียบเทียบกับสี่เหลี่ยมผืนผ้าที่ครอบคลุมความกว้างทั้งหมดโดยใช้ความสูงต่ำสุด วิธีนี้ใช้เวลาเฉลี่ย O(n log n) แต่กรณีเลวร้ายที่สุดเป็น O(n) สำหรับข้อมูลนำเข้าแบบเรียงลำดับ
วิธีใช้สแตกแบบโมโนโทนิกดีกว่าอย่างชัดเจนด้วยเวลา O(n) ในกรณีเลวร้ายที่สุด อย่างไรก็ตาม การทำความเข้าใจวิธีแบ่งและพิชิตจะช่วยให้เข้าใจสัญชาตญาณของปัญหาได้ลึกซึ้งขึ้น และอธิบายได้ว่าเหตุใดแท่งที่มีความสูงต่ำสุดในแต่ละช่วงจึงเป็นปัจจัยจำกัดสำหรับสี่เหลี่ยมผืนผ้าที่ครอบคลุมความกว้างทั้งหมดเสมอ
def largest_rectangle_dc(heights, lo=0, hi=None):
if hi is None:
hi = len(heights) - 1
if lo > hi:
return 0
# Find the index of the minimum height in [lo, hi]
min_idx = lo
for i in range(lo, hi + 1):
if heights[i] < heights[min_idx]:
min_idx = i
# Three options:
# 1. Max rect entirely in left half
# 2. Max rect entirely in right half
# 3. Max rect spanning entire [lo, hi] with height = min
full_width_area = heights[min_idx] * (hi - lo + 1)
left_area = largest_rectangle_dc(heights, lo, min_idx - 1)
right_area = largest_rectangle_dc(heights, min_idx + 1, hi)
return max(full_width_area, left_area, right_area)
print(largest_rectangle_dc([2, 1, 5, 6, 2, 3])) # 10รูปแบบฮิสโตแกรม: จำนวนช่วงย่อย
ปัญหาที่เกี่ยวข้องซึ่งใช้เทคนิคสแตกเดียวกันคือ การนับจำนวนช่วงย่อยในฮิสโตแกรมที่สมาชิกต่ำสุดมีค่าเท่ากับเป้าหมายที่กำหนด คำตอบหาได้จากการคำนวณ PSE และ NSE สำหรับแท่งแต่ละแท่ง แล้วใช้สูตร (i - pse[i]) × (nse[i] - i) ซึ่งนับฮิสโตแกรมย่อยที่แท่ง i เป็นสมาชิกต่ำสุด
เทคนิค 'จำนวนทางซ้าย × จำนวนทางขวา' นี้ปรากฏในปัญหา LeetCode หลายข้อ ได้แก่ ผลรวมของสมาชิกต่ำสุดในช่วงย่อย (907) การนับสตริงย่อยที่มีอักขระไม่ซ้ำกันทั้งหมด และปัญหาที่ใช้เทคนิคการคำนวณส่วนร่วม สแตกแบบโมโนโทนิกคำนวณ PSE และ NSE ได้ในเวลา O(n) ทำให้คำนวณส่วนร่วมของสมาชิกแต่ละตัวได้ในเวลา O(1)
def sum_of_subarray_minimums(arr):
n = len(arr)
pse = [-1] * n # previous strictly smaller element
nse = [n] * n # next smaller or equal element
stack = []
for i in range(n):
while stack and arr[stack[-1]] >= arr[i]:
stack.pop()
pse[i] = stack[-1] if stack else -1
stack.append(i)
stack = []
for i in range(n - 1, -1, -1):
while stack and arr[stack[-1]] > arr[i]:
stack.pop()
nse[i] = stack[-1] if stack else n
stack.append(i)
MOD = 10**9 + 7
total = 0
for i in range(n):
left_count = i - pse[i] # subarrays where i is leftmost min
right_count = nse[i] - i # subarrays where i is the min
total += arr[i] * left_count * right_count
return total % MOD
print(sum_of_subarray_minimums([3, 1, 2, 4])) # 17
print(sum_of_subarray_minimums([11, 81, 94, 43, 3])) # 444เคล็ดลับการสัมภาษณ์เชิงปฏิบัติ
เมื่อพบโจทย์ฮิสโตแกรมในการสัมภาษณ์ ให้ทำตามรายการตรวจสอบนี้:
- ชี้แจงว่าอนุญาตให้ความสูงเป็น 0 หรือไม่ และผลลัพธ์คือพื้นที่ ดัชนี หรือจำนวน
- เริ่มด้วยวิธีลองทุกกรณี และระบุความซับซ้อนเป็น O(n²) หรือ O(n³)
- กล่าวถึงการที่ส่วนร่วมของแต่ละแท่งขึ้นอยู่กับระยะขยายไปทางซ้ายและขวาจนถึงแท่งที่สั้นกว่าที่ใกล้ที่สุด
- แนะนำ PSE/NSE → สแตกแบบโมโนโทน → วิธีแก้ปัญหา O(n)
- จัดการเทคนิคค่าตัวเฝ้ายาม (append 0) เพื่อทำให้โค้ดเรียบง่ายขึ้น
- ไล่ดูตัวอย่างขนาดเล็กบนกระดานไวท์บอร์ด
คำถามต่อยอดที่พบบ่อยคือการขยายไปเป็น 2 มิติ (สี่เหลี่ยมผืนผ้าที่มีพื้นที่มากที่สุด) โดยแสดงให้เห็นว่าสามารถลดปัญหานี้เป็นโจทย์ฮิสโตแกรมจำนวน n โจทย์ แต่ละโจทย์ใช้เวลา O(n) จึงมีความซับซ้อนรวม O(m×n)
# Final clean solution for interview
def largest_rectangle_in_histogram(heights):
stack = []
max_area = 0
for i, h in enumerate(heights + [0]): # sentinel forces final pops
while stack and heights[stack[-1]] > h:
height = heights[stack.pop()]
width = i if not stack else i - stack[-1] - 1
max_area = max(max_area, height * width)
stack.append(i)
return max_area
# Verify all test cases from earlier
test_cases = [
([2, 1, 5, 6, 2, 3], 10),
([6, 7, 5, 2, 4, 5, 9, 3], 16),
([1], 1),
([2, 0, 2], 2),
([], 0),
]
for heights, expected in test_cases:
if not heights:
result = 0
else:
result = largest_rectangle_in_histogram(heights)
status = 'PASS' if result == expected else 'FAIL'
print(f'{status}: {heights} => {result} (expected {expected})')ผลรวมช่วงของอาร์เรย์ย่อยและรูปแบบที่คล้ายกัน
เทคนิค PSE/NSE สามารถนำไปใช้กับโจทย์ LeetCode ได้หลายข้อ ผลรวมช่วงของอาร์เรย์ย่อย (2104) ขอให้หาผลรวมของ (ค่าสูงสุด - ค่าต่ำสุด) ของอาร์เรย์ย่อยทั้งหมด ซึ่งเท่ากับ (ผลรวมของค่าสูงสุดของอาร์เรย์ย่อย) ลบด้วย (ผลรวมของค่าต่ำสุดของอาร์เรย์ย่อย) โดยแต่ละส่วนคำนวณได้ด้วยสแตกแบบโมโนโทนในเวลา O(n) ส่วน จำนวนคนที่มองเห็นได้ในคิว (1944) ใช้สแตกแบบลดลง โดยการ pop แต่ละครั้งนับเป็นคนที่มองเห็นได้หนึ่งคน การจดจำกลุ่มโจทย์นี้ได้เกิดจากการสังเกตวลีว่า “สำหรับแต่ละองค์ประกอบ มันสามารถครอบงำไปได้ไกลเพียงใด” — คำตอบคือ PSE/NSE ด้วยสแตกแบบโมโนโทนเสมอ
def sum_subarray_ranges(nums):
n = len(nums)
# Sum of subarray max - sum of subarray min
def contrib(arr, is_max):
# Count contribution of each element as max (or min)
n = len(arr)
left = [0]*n; right = [0]*n
stack = []
for i in range(n):
while stack and (arr[stack[-1]] < arr[i] if is_max else arr[stack[-1]] > arr[i]):
stack.pop()
left[i] = i - (stack[-1] if stack else -1)
stack.append(i)
stack = []
for i in range(n-1, -1, -1):
while stack and (arr[stack[-1]] <= arr[i] if is_max else arr[stack[-1]] >= arr[i]):
stack.pop()
right[i] = (stack[-1] if stack else n) - i
stack.append(i)
return sum(arr[i] * left[i] * right[i] for i in range(n))
return contrib(nums, True) - contrib(nums, False)
print(sum_subarray_ranges([1, 2, 3])) # 4
print(sum_subarray_ranges([1, 3, 3])) # 4
print(sum_subarray_ranges([4, -2, -3, 4, 1])) # 59ตรวจสอบความเข้าใจ
ทดสอบความเข้าใจแนวคิดโครงสร้างข้อมูลและอัลกอริทึม — การเตรียมตัวสัมภาษณ์การเขียนโปรแกรมจากบทเรียนนี้
ทบทวนบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้ว่า สำหรับแต่ละแท่ง สี่เหลี่ยมผืนผ้าที่ครอบคลุมแท่งนั้นและมีพื้นที่มากที่สุดมีขอบเขตที่กำหนดโดยแท่งที่สั้นกว่าที่ใกล้ที่สุดในแต่ละด้าน (PSE และ NSE) สแตกแบบเพิ่มขึ้นโมโนโทนสามารถคำนวณขอบเขต PSE/NSE ทั้งหมดได้ในการไล่ดูเพียงรอบเดียวที่มีความซับซ้อน O(n) โดยค้นหาขอบเขตทั้งสองขณะนำแท่งออกจากสแตก และ การเติมค่าตัวเฝ้ายาม 0 ทำให้แท่งทั้งหมดถูกนำออกจากสแตก จึงทำให้โค้ดเรียบง่ายเป็นลูปเดียว ต่อไปเราจะใช้ดีคิวแบบโมโนโทนเพื่อแก้ปัญหาค่าสูงสุดของหน้าต่างเลื่อนในเวลา O(n)
คำถามที่พบบ่อย
บทเรียน “สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในฮิสโตแกรม” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในฮิสโตแกรม” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในฮิสโตแกรม”
ใช้สแตกโมโนโทนิกติดตามขอบเขตด้านซ้าย และคำนวณพื้นที่สูงสุดของสี่เหลี่ยมผืนผ้าที่อยู่ภายในฮิสโตแกรมได้ในรอบเดียว คุณปฏิบัติ Coding Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Coding Interview Prep หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Coding Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในฮิสโตแกรม” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Coding Interview Prep นี้ได้ไหม
ได้ บทเรียน Coding Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- สแตกโมโนโทนิก: เพิ่มขึ้นเทียบกับลดลง
- สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในฮิสโตแกรม
- ค่าสูงสุดในหน้าต่างเลื่อนด้วยคิวสองทางโมโนโทนิก
- กักเก็บน้ำฝน: สแตกและตัวชี้สองตัว