0Pricing
DSA Interview Prep · درس

تعقيد المساحة والمفاضلات

قِس المساحة المساعدة لمكدسات الاستدعاء وهياكل البيانات المساعدة، وتعرّف إلى مفاضلات الزمن والمساحة في التخزين المؤقت والخوارزميات التي تعمل في مكانها

تعقيد المساحة والمفاضلات درس مجاني في DSA Interview Prep على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 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]

الموازنة بين الزمن والمساحة: Two-Sum

تظهر الموازنة بين الزمن والمساحة في كل مكان. تبلغ كلفة Two-Sum الزمنية 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) — أي البرمجة الديناميكية المحسّنة مكانيًا.

# 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]

التعرّف على موازنات المساحة في المقابلات

اذكر التعقيد المكاني في البداية. إذا أراد المحاوِر مساحة أقل، فمن الخيارات الشائعة استخدام البرمجة الديناميكية من أسفل إلى أعلى بدلًا من الحفظ، أو استخدام فرز داخل المكان بدلًا من خريطة تجزئة. اطّلع على الشيفرة.

# 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(العمق)، كما أن الموازنة بين الزمن والمساحة توجه معظم خيارات تصميم الخوارزميات.

الأسئلة الشائعة

هل درس «تعقيد المساحة والمفاضلات» مجاني؟

نعم — نص درس «تعقيد المساحة والمفاضلات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة DSA Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة DSA Interview Prep 4 دروس في المجموع.

ماذا ستتعلم في «تعقيد المساحة والمفاضلات»؟

قِس المساحة المساعدة لمكدسات الاستدعاء وهياكل البيانات المساعدة، وتعرّف إلى مفاضلات الزمن والمساحة في التخزين المؤقت والخوارزميات التي تعمل في مكانها تتمرن على DSA Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ DSA Interview Prep؟

لا تُشترط خبرة سابقة. DSA Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «تعقيد المساحة والمفاضلات»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس DSA Interview Prep هذا؟

نعم. كل درس في DSA Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ترميز Big-O من الصفر
  2. تحليل الحلقات والحلقات المتداخلة
  3. الاستدعاء الذاتي وطريقة شجرة الاستدعاء الذاتي
  4. تعقيد المساحة والمفاضلات
← العودة إلى DSA Interview Prep