0Pricing
Coding Interview Prep · درس

خاصية Heap وتمثيلها كمصفوفة

افهم بنية الشجرة الثنائية الكاملة المخزنة كمصفوفة، واستنتج صيغ فهارس الآباء والأبناء، وصوّر عمليتي الرفع والإنزال

خاصية Heap وتمثيلها كمصفوفة درس مجاني في Coding Interview Prep على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Coding Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

ما الكومة؟

الكومة هي شجرة ثنائية كاملة متخصصة تستوفي خاصية الكومة: في الكومة الصغرى، تكون كل عقدة أب أصغر من أبنائها أو مساوية لهم؛ وفي الكومة الكبرى، تكون كل عقدة أب أكبر من أبنائها أو مساوية لهم. تضمن هذه الخاصية أن يكون العنصر الأصغر (أو الأكبر) دائماً في الجذر، مما يتيح الوصول إلى العنصر الأقصى أو الأدنى في O(1). الأكوام هي هيكل البيانات الذي تقوم عليه طوابير الأولوية.

# Min-heap example:
#         1
#        / \
#       3   2
#      / \ / \
#     7  4 5  6
# Every parent <= its children
# Root (1) is always the minimum

# Max-heap example:
#         9
#        / \
#       7   8
#      / \ / \
#     3  4 5  6
# Every parent >= its children
# Root (9) is always the maximum
print('Heap property: parent dominates all descendants')

بنية الشجرة الثنائية الكاملة

تُخزَّن الكومة على شكل شجرة ثنائية كاملة؛ إذ تكون جميع المستويات ممتلئة بالكامل، باستثناء المستوى الأخير الذي يُملأ من اليسار إلى اليمين. تتيح هذه البنية تمثيل المصفوفة الأنيق، من دون مساحة مهدرة أو مؤشرات. وتضمن خاصية الاكتمال أن يكون ارتفاع الكومة دائماً floor(log₂ n)، مما يضمن أن تستغرق عمليتا الدفع والإزالة O(log n).

# Complete binary tree properties:
# 1. All levels filled except possibly the last
# 2. Last level filled from LEFT to right
# 3. For n nodes: height = floor(log2(n))

# NOT complete (last level not left-filled):
#     1
#    / \
#   2   3
#        \
#         4  <- right child without left sibling

# Valid complete binary tree with 4 nodes:
#     1
#    / \
#   2   3
#  /
# 4
print('Complete BT: height = floor(log2(n)) always')

تمثيل الكومة باستخدام مصفوفة

تتيح بنية الشجرة الثنائية الكاملة تخزين الكومة في مصفوفة عادية من دون أي مؤشرات. بالنسبة إلى عقدة عند الفهرس i (بفهرسة تبدأ من الصفر)، يوجد الأب عند (i-1) // 2، والابن الأيسر عند 2i+1، والابن الأيمن عند 2i+2. تحل هذه الحسابات الصحيحة محل التنقل عبر المؤشرات، وتجعل الأكوام ملائمة جداً لذاكرة التخزين المؤقت.

# Array representation (0-indexed):
# Index:  0  1  2  3  4  5  6
# Array: [1, 3, 2, 7, 4, 5, 6]
# Tree:        1          (index 0)
#             / \         
#            3   2        (indices 1, 2)
#           / \ / \       
#          7  4 5  6      (indices 3,4,5,6)

# Index formulas (0-based):
def parent(i):      return (i - 1) // 2
def left_child(i):  return 2 * i + 1
def right_child(i): return 2 * i + 2

heap = [1, 3, 2, 7, 4, 5, 6]
print('Parent of index 3:', parent(3), '-> value', heap[parent(3)])
print('Left child of 1:', left_child(1), '-> value', heap[left_child(1)])

الرفع: استعادة الكومة بعد الإدراج

يُستخدم الرفع (ويُسمّى أيضاً الفقاقيع إلى أعلى أو بناء الكومة إلى أعلى) بعد إدراج عنصر جديد في نهاية مصفوفة الكومة. قارِن العنصر الجديد بأبيه؛ فإذا خالف خاصية الكومة، فبدّل بينهما وتابع الصعود. كرّر ذلك حتى يصل العنصر إلى موضعه الصحيح أو إلى الجذر. تستغرق هذه العملية O(log n)، لأن ارتفاع الشجرة هو O(log n).

def sift_up(heap, i):
    while i > 0:
        p = (i - 1) // 2  # parent index
        if heap[p] > heap[i]:  # min-heap: parent should be smaller
            heap[p], heap[i] = heap[i], heap[p]
            i = p
        else:
            break  # heap property restored

# Demonstrate: insert 0 into an existing min-heap
heap = [1, 3, 2, 7, 4, 5, 6]
heap.append(0)  # add at end
print('Before sift-up:', heap)
sift_up(heap, len(heap) - 1)
print('After sift-up:', heap)  # 0 should bubble to root

الخفض: استعادة الكومة بعد الإزالة

يُستخدم الخفض (بناء الكومة إلى أسفل) بعد إزالة الجذر. انقل العنصر الأخير إلى الجذر، ثم ادفعه إلى الأسفل عبر التبديل المتكرر مع الابن الأصغر (في الكومة الصغرى) حتى تُستعاد خاصية الكومة. وتستغرق هذه العملية أيضاً O(log n). يُعد الرفع والخفض اللبنات الأساسية لجميع عمليات الكومة.

def sift_down(heap, i, n):
    while True:
        smallest = i
        l = 2 * i + 1  # left child
        r = 2 * i + 2  # right child
        if l < n and heap[l] < heap[smallest]:
            smallest = l
        if r < n and heap[r] < heap[smallest]:
            smallest = r
        if smallest == i:
            break  # already in correct position
        heap[i], heap[smallest] = heap[smallest], heap[i]
        i = smallest

heap = [1, 3, 2, 7, 4, 5, 6]
# Pop min: move last to root, then sift-down
heap[0] = heap[-1]
heap.pop()
print('After move last to root:', heap)
sift_down(heap, 0, len(heap))
print('After sift-down:', heap)  # valid min-heap again

بناء كومة من مصفوفة: خوارزمية Floyd

يستغرق إدراج n من العناصر واحداً تلو الآخر بطريقة ساذجة O(n log n). أما خوارزمية Floyd لبناء الكومة فتبني كومة في O(n) عبر تطبيق الخفض على كل عقدة غير ورقية، بدءاً من آخر عقدة غير ورقية (الفهرس n//2 - 1) والعمل عكسياً حتى الجذر. العقد الورقية أكوام بسيطة بالفعل، لذلك لا نحتاج إلا إلى إصلاح العقد الداخلية؛ ولهذا يساوي مجموع العمل O(n) بدلاً من O(n log n).

def build_heap(arr):
    n = len(arr)
    # Start from last non-leaf node: index n//2 - 1
    for i in range(n // 2 - 1, -1, -1):
        sift_down(arr, i, n)
    return arr

arr = [5, 3, 8, 1, 9, 2, 7]
print('Before:', arr)
build_heap(arr)
print('After (min-heap):', arr)  # root should be 1

# Why O(n)? Most nodes are near the bottom (leaves).
# Level k from bottom has ~n/2^k nodes, each needing
# at most k swaps. Sum = n * sum(k/2^k) = O(n).

ترتيب الكومة باستخدام كومة المصفوفة

يعمل ترتيب الكومة في O(n log n) مع استخدام مساحة إضافية O(1). المرحلة الأولى: بناء كومة كبرى من المصفوفة في O(n). المرحلة الثانية: استخراج القيمة الكبرى بشكل متكرر عبر التبديل بين الجذر وآخر عنصر غير مرتب، ثم إجراء الخفض على الكومة المصغّرة. بعد n من عمليات الاستخراج، تُرتّب المصفوفة تصاعدياً. توضّح هذه الخوارزمية التي تعمل داخل المصفوفة كيف يتيح تمثيل المصفوفة إجراء الترتيب من دون تخصيص هيكل بيانات منفصل.

def sift_down_max(arr, i, n):
    while True:
        largest = i
        l, r = 2*i+1, 2*i+2
        if l < n and arr[l] > arr[largest]: largest = l
        if r < n and arr[r] > arr[largest]: largest = r
        if largest == i: break
        arr[i], arr[largest] = arr[largest], arr[i]
        i = largest

def heap_sort(arr):
    n = len(arr)
    # Build max-heap
    for i in range(n // 2 - 1, -1, -1):
        sift_down_max(arr, i, n)
    # Extract elements one by one
    for end in range(n - 1, 0, -1):
        arr[0], arr[end] = arr[end], arr[0]  # move max to end
        sift_down_max(arr, 0, end)

arr = [5, 3, 8, 1, 9, 2, 7]
heap_sort(arr)
print(arr)  # [1, 2, 3, 5, 7, 8, 9]

الكومة الصغرى مقابل الكومة الكبرى

تحتوي الكومة الصغرى على أصغر عنصر في الجذر؛ لذا تعيد عملية الإزالة دائماً القيمة الصغرى. أما الكومة الكبرى فتحتوي على أكبر عنصر في الجذر؛ لذا تعيد عملية الإزالة دائماً القيمة الكبرى. لهما البنية والعمليات نفسيهما، ولا يتغير سوى اتجاه المقارنة. تنفّذ وحدة heapq في Python كومة صغرى فقط، لذلك يجب نفي القيم لمحاكاة كومة كبرى.

import heapq

# Python heapq is a MIN-HEAP
min_heap = []
heapq.heappush(min_heap, 5)
heapq.heappush(min_heap, 1)
heapq.heappush(min_heap, 3)
print('Min-heap min:', heapq.heappop(min_heap))  # 1

# Simulate MAX-HEAP by negating values
max_heap = []
for val in [5, 1, 3]:
    heapq.heappush(max_heap, -val)  # negate on push
print('Max-heap max:', -heapq.heappop(max_heap))  # 5 (negate on pop)

# For tuples: heapq sorts by first element
print(min_heap, max_heap)

ملخص تعقيد عمليات الكومة

تنبثق جميع عمليات الكومة من الرفع والخفض، ويستغرق كل منهما O(log n). الدفع: الإلحاق ثم الرفع = O(log n). الإزالة: التبديل بين الجذر والعنصر الأخير ثم الخفض = O(log n). المعاينة: الوصول إلى الفهرس 0 = O(1). بناء الكومة: O(n) باستخدام خوارزمية Floyd. ترتيب الكومة: O(n log n). تجعل هذه التعقيدات الأكوام بنية مثالية عندما تحتاج بشكل متكرر إلى أصغر أو أكبر عنصر في مجموعة ديناميكية.

# Heap complexity summary:
# Operation     | Time       | Space
# --------------|------------|-------
# Push          | O(log n)   | O(1)
# Pop (min/max) | O(log n)   | O(1)
# Peek          | O(1)       | O(1)
# Build from n  | O(n)       | O(1) in-place
# Heap sort     | O(n log n) | O(1)
# nlargest(k,n) | O(n log k) | O(k)

import heapq
data = [5, 3, 8, 1, 9, 2, 7]
print('Top 3 largest:', heapq.nlargest(3, data))  # [9, 8, 7]
print('Top 3 smallest:', heapq.nsmallest(3, data))  # [1, 2, 3]

أنماط عملية لاستخدام الأكوام في المقابلات

تحل الأكوام عائلة من مسائل المقابلات ذات النمط المشترك الآتي: الحفاظ على طابور أولوية يضم k من المرشحين أثناء معالجة n من العناصر المتدفقة. تستخدم مسائل أكثر العناصر تكراراً، وk من النقاط الأقرب إلى الأصل، وجدولة المهام هذا النمط جميعاً. تعرّف إلى هذا النمط عندما ترى: «لديك تدفق من n عنصراً، وحافظ على أفضل k منها»؛ فهذا يتطلب دائماً كومة بحجم k، ويعطي زمناً إجمالياً قدره O(n log k).

import heapq

# Top-K closest points to origin using a max-heap of size k
def k_closest(points, k):
    # Use max-heap (negate distance) of size k
    heap = []
    for x, y in points:
        dist = -(x*x + y*y)  # negate for max-heap
        heapq.heappush(heap, (dist, x, y))
        if len(heap) > k:
            heapq.heappop(heap)  # remove farthest
    return [[x, y] for _, x, y in heap]

points = [[1,3], [-2,2], [5,8], [0,1]]
print(k_closest(points, 2))  # 2 closest to origin

المفاضلة بين الكومة والمصفوفة المرتبة

اختر كومة عندما تحتاج فقط إلى الوصول المتكرر إلى أصغر أو أكبر عنصر، وعندما تتغير المجموعة ديناميكياً. واختر مصفوفة مرتبة عندما تحتاج إلى الوصول العشوائي حسب الفهرس أو إلى استعلامات النطاق. تكمن نقطة ضعف الكومة في أن البحث عن عناصر عشوائية يستغرق O(n)، بينما تكمن قوتها في أن الإدراج والحذف يستغرقان O(log n)، والوصول إلى الأصغر أو الأكبر يستغرق O(1). أما المصفوفة المرتبة، فيستغرق الإدراج فيها O(n)، لكن البحث باستخدام البحث الثنائي يستغرق O(log n).

# Trade-off comparison:
# Structure     | insert  | delete_min | search | range_query
# --------------|---------|------------|--------|------------
# Min-heap      | O(logn) | O(logn)    | O(n)   | O(n)
# Sorted array  | O(n)    | O(n)       | O(logn)| O(logn+k)
# BST (balanced)| O(logn) | O(logn)    | O(logn)| O(logn+k)
# Hash map      | O(1)    | O(1)       | O(1)   | O(n)

# Interview heuristic:
# 'Find minimum repeatedly from dynamic collection' -> HEAP
# 'Binary search or range query' -> sorted array or BST
# 'Fast lookup by key' -> hash map
print('Heap = dynamic collection with priority access')

تحقق سريع

اختبر مدى فهمك لمفاهيم Data Structures & Algorithms — Coding Interview Prep الواردة في هذا الدرس.

مراجعة الدرس

تعلّمت في هذا الدرس: خاصية الكومة وبنية الشجرة الثنائية الكاملة، وتمثيل المصفوفة مع صيغ فهارس الأبناء والآباء، والرفع والخفض بوصفهما اللبنات الأساسية لجميع عمليات الكومة، بما فيها البناء في O(n) باستخدام خوارزمية Floyd. بعد ذلك سنطبّق بناء الكومة ونستكشف وحدة heapq في Python.

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

هل درس «خاصية Heap وتمثيلها كمصفوفة» مجاني؟

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

ماذا ستتعلم في «خاصية Heap وتمثيلها كمصفوفة»؟

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

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

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

كم من الوقت يستغرق درس «خاصية Heap وتمثيلها كمصفوفة»؟

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

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

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

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

  1. خاصية Heap وتمثيلها كمصفوفة
  2. بناء Heap وPush وPop من الصفر
  3. heapq في Python وحيل Max-Heap
  4. الوسيط من تدفق البيانات والدمج K-Way
← العودة إلى Coding Interview Prep