ความซับซ้อนด้านพื้นที่และการแลกเปลี่ยน
วัดพื้นที่เสริมสำหรับสแตกการเรียกและโครงสร้างข้อมูลเสริม พร้อมทำความเข้าใจการแลกเปลี่ยนระหว่างเวลาและพื้นที่ในการจดจำผลลัพธ์และอัลกอริทึมแบบทำงานในที่เดิม
ความซับซ้อนด้านพื้นที่และการแลกเปลี่ยน เป็นบทเรียน DSA Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน DSA Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส DSA Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
ความซับซ้อนด้านพื้นที่วัดอะไร
ความซับซ้อนด้านพื้นที่วัดหน่วยความจำเพิ่มเติมนอกเหนือจากข้อมูลเข้า ซึ่งเรียกว่าพื้นที่ช่วย ตัวแปรไม่กี่ตัวมีความซับซ้อนเป็น O(1) ส่วนอาร์เรย์ผลลัพธ์หรือตารางแฮชมีความซับซ้อนเป็น O(n) ดูโค้ดได้เลย
# O(1) auxiliary space
def sum_array(nums):
total = 0 # one integer variable
for n in nums:
total += n # constant extra space
return total
# O(n) auxiliary space
def copy_array(nums):
return list(nums) # allocates n slots
print(sum_array([1, 2, 3, 4])) # 10
print(copy_array([1, 2, 3, 4])) # [1, 2, 3, 4]พื้นที่สแตกการเรียกฟังก์ชันในการเรียกซ้ำ
การเรียกซ้ำแต่ละครั้งเพิ่มเฟรมของสแตกหนึ่งเฟรม ดังนั้นความลึกจึงเป็นตัวกำหนดพื้นที่ การเรียกซ้ำแบบเชิงเส้นมีความซับซ้อนเป็น O(n) ส่วนการค้นหาแบบ DFS บนต้นไม้สมดุลมีความซับซ้อนเป็น O(log n) การเขียนแบบวนซ้ำช่วยควบคุมพื้นที่ได้ดีกว่า
import sys
def recursive_sum(n):
if n == 0: return 0
return n + recursive_sum(n - 1)
# Space: O(n) stack frames
def iterative_sum(n):
total = 0
while n > 0:
total += n
n -= 1
return total
# Space: O(1)
print(recursive_sum(100)) # 5050
print(iterative_sum(100)) # 5050พื้นที่ของการเรียงลำดับแบบผสาน: O(n)
การเรียงลำดับแบบผสานต้องใช้พื้นที่เพิ่มเติม O(n) สำหรับอาร์เรย์ชั่วคราว นี่คือราคาที่ต้องจ่ายเพื่อการเรียงลำดับแบบคงเสถียรภาพที่มีความซับซ้อน O(n log n) — การเรียงลำดับแบบฮีปประหยัดพื้นที่กว่า แต่ไม่คงเสถียรภาพ ดูโค้ดได้เลย
import tracemalloc
tracemalloc.start()
def merge_sort(arr):
if len(arr) <= 1: return arr
m = len(arr) // 2
l = merge_sort(arr[:m]) # new list
r = merge_sort(arr[m:]) # new list
out, i, j = [], 0, 0
while i < len(l) and j < len(r):
if l[i] <= r[j]: out.append(l[i]); i+=1
else: out.append(r[j]); j+=1
return out + l[i:] + r[j:]
data = list(range(1000, 0, -1))
merge_sort(data)
_, peak = tracemalloc.get_traced_memory()
print(f'Peak memory: {peak} bytes') # proportional to nอัลกอริทึมที่ทำงานในข้อมูลเดิม: พื้นที่ O(1)
อัลกอริทึมที่ทำงานแบบ ในข้อมูลเดิมจะเปลี่ยนข้อมูลเข้าโดยตรงโดยไม่ใช้พื้นที่เพิ่มเติมที่เพิ่มขึ้นตามขนาดข้อมูล — เช่น การกลับลำดับอาร์เรย์ด้วยตัวชี้สองตัว จึงรักษาความซับซ้อนด้านพื้นที่ไว้ที่ O(1) ดูโค้ดได้เลย
def reverse_inplace(arr):
l, r = 0, len(arr) - 1
while l < r:
arr[l], arr[r] = arr[r], arr[l] # swap
l += 1
r -= 1
# Space: O(1) -- only two pointer variables
def rotate_right(arr, k):
'''Rotate array right by k positions in-place.'''
n = len(arr)
k %= n
arr.reverse() # O(1) space
arr[:k] = arr[:k][::-1]
arr[k:] = arr[k:][::-1]
a = [1, 2, 3, 4, 5]
rotate_right(a, 2)
print(a) # [4, 5, 1, 2, 3]การแลกเปลี่ยนระหว่างเวลาและพื้นที่: ผลรวมสองตัว
การแลกเปลี่ยนระหว่างเวลาและพื้นที่พบได้ทั่วไป ปัญหาผลรวมสองตัวใช้เวลา O(n^2) และพื้นที่ O(1) หรือใช้เวลา O(n) และพื้นที่ O(n) โดยใช้ตารางแฮช ควรกล่าวถึงทั้งสองทางเลือกและถามว่าอะไรสำคัญกว่ากัน
# O(n^2) time, O(1) space
def two_sum_slow(nums, target):
for i in range(len(nums)): # O(n)
for j in range(i+1, len(nums)): # O(n)
if nums[i] + nums[j] == target:
return [i, j]
return []
# O(n) time, O(n) space
def two_sum_fast(nums, target):
seen = {} # O(n) space
for i, n in enumerate(nums):
comp = target - n
if comp in seen: # O(1) lookup
return [seen[comp], i]
seen[n] = i
return []
print(two_sum_fast([2, 7, 11, 15], 9)) # [0, 1]พื้นที่ของการจดจำผลลัพธ์เทียบกับการสร้างตาราง
การจดจำผลลัพธ์แบบจากบนลงล่างใช้พื้นที่ O(n) สำหรับผลลัพธ์ที่จดจำไว้ และ O(n) สำหรับสแตก ส่วนการสร้างตารางแบบจากล่างขึ้นบนไม่ต้องใช้สแตก หากเก็บไว้เพียงไม่กี่แถวสุดท้าย จะลดพื้นที่เหลือ O(1) — นี่คือ DP ที่ปรับลดการใช้พื้นที่
# Fibonacci: O(n) space with full table
def fib_table(n):
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
# O(1) space: keep only last two values
def fib_optimal(n):
if n <= 1: return n
a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b
return b
print(fib_table(10)) # 55
print(fib_optimal(10)) # 55พื้นที่ของตารางแฮช: O(n)
ตารางแฮชเป็นค่าใช้จ่ายด้านพื้นที่ O(n) ที่พบได้บ่อยในวิธีแก้ปัญหา: ใช้เซตสำหรับติดตามสมาชิกที่พบแล้ว และใช้แผนผังความถี่สำหรับการนับ ต้องรายงานค่านี้เสมอ — "เวลา O(n) พื้นที่ O(n)" จึงจะเป็นคำตอบที่ครบถ้วน
def contains_duplicate(nums):
# O(n) time, O(n) space
seen = set()
for n in nums:
if n in seen: return True
seen.add(n)
return False
def group_anagrams(words):
# O(n*m) time, O(n) space (m = avg word length)
from collections import defaultdict
groups = defaultdict(list)
for w in words:
groups[tuple(sorted(w))].append(w)
return list(groups.values())
print(contains_duplicate([1,2,3,1])) # True
print(group_anagrams(['eat','tea','tan','ate','nat','bat']))การวิเคราะห์พื้นที่สำหรับอัลกอริทึมกราฟ
กราฟใช้พื้นที่จริง: รายการเพื่อนบ้านมีความซับซ้อนเป็น O(V + E) เซตสมาชิกที่ BFS พบแล้วและคิวมีความซับซ้อนเป็น O(V) ส่วนการเรียกซ้ำของ DFS มีความลึกเป็น O(V) ให้รายงานพื้นที่ของกราฟโดยใช้ V และ E
from collections import deque
def bfs(graph, start):
# Space: O(V) for visited set + O(V) for queue
visited = set() # O(V)
queue = deque([start]) # O(V) max
order = []
while queue:
node = queue.popleft()
if node in visited: continue
visited.add(node)
order.append(node)
for nb in graph.get(node, []):
queue.append(nb)
return order
g = {0:[1,2], 1:[3], 2:[3], 3:[]}
print(bfs(g, 0)) # [0, 1, 2, 3]ข้อผิดพลาดจากการสร้างสตริงและอาร์เรย์
การจัดสรรพื้นที่ที่ซ่อนอยู่ทำให้ใช้พื้นที่ O(n) ได้: การตัดแบ่งจะสร้างรายการใหม่ และการใช้ + กับสตริงในลูปมีความซับซ้อนเป็น O(n^2) sorted() จะคัดลอกข้อมูล แต่ lst.sort() ยังคงทำงานในข้อมูลเดิม ดูโค้ดได้เลย
# Hidden allocations:
nums = [1, 2, 3, 4, 5]
# Creates a NEW list -- O(n) space
slice_copy = nums[1:4] # [2, 3, 4]
# Creates a NEW sorted list -- O(n) space
sorted_copy = sorted(nums) # nums unchanged
# Sorts IN PLACE -- O(1) extra space
nums.sort()
print(slice_copy) # [2, 3, 4]
print(sorted_copy) # [1, 2, 3, 4, 5]
print(nums) # [1, 2, 3, 4, 5]การมองการแลกเปลี่ยนด้านพื้นที่ในการสัมภาษณ์
ระบุ ความซับซ้อนด้านพื้นที่ของคุณตั้งแต่ต้น หากผู้สัมภาษณ์ต้องการใช้พื้นที่น้อยลง วิธีที่ใช้บ่อยคือเปลี่ยนจากการจดจำผลลัพธ์เป็น DP แบบจากล่างขึ้นบน หรือเปลี่ยนจากตารางแฮชเป็นการเรียงลำดับในข้อมูลเดิม ดูโค้ดได้เลย
# Problem: find if array has duplicates
# Option 1: O(1) time-per-check, O(n) space
def has_dup_hash(nums):
return len(nums) != len(set(nums))
# Option 2: O(n log n) time, O(1) extra space
def has_dup_sort(nums):
nums_copy = sorted(nums) # O(n) space -- still!
for i in range(1, len(nums_copy)):
if nums_copy[i] == nums_copy[i-1]:
return True
return False
# Option 3: truly O(1) extra -- sort in-place
def has_dup_inplace(nums):
nums.sort() # modifies original
for i in range(1, len(nums)):
if nums[i] == nums[i-1]: return True
return Falseแม่แบบสำหรับระบุความซับซ้อนทั้งหมด
ระบุคำตอบให้ ครบถ้วนเสมอ — ทั้งเวลาและพื้นที่: "ใช้เวลา O(n) และใช้พื้นที่เพิ่มเติม O(1)" กล่าวถึงการแลกเปลี่ยนด้วยหากมี นี่คือสิ่งที่ทำให้ผู้สมัครระดับอาวุโสโดดเด่น
# Complete complexity example: Merge Intervals
def merge_intervals(intervals):
# Time: O(n log n) for sort + O(n) for merge = O(n log n)
# Space: O(n) for output (could be n/2 to n intervals)
intervals.sort(key=lambda x: x[0]) # O(n log n)
merged = [intervals[0]]
for start, end in intervals[1:]:
if start <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], end)
else:
merged.append([start, end])
return merged
print(merge_intervals([[1,3],[2,6],[8,10],[15,18]]))
# [[1,6],[8,10],[15,18]]ตรวจสอบความเข้าใจอย่างรวดเร็ว
ตรวจสอบความเข้าใจอย่างรวดเร็ว — มาดูกันว่าแนวคิดเรื่องความซับซ้อนด้านพื้นที่เข้าที่ดีแค่ไหน คุณพร้อมสำหรับเรื่องนี้แล้ว ✅
ทบทวนบทเรียน
ทบทวน: พื้นที่ช่วยจะนับแยกจากข้อมูลเข้า การเรียกซ้ำใช้พื้นที่สแตกเป็น O(ความลึก) และ การแลกเปลี่ยนระหว่างเวลาและพื้นที่เป็นตัวขับเคลื่อนการเลือกออกแบบอัลกอริทึมส่วนใหญ่
คำถามที่พบบ่อย
บทเรียน “ความซับซ้อนด้านพื้นที่และการแลกเปลี่ยน” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ความซับซ้อนด้านพื้นที่และการแลกเปลี่ยน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส DSA Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส DSA Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ความซับซ้อนด้านพื้นที่และการแลกเปลี่ยน”
วัดพื้นที่เสริมสำหรับสแตกการเรียกและโครงสร้างข้อมูลเสริม พร้อมทำความเข้าใจการแลกเปลี่ยนระหว่างเวลาและพื้นที่ในการจดจำผลลัพธ์และอัลกอริทึมแบบทำงานในที่เดิม คุณปฏิบัติ DSA Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน DSA Interview Prep หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน DSA Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “ความซับซ้อนด้านพื้นที่และการแลกเปลี่ยน” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน DSA Interview Prep นี้ได้ไหม
ได้ บทเรียน DSA Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- สัญกรณ์ Big-O ตั้งแต่พื้นฐาน
- วิเคราะห์ลูปและลูปซ้อน
- การเรียกซ้ำและวิธีต้นไม้การเรียกซ้ำ
- ความซับซ้อนด้านพื้นที่และการแลกเปลี่ยน