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

DSU กับการบีบอัดเส้นทาง

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

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

DSU คืออะไร

การรวมเซตที่ไม่ทับซ้อนกัน (DSU) หรือที่เรียกว่าการรวมและค้นหา เป็นโครงสร้างข้อมูลที่ดูแลกลุ่มของเซตที่ไม่ทับซ้อนกัน โดยรองรับการดำเนินการหลักสองอย่าง: find (องค์ประกอบ x อยู่ในเซตใด) และ union (รวมเซตที่มี x และ y อยู่เข้าด้วยกัน) DSU เหมาะอย่างยิ่งสำหรับปัญหาการเชื่อมต่อแบบพลวัต ซึ่งกลุ่มต่าง ๆ จะถูกรวมเข้าด้วยกันตามเวลาแต่ไม่เคยถูกแยกออก

องค์ประกอบทุกตัวจะเริ่มต้นเป็นเซตของตัวเอง เมื่อประมวลผลเส้นเชื่อมหรือความสัมพันธ์ เราจะรวมเซตต่าง ๆ เข้าด้วยกัน ความท้าทายคือการทำสิ่งนี้ให้มีประสิทธิภาพ — การนำไปใช้แบบตรงไปตรงมาใช้เวลา O(n) ต่อการดำเนินการ แต่เมื่อใช้การปรับปรุงประสิทธิภาพ เราจะเข้าใกล้ O(1) เมื่อคิดต้นทุนเฉลี่ย

# Naive DSU without optimisations
class DSU:
    def __init__(self, n):
        self.parent = list(range(n))  # each node is its own parent

    def find(self, x):
        while self.parent[x] != x:
            x = self.parent[x]
        return x

    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px != py:
            self.parent[px] = py

ปัญหาของการดำเนินการ find แบบพื้นฐาน

ใน DSU แบบพื้นฐาน find(x) จะเดินขึ้นไปตามสายโซ่โหนดแม่จนถึงโหนดที่ชี้มายังตัวเอง (หรือ ราก) หากต้นไม้สมดุล การดำเนินการนี้จะใช้เวลา O(log n) แต่หากเรา union โดยให้รากที่สองชี้ไปอยู่ใต้รากแรกเสมอ เราอาจสร้าง สายโซ่ (ต้นไม้เสื่อมสภาพ) ที่มีความยาว n ได้ ทำให้การดำเนินการ find แต่ละครั้งใช้เวลา O(n)

ลองพิจารณาการ union 0→1→2→3→4 ตามลำดับ การเรียก find ของโหนด 0 ต้องเดินผ่านสายโซ่ทั้งหมด ด้วย การบีบอัดเส้นทาง เราจะแก้ปัญหานี้โดยทำให้ทุกโหนดที่ผ่านชี้ตรงไปยังรากระหว่างการดำเนินการ find เอง

# Worst case without compression: a chain
# parent = [1, 2, 3, 4, 4]  => find(0) takes 4 steps
# After path compression: parent = [4, 4, 4, 4, 4]  => find(0) takes 1 step

parent = [1, 2, 3, 4, 4]
print('Before:', parent)
# Simulate find(0) with naive approach
x = 0
steps = 0
while parent[x] != x:
    x = parent[x]
    steps += 1
print('Root:', x, 'Steps taken:', steps)

การบีบอัดเส้นทาง: แบบเวียนเกิดเที่ยวเดียว

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

แนวคิดสำคัญคือ หลังจากการเรียกแบบเวียนเกิดคืนค่ารากแล้ว เรากำหนด self.parent[x] = root ก่อนคืนค่า การทำเช่นนี้จะ ทำให้ต้นไม้แบนลง — โหนดทั้งหมดบนเส้นทางค้นหาจะชี้ตรงไปยังราก การดำเนินการนี้ไม่ได้เปลี่ยนว่าโหนดนั้นอยู่ในเซตใด แต่เพียงทำให้เส้นทางค้นหาในอนาคตสั้นลง

class DSU:
    def __init__(self, n):
        self.parent = list(range(n))

    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])  # path compression
        return self.parent[x]

    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px != py:
            self.parent[px] = py

dsu = DSU(5)
dsu.union(0, 1)
dsu.union(1, 2)
dsu.union(2, 3)
print('Root of 0:', dsu.find(0))
print('Parent array after compression:', dsu.parent)

การบีบอัดเส้นทาง: แบบวนซ้ำสองเที่ยว

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

ทั้งแบบเวียนเกิดและแบบวนซ้ำยังคงมี ความถูกต้อง เหมือนเดิม — find ยังคงคืนค่ารากเดิม ความแตกต่างเพียงอย่างเดียวคือมีการปรับตัวชี้โหนดแม่เป็นผลข้างเคียง ซึ่งทำให้การเรียก find ในอนาคตสำหรับโหนดเหล่านั้นใช้เวลา O(1)

class DSU:
    def __init__(self, n):
        self.parent = list(range(n))

    def find(self, x):
        root = x
        while self.parent[root] != root:
            root = self.parent[root]          # first pass: find root
        while self.parent[x] != root:
            nxt = self.parent[x]
            self.parent[x] = root             # second pass: compress
            x = nxt
        return root

    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px != py:
            self.parent[px] = py
            return True
        return False  # already connected

dsu = DSU(6)
for a, b in [(0,1),(1,2),(2,3),(3,4)]:
    dsu.union(a, b)
print('Parent before find(0):', dsu.parent[:])
dsu.find(0)
print('Parent after  find(0):', dsu.parent[:])

ความซับซ้อนแบบเฉลี่ยตัดจำหน่ายของการบีบอัดเส้นทาง

การบีบอัดเส้นทางเพียงอย่างเดียวทำให้มีเวลาแบบเฉลี่ยตัดจำหน่าย O(log n) ต่อการดำเนินการหนึ่งครั้งตลอดลำดับการดำเนินการ m ครั้ง การดำเนินการ find แต่ละครั้งอาจใช้เวลามากในครั้งแรกที่เดินผ่านสายโซ่ แต่จะทำให้สายโซ่นั้นแบนลง ดังนั้นการเรียก find ครั้งถัดไปบนโหนดเหล่านั้นจะใช้เวลา O(1) งานทั้งหมดจึงถูกเฉลี่ยกระจายไปตามการดำเนินการจำนวนมาก

การวิเคราะห์อย่างเป็นทางการใช้ วิธีฟังก์ชันศักย์: ศักย์ของ DSU จะลดลงทุกครั้งที่โหนดมีโหนดแม่อยู่ใกล้ขึ้น และการลดลงนี้จะชดเชยต้นทุนการเดินผ่าน หากไม่มี union ตามอันดับ การบีบอัดเส้นทางเพียงอย่างเดียวจะให้เวลาแบบเฉลี่ยตัดจำหน่าย O(log n) ซึ่งดีขึ้นอย่างมากจาก O(n) แบบพื้นฐาน

# Demonstrating amortised benefit
import time

def build_chain(n):
    parent = list(range(n))
    for i in range(n - 1):
        parent[i] = i + 1  # chain: 0->1->2->...->n-1
    return parent

n = 1000
parent = build_chain(n)

# First find on a chain: visits n nodes
x = 0
root = x
while parent[root] != root:
    root = parent[root]
# Compress
while parent[x] != root:
    nxt = parent[x]; parent[x] = root; x = nxt
print('After first find, parent[0]:', parent[0])  # should be n-1
print('Second find cost: O(1) since parent[0] is now the root')

จำนวนองค์ประกอบที่เชื่อมต่อกัน

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

วิธีนี้มีประสิทธิภาพมากกว่าการเรียก BFS หรือ DFS เพื่อสอบถามการเชื่อมต่อ โดยเฉพาะเมื่อมีเส้นเชื่อมเข้ามาทีละรายการ (แบบออนไลน์) DSU ประมวลผลเส้นเชื่อมแต่ละเส้นด้วยเวลาแบบเฉลี่ยตัดจำหน่ายเกือบ O(1) โดยไม่ขึ้นกับว่าเส้นเชื่อมนั้นเข้ามาเมื่อใด

class DSU:
    def __init__(self, n):
        self.parent = list(range(n))
        self.components = n

    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]

    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px == py:
            return False
        self.parent[px] = py
        self.components -= 1
        return True

dsu = DSU(7)
edges = [(0,1),(1,2),(3,4),(5,6)]
for u, v in edges:
    dsu.union(u, v)
print('Components:', dsu.components)  # 4: {0,1,2}, {3,4}, {5,6}, {6 alone was merged}
# Node 6 is alone => 4 total: {0,1,2},{3,4},{5,6},{6} wait
# Let me recalculate: 7 nodes, 4 edges merged 4 pairs => 7-4=3... no
# {0,1,2} one union, {3,4} one, {5,6} one => 7-3=4 components
print('Expected: 4')

DSU สำหรับปัญหากราฟ: จำนวนจังหวัด

ปัญหา จำนวนจังหวัด ให้เมทริกซ์การเชื่อมโยงขนาด n×n และถามว่ามีกลุ่มเมืองที่เชื่อมต่อกันโดยตรงหรือโดยอ้อมอยู่กี่กลุ่ม นี่เป็นปัญหาองค์ประกอบที่เชื่อมต่อกันโดยตรง ซึ่ง DSU แก้ได้อย่างเป็นระเบียบ เราวนพิจารณาคู่ทั้งหมด (i, j) ที่ isConnected[i][j] == 1 และเรียก union(i, j)

หลังประมวลผลการเชื่อมต่อทั้งหมดแล้ว dsu.components คือคำตอบ วิธีนี้ง่ายและเร็วกว่าการเรียก BFS จากทุกโหนดที่ยังไม่เคยเยี่ยมชม และจัดการตัวแทนแบบเมทริกซ์ได้โดยตรงโดยไม่ต้องสร้างรายการการเชื่อมโยงก่อน

def find_provinces(isConnected):
    n = len(isConnected)
    parent = list(range(n))

    def find(x):
        if parent[x] != x:
            parent[x] = find(parent[x])
        return parent[x]

    def union(x, y):
        px, py = find(x), find(y)
        if px != py:
            parent[px] = py
            return True
        return False

    count = n
    for i in range(n):
        for j in range(i + 1, n):
            if isConnected[i][j] == 1:
                if union(i, j):
                    count -= 1
    return count

matrix = [[1,1,0],[1,1,0],[0,0,1]]
print(find_provinces(matrix))  # 2: cities {0,1} and {2}

รูปแบบการบีบอัดเส้นทาง: การลดครึ่ง

นอกเหนือจากการบีบอัดแบบสองเที่ยวแล้ว ยังมีรูปแบบแบบเที่ยวเดียวที่ง่ายกว่า เรียกว่า การลดครึ่งของเส้นทาง: ขณะเดินขึ้นไปตามสายโซ่ เราจะทำให้แต่ละโหนดชี้ไปยัง โหนดปู่ แทนที่จะชี้ไปยังโหนดแม่ วิธีนี้ลดความยาวของเส้นทางลงครึ่งหนึ่งในการเดินแต่ละครั้งโดยไม่ต้องเดินเที่ยวที่สอง และเมื่อใช้ร่วมกับ union ตามอันดับ จะมีความซับซ้อนแบบเฉลี่ยตัดจำหน่าย O(alpha(n)) เช่นเดียวกัน

การลดครึ่งของเส้นทางมักเป็นที่นิยมในการเขียนโปรแกรมแข่งขัน เพราะเป็นลูปเดียวที่กระชับและไม่มีการเรียกแบบเวียนเกิดหรือการเดินซ้ำอีกเที่ยว แต่ละขั้นตอนทำ self.parent[x] = self.parent[self.parent[x]]; x = self.parent[x]

class DSUHalving:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n

    def find(self, x):
        while self.parent[x] != x:
            self.parent[x] = self.parent[self.parent[x]]  # point to grandparent
            x = self.parent[x]
        return x

    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px == py:
            return False
        if self.rank[px] < self.rank[py]:
            px, py = py, px
        self.parent[py] = px
        if self.rank[px] == self.rank[py]:
            self.rank[px] += 1
        return True

dsu = DSUHalving(8)
for u, v in [(0,1),(2,3),(4,5),(6,7),(0,2),(4,6),(0,4)]:
    dsu.union(u, v)
print('All in one component:', dsu.find(0) == dsu.find(7))

การตรวจสอบการเชื่อมต่อหลังการ union

หากต้องการตรวจสอบว่าโหนดสองโหนด connected (อยู่ในองค์ประกอบเดียวกัน) หรือไม่ ให้เรียก find(x) == find(y) หากทั้งสองการเรียกคืนค่ารากเดียวกัน แสดงว่าโหนดทั้งสองอยู่ในองค์ประกอบเดียวกัน นี่คือการสอบถามแบบ connected และเมื่อใช้การบีบอัดเส้นทาง จะใช้เวลาแบบเฉลี่ยตัดจำหน่ายเกือบ O(1)

ในโจทย์สัมภาษณ์ การสอบถามการเชื่อมต่อมักสลับปะปนกับการดำเนินการ union DSU รองรับทั้งสองอย่างแบบออนไลน์ — คุณสามารถสลับทำ union และการสอบถามได้ตามลำดับใดก็ได้ จุดนี้ทำให้ DSU แตกต่างจากอัลกอริทึมกราฟแบบสถิติเช่น BFS/DFS ซึ่งต้องทำงานใหม่หลังโครงสร้างมีการเปลี่ยนแปลงแต่ละครั้ง

class DSU:
    def __init__(self, n):
        self.parent = list(range(n))

    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]

    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px != py:
            self.parent[px] = py

    def connected(self, x, y):
        return self.find(x) == self.find(y)

dsu = DSU(10)
dsu.union(0, 3)
dsu.union(3, 7)
dsu.union(1, 5)
print(dsu.connected(0, 7))   # True: 0-3-7
print(dsu.connected(0, 5))   # False: different components
print(dsu.connected(1, 5))   # True: 1-5

ข้อผิดพลาดที่พบบ่อยในการเขียน DSU

ข้อผิดพลาดที่พบบ่อยคือการเรียก find แล้วแก้ไข parent อย่างไม่ถูกต้อง ควรเรียก find กับสมาชิกทั้งสองตัว ก่อน ตรวจสอบความเท่ากันเสมอ มิฉะนั้นคุณอาจเปรียบเทียบโหนดกับรากของตัวเองอย่างไม่ถูกต้อง อีกข้อผิดพลาดคือการลืมว่า union ควรไม่ทำอะไรเมื่อสมาชิกทั้งสองตัวมีรากเดียวกันอยู่แล้ว

ใน Python ขีดจำกัดความลึกของการเรียกแบบเวียนเกิด (ค่าเริ่มต้น 1000) อาจทำให้เกิด RecursionError เมื่อมีสายโซ่ขนาดใหญ่และใช้ find แบบเวียนเกิด คุณอาจใช้แบบวนซ้ำสองเที่ยว เพิ่มขีดจำกัดด้วย sys.setrecursionlimit หรือใช้การลดครึ่งของเส้นทางแบบวนซ้ำเพื่อหลีกเลี่ยงการเรียกแบบเวียนเกิดที่ลึกโดยสิ้นเชิง

import sys
sys.setrecursionlimit(10000)  # needed for large recursive DSU

class DSU:
    def __init__(self, n):
        self.parent = list(range(n))

    def find(self, x):
        # Safe iterative path compression
        root = x
        while self.parent[root] != root:
            root = self.parent[root]
        while self.parent[x] != root:
            nxt = self.parent[x]
            self.parent[x] = root
            x = nxt
        return root

    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px == py:
            return False  # already same component — do nothing
        self.parent[px] = py
        return True

dsu = DSU(5)
print(dsu.union(0, 1))  # True: merged
print(dsu.union(0, 1))  # False: already merged — no double-counting

การติดตามขนาดของ DSU

ในบางปัญหา คุณต้องทราบ ขนาด ของแต่ละองค์ประกอบ ไม่ใช่เพียงรากขององค์ประกอบนั้น ให้เพิ่มอาร์เรย์ size ที่เริ่มต้นเป็น 1 ทุกตำแหน่ง เมื่อรวมองค์ประกอบสององค์ประกอบ ให้บวกขนาดของรากที่เล็กกว่าเข้ากับรากที่ใหญ่กว่า วิธีนี้ทำให้สอบถามขนาดองค์ประกอบได้ในเวลา O(1) หลังการ union ใด ๆ

การติดตามขนาดยังเป็นพื้นฐานของ union ตามขนาด (ทางเลือกอื่นนอกเหนือจาก union ตามอันดับ): ให้เชื่อมต้นไม้ที่เล็กกว่าไว้ใต้รากของต้นไม้ที่ใหญ่กว่าเสมอ วิธีนี้รับประกันว่าความสูงของต้นไม้จะอยู่ที่ O(log n) และให้การรับประกันเชิงอสมมาตรเช่นเดียวกับ union ตามอันดับ

class DSUWithSize:
    def __init__(self, n):
        self.parent = list(range(n))
        self.size = [1] * n

    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]

    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px == py:
            return
        if self.size[px] < self.size[py]:
            px, py = py, px           # attach smaller under larger
        self.parent[py] = px
        self.size[px] += self.size[py]

    def get_size(self, x):
        return self.size[self.find(x)]

dsu = DSUWithSize(6)
for u, v in [(0,1),(1,2),(3,4)]:
    dsu.union(u, v)
print('Size of component containing 0:', dsu.get_size(0))  # 3
print('Size of component containing 3:', dsu.get_size(3))  # 2
print('Size of component containing 5:', dsu.get_size(5))  # 1

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

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

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

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

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

บทเรียน “DSU กับการบีบอัดเส้นทาง” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “DSU กับการบีบอัดเส้นทาง”

นำ find ที่มีการบีบอัดเส้นทางไปใช้งาน เพื่อให้โหนดทุกตัวบนเส้นทางชี้ตรงไปยังราก และทำให้ find มีเวลาเฉลี่ยตัดจำหน่ายใกล้เคียง O(1) คุณปฏิบัติ Coding Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Coding Interview Prep หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Coding Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “DSU กับการบีบอัดเส้นทาง” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Coding Interview Prep นี้ได้ไหม

ได้ บทเรียน Coding Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

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

  1. DSU กับการบีบอัดเส้นทาง
  2. การรวมตามอันดับและขอบเขตอินเวอร์สแอกเคอร์มันน์
  3. การเชื่อมโยงเกินจำเป็นและการตรวจจับวงจร
  4. การรวมบัญชีและองค์ประกอบเชื่อมโยง
← กลับไปที่ Coding Interview Prep