Coding Interview Prep · บทเรียน

ค่าสูงสุดในหน้าต่างเลื่อนด้วยคิวสองทางโมโนโทนิก

รักษาคิวสองทางของดัชนีแบบลดลงเพื่อหาค่าสูงสุดในหน้าต่างในเวลา O(1) ต่อสมาชิก และแก้ปัญหาค่าสูงสุดในหน้าต่างเลื่อนในเวลา O(n)

บทเรียน 3 จาก 413 ขั้นตอน

ค่าสูงสุดในหน้าต่างเลื่อนด้วยคิวสองทางโมโนโทนิก เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

โจทย์ค่าสูงสุดของหน้าต่างเลื่อน

โจทย์ ค่าสูงสุดของหน้าต่างเลื่อน (LeetCode 239) ให้ข้อมูลเป็นอาร์เรย์และขนาดหน้าต่าง k เมื่อหน้าต่างเลื่อนจากซ้ายไปขวาทีละหนึ่งตำแหน่ง ให้แสดงองค์ประกอบที่มีค่าสูงสุดในแต่ละหน้าต่าง วิธีลองทุกกรณีจะคำนวณค่าสูงสุดของหน้าต่างที่มี k องค์ประกอบแต่ละหน้าต่างในเวลา O(k) รวมเป็น O(nk) ซึ่งช้าเกินไปเมื่อ k มีขนาดใหญ่

วิธีใช้ ดีคิวแบบโมโนโทน (คิวสองปลาย) ทำเวลาโดยรวมได้ O(n) ด้วยการรักษาดีคิวของดัชนีที่เรียงค่าลดลง ด้านหน้าจะเก็บดัชนีของค่าสูงสุดในหน้าต่างปัจจุบันเสมอ ทำให้ query ค่าสูงสุดได้ในเวลา O(1) พร้อมรองรับการดำเนินการที่ปลายทั้งสองด้าน

from collections import deque

# Brute force O(nk) for comparison
def sliding_max_brute(nums, k):
    return [max(nums[i:i+k]) for i in range(len(nums) - k + 1)]

nums = [1, 3, -1, -3, 5, 3, 6, 7]
k = 3
print('Input:', nums, 'k=', k)
print('Expected: [3, 3, 5, 5, 6, 7]')
print('Brute:   ', sliding_max_brute(nums, k))

แนวคิดสำคัญของดีคิวแบบโมโนโทน

รักษา ดีคิวแบบลดลงโมโนโทน ที่เก็บ ดัชนี (ไม่ใช่ค่า) โดยมีเงื่อนไขคงเดิมดังนี้: nums[deque[0]] >= nums[deque[1]] >= ... >= nums[deque[-1]] ก่อนเพิ่มดัชนี i:

  • นำดัชนีที่หมดอายุออกจากด้านหน้า: หาก deque[0] <= i - k แสดงว่าดัชนีนั้นออกจากหน้าต่างแล้ว
  • นำดัชนีที่มีค่าน้อยกว่าออกจากด้านหลัง: ขณะที่ nums[deque[-1]] <= nums[i] ดัชนีเหล่านั้นจะไม่มีทางเป็นค่าสูงสุดของหน้าต่างในอนาคตได้ (เพราะอยู่ทางซ้ายและมีค่าน้อยกว่า) จึงนำออก

หลังจากดำเนินการเหล่านี้แล้ว ให้ใส่ i ที่ด้านหลัง ด้านหน้าจะให้ค่าสูงสุดของหน้าต่างปัจจุบันเสมอ

from collections import deque

def sliding_window_max(nums, k):
    dq = deque()  # stores indices; values are decreasing
    result = []

    for i, n in enumerate(nums):
        # 1. Remove indices outside the current window
        while dq and dq[0] <= i - k:
            dq.popleft()

        # 2. Remove indices with smaller values from the back
        while dq and nums[dq[-1]] <= n:
            dq.pop()

        dq.append(i)

        # 3. Record max when first full window is complete
        if i >= k - 1:
            result.append(nums[dq[0]])   # front = max of current window

    return result

nums = [1, 3, -1, -3, 5, 3, 6, 7]
print(sliding_window_max(nums, 3))  # [3, 3, 5, 5, 6, 7]

ไล่ดูการทำงานของดีคิวทีละขั้นตอน

ลองไล่ดู [1, 3, -1, -3, 5, 3, 6, 7] โดยมี k=3:

  • i=0 (1): dq=[0]
  • i=1 (3): pop 0 (1<3), dq=[1]
  • i=2 (-1): -1<3 จึงเก็บไว้, dq=[1,2]. หน้าต่าง [1,3,-1], ค่าสูงสุด=nums[1]=3
  • i=3 (-3): -3<-1, dq=[1,2,3]. ตรวจสอบด้านหน้า: 1 > 3-3=0, OK. ค่าสูงสุดของหน้าต่าง=3
  • i=4 (5): pop 3,2,1 (ทั้งหมดมีค่าน้อยกว่า), dq=[4]. ด้านหน้า 4 > 4-3=1, OK. ค่าสูงสุด=5
  • i=5 (3): 3<5, dq=[4,5]. ด้านหน้า 4 > 5-3=2, OK. ค่าสูงสุด=5
  • i=6 (6): pop 5,4 (ทั้งคู่มีค่าน้อยกว่า), dq=[6]. ค่าสูงสุด=6
  • i=7 (7): pop 6, dq=[7]. ค่าสูงสุด=7
from collections import deque

def sliding_window_max_trace(nums, k):
    dq = deque()
    result = []
    for i, n in enumerate(nums):
        while dq and dq[0] <= i - k:
            print(f'  Remove expired index {dq[0]} from front')
            dq.popleft()
        while dq and nums[dq[-1]] <= n:
            print(f'  Remove smaller index {dq[-1]} (val={nums[dq[-1]]}) from back')
            dq.pop()
        dq.append(i)
        print(f'i={i} n={n}: dq={list(dq)} vals={[nums[j] for j in dq]}')
        if i >= k - 1:
            win_max = nums[dq[0]]
            result.append(win_max)
            print(f'  Window {nums[max(0,i-k+1):i+1]} -> max={win_max}')
    return result

nums = [1, 3, -1, -3, 5, 3, 6, 7]
result = sliding_window_max_trace(nums, 3)
print('Result:', result)

เหตุใดแต่ละองค์ประกอบจึงถูกใส่และนำออกอย่างมากที่สุดครั้งเดียว

การรับประกันเวลา O(n) มาจากเหตุผลแบบตัดจำหน่ายเดียวกับสแตกแบบโมโนโทน: แต่ละดัชนีถูกใช้ append ลงในดีคิวเพียงครั้งเดียว และถูกนำออกอย่างมากที่สุดครั้งเดียว ไม่ว่าจะจากด้านหน้าเมื่อหมดอายุ หรือจากด้านหลังเมื่อมีองค์ประกอบใหม่มาแทนที่ ดังนั้นการดำเนินการกับดีคิวตลอดทั้งลูปมีจำนวนไม่เกิน 2n ครั้ง

ลูป while ด้านในไม่ได้เพิ่มความซับซ้อนโดยรวม เพราะการใช้ pop ในลูปเหล่านั้นมีต้นทุนที่ชดเชยด้วยการใส่ดัชนีลงไปก่อนหน้า นี่เป็นเหตุผลเดียวกับสแตกแบบโมโนโทน แต่ขยายไปยังดีคิวที่สามารถนำองค์ประกอบออกจากปลายทั้งสองด้านได้

from collections import deque

def sliding_window_max_instrumented(nums, k):
    dq = deque()
    result = []
    front_pops = back_pops = pushes = 0

    for i, n in enumerate(nums):
        while dq and dq[0] <= i - k:
            dq.popleft(); front_pops += 1
        while dq and nums[dq[-1]] <= n:
            dq.pop(); back_pops += 1
        dq.append(i); pushes += 1
        if i >= k - 1:
            result.append(nums[dq[0]])

    print(f'n={len(nums)}: pushes={pushes}, front_pops={front_pops}, back_pops={back_pops}')
    print(f'Total deque ops = {pushes + front_pops + back_pops} <= 3n = {3*len(nums)}')
    return result

import random; random.seed(0)
nums = [random.randint(-100, 100) for _ in range(20)]
sliding_window_max_instrumented(nums, 5)

ค่าต่ำสุดของหน้าต่างเลื่อน

ค่าต่ำสุดของหน้าต่างเลื่อนเป็นกรณีคู่สมมาตร: รักษา ดีคิวแบบเพิ่มขึ้นโมโนโทน (pop จากด้านหลังเมื่อองค์ประกอบใหม่มีค่าน้อยกว่าด้านหลัง) ด้านหน้าจะเก็บค่าต่ำสุดของหน้าต่างปัจจุบันเสมอ ขั้นตอนอื่นเหมือนกับกรณีค่าสูงสุดทุกประการ เพียงกลับทิศทางการเปรียบเทียบ

โจทย์ที่ถามหาค่าต่ำสุดของหน้าต่างเลื่อนมักปรากฏเป็นโจทย์ย่อยภายในอัลกอริทึมที่ใหญ่กว่า ตัวอย่างเช่น การหาต้นทุนต่ำสุดในการขนย้ายสินค้าตามเส้นทางที่มีจุดแวะระหว่างทาง k จุด อาจต้องใช้ค่าต่ำสุดของหน้าต่างเลื่อนกับอาร์เรย์ DP

from collections import deque

def sliding_window_min(nums, k):
    dq = deque()  # increasing monotonic deque
    result = []

    for i, n in enumerate(nums):
        while dq and dq[0] <= i - k:
            dq.popleft()               # expired
        while dq and nums[dq[-1]] >= n:
            dq.pop()                   # pop larger values from back
        dq.append(i)
        if i >= k - 1:
            result.append(nums[dq[0]])  # front = min
    return result

nums = [1, 3, -1, -3, 5, 3, 6, 7]
print('Max k=3:', sliding_window_min.__name__, '->', end=' ')
print(sliding_window_min(nums, 3))   # [-1, -3, -3, -3, 3, 3]

from collections import deque
def sliding_window_max(nums, k):
    dq = deque(); result = []
    for i, n in enumerate(nums):
        while dq and dq[0] <= i-k: dq.popleft()
        while dq and nums[dq[-1]] <= n: dq.pop()
        dq.append(i)
        if i >= k-1: result.append(nums[dq[0]])
    return result

print('Max k=3:', sliding_window_max(nums, 3))   # [3,3,5,5,6,7]

เกมกระโดด VI: DP ด้วยดีคิวแบบโมโนโทน

เกมกระโดด VI (LeetCode 1696) เป็นตัวอย่างคลาสสิกที่ผสาน DP กับดีคิวแบบโมโนโทน เมื่อกำหนดอาร์เรย์และระยะกระโดดสูงสุด k โดยเริ่มที่ดัชนี 0 แต่ละขั้นสามารถกระโดดไปข้างหน้า 1 ถึง k ตำแหน่ง และบวกคะแนนของช่องเป้าหมายเข้าไป จงหาคะแนนรวมที่มากที่สุด สมการเวียนเกิดของ DP คือ dp[i] = nums[i] + max(dp[i-k], ..., dp[i-1]) การหาค่าสูงสุดของหน้าต่างเลื่อนบนอาร์เรย์ DP ทำให้เวลารวมเป็น O(n)

รูปแบบนี้ — สมการเวียนเกิดของ DP ที่แต่ละช่องขึ้นอยู่กับค่าสูงสุดของหน้าต่างขนาดคงที่จากช่องก่อนหน้า — ปรากฏอยู่บ่อยครั้ง และต้องใช้ดีคิวแบบโมโนโทนเสมอ

from collections import deque

def max_result(nums, k):
    n = len(nums)
    dp = [0] * n
    dp[0] = nums[0]
    dq = deque([0])   # indices of max dp values in current window

    for i in range(1, n):
        # Remove expired indices
        while dq and dq[0] < i - k:
            dq.popleft()
        # dp[i] = nums[i] + max dp in window [i-k, i-1]
        dp[i] = nums[i] + dp[dq[0]]
        # Maintain decreasing deque on dp values
        while dq and dp[dq[-1]] <= dp[i]:
            dq.pop()
        dq.append(i)

    return dp[n - 1]

print(max_result([1,-1,-2,4,-7,3], 2))    # 7: path 1->4->3
print(max_result([10,-5,-2,4,0,3], 3))    # 17: path 10->4->3
print(max_result([1,-5,-20,4,-1,3,-6,-3], 2))  # 0

ค่าสูงสุดของหน้าต่างเลื่อน: ทางเลือกใช้ต้นไม้เซกเมนต์

สำหรับโจทย์ที่ขนาดหน้าต่างเปลี่ยนแปลง (ไม่ใช่ k คงที่) ดีคิวแบบโมโนโทนไม่สามารถนำมาใช้ได้โดยตรง ให้ใช้ ตารางแบบเบาบางสำหรับ query ค่าสูงสุดของช่วงแบบคงที่ในเวลา O(1) ต่อ query หลังการเตรียมข้อมูลล่วงหน้าในเวลา O(n log n) หรือใช้ ต้นไม้เซกเมนต์สำหรับการอัปเดตแบบพลวัต โดยใช้เวลา O(log n) ต่อ query อย่างไรก็ตาม สำหรับหน้าต่างเลื่อนที่มี k คงที่ ดีคิวนั้นไม่มีวิธีใดทำได้ดีกว่าในเวลา O(n)

ในการสัมภาษณ์ ให้เลือกดีคิวแบบโมโนโทน O(n) แทนต้นไม้เซกเมนต์ O(n log n) เสมอเมื่อขนาดหน้าต่างคงที่ และกล่าวถึงข้อแลกเปลี่ยนด้วยว่า ดีคิวไม่สามารถรองรับขนาดหน้าต่างหรือการอัปเดตที่กำหนดได้อย่างอิสระ ขณะที่ต้นไม้เซกเมนต์ทำได้

# Sparse table for static RMQ (range maximum query)
import math

def build_sparse_table(arr):
    n = len(arr)
    LOG = int(math.log2(n)) + 1 if n else 1
    table = [[0]*n for _ in range(LOG)]
    table[0] = arr[:]
    j = 1
    while (1 << j) <= n:
        for i in range(n - (1 << j) + 1):
            table[j][i] = max(table[j-1][i], table[j-1][i + (1 << (j-1))])
        j += 1
    return table

def query(table, l, r):
    k = int(math.log2(r - l + 1))
    return max(table[k][l], table[k][r - (1 << k) + 1])

arr = [1, 3, -1, -3, 5, 3, 6, 7]
table = build_sparse_table(arr)
k = 3
result = [query(table, i, i + k - 1) for i in range(len(arr) - k + 1)]
print('Sparse table result:', result)  # [3, 3, 5, 5, 6, 7]

อาร์เรย์ย่อยของเลขหนึ่งที่ยาวที่สุดหลังลบหนึ่งองค์ประกอบ

LeetCode 1493: เมื่อกำหนดอาร์เรย์ไบนารี ให้หาความยาวของอาร์เรย์ย่อยของเลข 1 ที่ยาวที่สุดหลังลบองค์ประกอบหนึ่งรายการพอดี (ซึ่งอาจเป็น 0 หรือ 1) นี่เป็นโจทย์หน้าต่างเลื่อน ให้รักษาหน้าต่างที่มี 0 ได้ไม่เกินหนึ่งตัว เมื่อหน้าต่างมี 0 มากกว่าหนึ่งตัว ให้ย่อหน้าต่างจากด้านซ้าย

โจทย์นี้ใช้รูปแบบหน้าต่างเลื่อนขนาดเปลี่ยนแปลงได้ ไม่ใช่ดีคิว อย่างไรก็ตาม สามารถจับคู่กับเทคนิคหน้าต่างที่มีความยาวมากที่สุดได้: หลังจากพบหน้าต่างที่ถูกต้องทั้งหมดแล้ว ความยาวที่มากที่สุดคือคำตอบ การ “ลบหนึ่งองค์ประกอบ” หมายความว่าเราอนุญาตให้มี 0 ได้หนึ่งตัวพอดีในหน้าต่างของเลข 1

def longest_subarray(nums):
    left = 0
    zeros = 0
    max_len = 0

    for right in range(len(nums)):
        if nums[right] == 0:
            zeros += 1
        while zeros > 1:
            if nums[left] == 0:
                zeros -= 1
            left += 1
        # Window [left, right] has at most 1 zero
        # After deleting one element, length = right - left (not +1, since we delete one)
        max_len = max(max_len, right - left)

    return max_len

print(longest_subarray([1,1,0,1]))       # 3: delete the 0
print(longest_subarray([0,1,1,1,0,1,1,0,1]))  # 5
print(longest_subarray([1,1,1]))          # 2: must delete one 1

การเปรียบเทียบดีคิวกับคิวและสแตก

การเข้าใจว่าเมื่อใดควรใช้โครงสร้างแต่ละแบบเป็นสิ่งสำคัญในการสัมภาษณ์:

  • สแตก (รายการ): LIFO เข้าถึงได้จากปลายเดียว ใช้กับ DFS การแยกวิเคราะห์นิพจน์ และโจทย์สแตกแบบโมโนโทน
  • คิว (ดีคิวพร้อมการเติมทางซ้าย/popleft): FIFO ใส่ข้อมูลที่ปลายหนึ่งและนำออกจากอีกปลายหนึ่ง ใช้กับ BFS และการจัดตารางงาน
  • ดีคิว: เข้าถึงได้จากปลายทั้งสองด้านในเวลา O(1) ใช้กับหน้าต่างเลื่อนที่มีการหมดอายุ (นำออกจากด้านหน้า) และเงื่อนไขคงเดิมแบบโมโนโทน (นำออกจากด้านหลัง) ค่าสูงสุดของหน้าต่างเลื่อนเป็นโจทย์ดีคิวมาตรฐาน

collections.deque ของภาษาไพธอนเป็นเครื่องมือสำหรับทั้งสามกรณี ใช้ append/pop สำหรับพฤติกรรมแบบสแตก และใช้ append/popleft หรือ appendleft/pop สำหรับพฤติกรรมแบบคิวหรือดีคิว

from collections import deque

# deque as stack
stack = deque()
stack.append(1); stack.append(2); stack.append(3)
print('Stack pop:', stack.pop())  # 3 (LIFO)

# deque as queue
queue = deque()
queue.append(1); queue.append(2); queue.append(3)
print('Queue pop:', queue.popleft())  # 1 (FIFO)

# deque as sliding window with front expiry + back monotonic
dq = deque()
nums = [3, 1, 4, 1, 5, 9, 2, 6]
k = 3
for i, n in enumerate(nums):
    while dq and dq[0] <= i - k: dq.popleft()   # expire front
    while dq and nums[dq[-1]] <= n: dq.pop()     # maintain back
    dq.append(i)
    if i >= k - 1:
        print(f'Window {nums[max(0,i-k+1):i+1]}: max={nums[dq[0]]}')

อาร์เรย์ย่อยที่สั้นที่สุดซึ่งมีผลรวมอย่างน้อย K: ดีคิว + ผลรวมคำนำหน้า

อาร์เรย์ย่อยที่สั้นที่สุดซึ่งมีผลรวมอย่างน้อย K (LeetCode 862) เป็นโจทย์ขั้นสูงที่ผสานผลรวมคำนำหน้าเข้ากับดีคิวแบบโมโนโทน ให้สร้างผลรวมคำนำหน้า จากนั้นใช้ดีคิวเพื่อค้นหาผลรวมคำนำหน้าที่อยู่ซ้ายสุดซึ่งเป็นไปตาม prefix[right] - prefix[left] >= k สำหรับจุดสิ้นสุดด้านขวาแต่ละจุด ดีคิวจะรักษาผลรวมคำนำหน้าให้เพิ่มขึ้น (pop จากด้านหลังเพื่อคงลำดับที่เพิ่มขึ้น) และ pops จากด้านหน้าเพื่อรวบรวมคำตอบที่ถูกต้อง

นี่เป็นหนึ่งในโจทย์หน้าต่างเลื่อนที่ยากที่สุด เพราะมีจำนวนติดลบ (ทำให้ใช้วิธีสองพอยน์เตอร์แบบง่ายไม่ได้) และต้องให้ดีคิวทำหน้าที่ทั้งเป็นโครงสร้างแบบโมโนโทนและกลไกจัดการการหมดอายุ

from collections import deque

def shortest_subarray(nums, k):
    n = len(nums)
    prefix = [0] * (n + 1)
    for i in range(n):
        prefix[i + 1] = prefix[i] + nums[i]

    dq = deque()    # monotonic increasing deque of indices into prefix
    result = float('inf')

    for right in range(n + 1):
        # Pop from front: valid subarrays ending at `right`
        while dq and prefix[right] - prefix[dq[0]] >= k:
            result = min(result, right - dq.popleft())
        # Pop from back: maintain increasing deque
        while dq and prefix[dq[-1]] >= prefix[right]:
            dq.pop()
        dq.append(right)

    return result if result != float('inf') else -1

print(shortest_subarray([1], 1))               # 1
print(shortest_subarray([1, 2], 4))            # -1
print(shortest_subarray([2, -1, 2], 3))        # 3
print(shortest_subarray([84,-37,32,40,95], 167))  # 3

กลยุทธ์การสัมภาษณ์สำหรับโจทย์ดีคิว

ให้ระบุโจทย์ดีคิวแบบโมโนโทนจากสัญญาณเหล่านี้: (1) ต้องหาค่าสูงสุดหรือค่าต่ำสุดของ หน้าต่างเลื่อน ที่มีขนาดคงที่ (2) ต้องใช้สมการเวียนเกิดของ DP dp[i] = f(nums[i], max(dp[i-k..i-1])) หรือ (3) ต้องหาดัชนีที่ถูกต้องซึ่งอยู่ใกล้ที่สุดและเป็นไปตามเงื่อนไขโมโนโทน

ในการสัมภาษณ์ ให้เขียนวิธีใช้ดีคิวอย่างเป็นระเบียบ: นำเข้า deque รักษาเงื่อนไขคงเดิมสองข้อ (การหมดอายุด้านหน้าและความเป็นโมโนโทนด้านหลัง) และคืนผลลัพธ์โดยเริ่มจากดัชนี k-1 กล่าวถึงความซับซ้อนด้านเวลา O(n) และพื้นที่ O(k) สำหรับดีคิวเสมอ (เก็บดัชนีได้ไม่เกิน k ตัวในแต่ละครั้ง) พร้อมเปรียบเทียบกับวิธีลองทุกกรณี O(nk) เพื่อแสดงให้เห็นการปรับปรุง

from collections import deque

# Clean, interview-ready template
def sliding_window_max_template(nums, k):
    if not nums or k == 0:
        return []

    dq = deque()   # monotonic decreasing, stores indices
    result = []

    for i in range(len(nums)):
        # Invariant 1: remove expired indices (outside window)
        while dq and dq[0] < i - k + 1:
            dq.popleft()

        # Invariant 2: remove indices with smaller values (useless)
        while dq and nums[dq[-1]] < nums[i]:
            dq.pop()

        dq.append(i)

        # Record result once first full window is established
        if i >= k - 1:
            result.append(nums[dq[0]])

    return result

# Complexity: O(n) time, O(k) space
print(sliding_window_max_template([1,3,-1,-3,5,3,6,7], 3))
print(sliding_window_max_template([1], 1))
print(sliding_window_max_template([], 3))

ตรวจสอบความเข้าใจ

ทดสอบความเข้าใจแนวคิดโครงสร้างข้อมูลและอัลกอริทึม — การเตรียมตัวสัมภาษณ์การเขียนโปรแกรมจากบทเรียนนี้

ทบทวนบทเรียน

ในบทเรียนนี้ คุณได้เรียนรู้ว่า ดีคิวแบบลดลงโมโนโทนจะรักษาค่าสูงสุดของหน้าต่างไว้ที่ด้านหน้า พร้อมนำองค์ประกอบจากด้านหลังที่มีค่าน้อยกว่าสมาชิกใหม่ออก ดัชนีที่หมดอายุจะถูกนำออกจากด้านหน้าเมื่ออยู่นอกขอบเขตของหน้าต่าง และ แต่ละดัชนีถูกใส่และนำออกอย่างมากที่สุดครั้งเดียว ทำให้มีเวลาโดยรวม O(n) และใช้พื้นที่ O(k) สำหรับดีคิว ต่อไปเราจะศึกษาการคำนวณปริมาณน้ำฝนที่กักเก็บโดยใช้ทั้งสแตกแบบโมโนโทนและวิธีสองพอยน์เตอร์

เริ่มต้นได้ฟรี

เรียนรู้ Coding Interview Prep ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
90
บทเรียน
360

คำถามที่พบบ่อย

บทเรียน “ค่าสูงสุดในหน้าต่างเลื่อนด้วยคิวสองทางโมโนโทนิก” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ค่าสูงสุดในหน้าต่างเลื่อนด้วยคิวสองทางโมโนโทนิก” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ค่าสูงสุดในหน้าต่างเลื่อนด้วยคิวสองทางโมโนโทนิก”

รักษาคิวสองทางของดัชนีแบบลดลงเพื่อหาค่าสูงสุดในหน้าต่างในเวลา O(1) ต่อสมาชิก และแก้ปัญหาค่าสูงสุดในหน้าต่างเลื่อนในเวลา O(n) คุณปฏิบัติ 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. สแตกโมโนโทนิก: เพิ่มขึ้นเทียบกับลดลง
  2. สี่เหลี่ยมผืนผ้าที่ใหญ่ที่สุดในฮิสโตแกรม
  3. ค่าสูงสุดในหน้าต่างเลื่อนด้วยคิวสองทางโมโนโทนิก
  4. กักเก็บน้ำฝน: สแตกและตัวชี้สองตัว
← กลับไปที่ Coding Interview Prep