أساسيات المصفوفات والعمليات في مكانها
راجِع الفهرسة والتعديل وأكثر أخطاء مقابلات المصفوفات شيوعًا، مثل أخطاء تجاوز الحدود وتعديل قائمة أثناء التكرار
أساسيات المصفوفات والعمليات في مكانها درس مجاني في Coding Interview Prep على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Coding Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.
المصفوفات كذاكرة متجاورة
في باطن Python، تستند القائمة إلى مصفوفة ديناميكية — وهي كتلة متجاورة من الذاكرة تُخزَّن فيها العناصر في عناوين متتالية. يتيح هذا التخطيط الوصول العشوائي بتعقيد O(1) باستخدام الفهرس: تحسب Python address = base + index × element_size فورًا. ويتطلب الإدراج أو الحذف من الوسط إزاحة جميع العناصر اللاحقة، بتكلفة O(n). ويُعد هذا التفاوت مصدر معظم نقاشات الموازنة المتعلقة بالمصفوفات في المقابلات.
nums = [10, 20, 30, 40, 50]
# O(1) random access
print(nums[2]) # 30
print(nums[-1]) # 50
# O(1) append (amortised)
nums.append(60)
print(nums) # [10,20,30,40,50,60]
# O(n) insert at beginning
nums.insert(0, 0) # shifts all elements right
print(nums) # [0,10,20,30,40,50,60]خطأ الإزاحة بمقدار واحد: خطأ المصفوفات الكلاسيكي
تُعدّ أخطاء الإزاحة بمقدار واحد أكثر أسباب الإجابات الخاطئة شيوعًا في مسائل المصفوفات. يعني الفهرس الصفري في Python أن آخر فهرس صالح هو len(arr) - 1. عند كتابة الحلقات، حدّد ما إذا كنت تحتاج إلى < أو <= من خلال التحقق من شرط الحدود باستخدام أصغر إدخال صالح (n=1 أو n=2). تتبّع حدودك دائمًا بأمثلة ملموسة قبل الإرسال.
def find_max(nums):
# Use len(nums)-1 as last index
max_val = nums[0] # safe if n >= 1
for i in range(1, len(nums)): # start at 1, not 0
if nums[i] > max_val:
max_val = nums[i]
return max_val
print(find_max([3, 1, 4, 1, 5])) # 5
print(find_max([7])) # 7 (single element)
# Would crash if we accessed nums[len(nums)]العكس داخل المكان باستخدام مؤشرين
يستخدم عكس المصفوفة داخل المكان مؤشرين يبدأان من طرفين متقابلين، ويتبادلان العناصر باتجاه الداخل حتى يلتقيا. يتطلب ذلك مساحة إضافية O(1) وزمنًا O(n). يضمن الشرط left < right (أصغر من بشكل صارم) صحة التنفيذ مع الأطوال الزوجية والفردية؛ فعند وجود عدد فردي من العناصر يبقى العنصر الأوسط في مكانه تلقائيًا.
def reverse_inplace(arr):
left, right = 0, len(arr) - 1
while left < right:
arr[left], arr[right] = arr[right], arr[left]
left += 1
right -= 1
# Space: O(1) Time: O(n)
a = [1, 2, 3, 4, 5]
reverse_inplace(a)
print(a) # [5, 4, 3, 2, 1]
b = [1, 2, 3]
reverse_inplace(b)
print(b) # [3, 2, 1] middle element unchangedتدوير المصفوفة داخل المكان
يمكن تدوير المصفوفة إلى اليمين بمقدار k موضعًا داخل المكان عبر عكس ثلاثة أجزاء: اعكس المصفوفة كاملة، ثم اعكس أول k عنصرًا، ثم اعكس العناصر n-k المتبقية. يحقق ذلك زمنًا O(n) ومساحة O(1) — وهو أفضل بكثير من أسلوب التقطيع والدمج الذي يستهلك مساحة O(n). خفّض k دائمًا باستخدام باقي القسمة على n للتعامل مع k ≥ n.
def rotate(nums, k):
n = len(nums)
k %= n # handle k >= n
def rev(l, r):
while l < r:
nums[l], nums[r] = nums[r], nums[l]
l += 1; r -= 1
rev(0, n-1) # reverse all
rev(0, k-1) # reverse first k
rev(k, n-1) # reverse rest
a = [1, 2, 3, 4, 5, 6, 7]
rotate(a, 3)
print(a) # [5, 6, 7, 1, 2, 3, 4]إزالة العناصر داخل المكان
تستخدم إزالة التكرارات أو القيم المستهدفة داخل المكان مؤشر كتابة يتتبع الموضع الذي ينبغي كتابة العنصر الصالح التالي فيه. ويمسح مؤشر القراءة إلى الأمام؛ وعندما يجد عنصرًا صالحًا، ينسخه إلى موضع الكتابة ثم يحرّك المؤشرين. هذا هو النمط الأساسي لمسائل LeetCode مثل 'remove element' و'remove duplicates from sorted array' و'move zeroes'.
def remove_element(nums, val):
write = 0
for read in range(len(nums)):
if nums[read] != val:
nums[write] = nums[read]
write += 1
return write # new length
nums = [3, 2, 2, 3]
new_len = remove_element(nums, 3)
print(nums[:new_len]) # [2, 2]
nums2 = [0, 1, 2, 2, 3, 0, 4, 2]
new_len2 = remove_element(nums2, 2)
print(nums2[:new_len2]) # [0, 1, 3, 0, 4]تحريك الأصفار: مؤشر القراءة والكتابة
انقل جميع الأصفار إلى نهاية المصفوفة مع الحفاظ على ترتيب العناصر غير الصفرية. يضع نهج مؤشر القراءة والكتابة كل عنصر غير صفري في موضع الكتابة، ثم يملأ الجزء الأخير بالأصفار. وهناك نهج بديل يبدّل الأصفار إلى الخلف، مع الحفاظ على الترتيب دون تنفيذ مرور ثانٍ لملء الجزء الأخير. كلا النهجين يعملان في زمن O(n) وبمساحة O(1).
def move_zeroes(nums):
write = 0
# Move all non-zeroes to front
for read in range(len(nums)):
if nums[read] != 0:
nums[write] = nums[read]
write += 1
# Fill rest with zeroes
while write < len(nums):
nums[write] = 0
write += 1
a = [0, 1, 0, 3, 12]
move_zeroes(a)
print(a) # [1, 3, 12, 0, 0]تربيع العناصر وترتيبها في مكانها
مع إعطائك مصفوفة مرتبة من الأعداد الصحيحة (قد تتضمن أعدادًا سالبة)، أعد مصفوفة تحتوي على مربعاتها بترتيب تصاعدي. يربّع النهج الساذج العناصر ثم يرتبها، بتعقيد O(n log n). أما نهج المؤشرين الأمثل، فيستفيد من أن أكبر المربعات تأتي من أحد طرفي المصفوفة المرتبة: قارن القيم المطلقة للعنصرين في أقصى اليسار وأقصى اليمين، واملأ الناتج من اليمين إلى اليسار في زمن O(n).
def sorted_squares(nums):
n = len(nums)
result = [0] * n
left, right = 0, n - 1
pos = n - 1 # fill from the right
while left <= right:
l_sq = nums[left] ** 2
r_sq = nums[right] ** 2
if l_sq > r_sq:
result[pos] = l_sq
left += 1
else:
result[pos] = r_sq
right -= 1
pos -= 1
return result
print(sorted_squares([-4, -1, 0, 3, 10]))
# [0, 1, 9, 16, 100]العثور على المحور وتقسيم المصفوفة
تقسّم مشكلة العلم الوطني الهولندي المصفوفة داخل مكانها إلى ثلاثة أقسام (أصغر من المحور، ومساوٍ له، وأكبر منه) باستخدام ثلاثة مؤشرات. وهذه خطوة فرعية أساسية في الترتيب السريع، كما أنها حل مسألة LeetCode المسماة 'ترتيب الألوان'. ويقود الحفاظ على الثابت الذي يضمن أن العناصر قبل مؤشر low أصغر من المحور، وأن العناصر بعد مؤشر high أكبر منه، الخوارزمية.
def sort_colors(nums):
# Dutch national flag: 0s, 1s, 2s
low, mid, high = 0, 0, len(nums) - 1
while mid <= high:
if nums[mid] == 0:
nums[low], nums[mid] = nums[mid], nums[low]
low += 1; mid += 1
elif nums[mid] == 1:
mid += 1
else:
nums[mid], nums[high] = nums[high], nums[mid]
high -= 1 # don't advance mid: new nums[mid] unexamined
a = [2, 0, 2, 1, 1, 0]
sort_colors(a)
print(a) # [0, 0, 1, 1, 2, 2]تعديل عناصر المصفوفة أثناء التكرار
يمكنك بأمان تعديل قيم العناصر (مثل ضربها في -1 لتمييز العناصر التي تمت زيارتها) أثناء التكرار، لكن لا تغيّر طول القائمة مطلقًا أثناء حلقة for. ومن الحيل الآمنة للترميز ترميز قيمتين مؤقتًا داخل عدد صحيح واحد (مثل استخدام بت الإشارة)، لمحاكاة قيمة منطقية إضافية لكل عنصر دون حجز مساحة إضافية. يظهر هذا الأسلوب في مسائل مثل 'العثور على جميع الأعداد المفقودة من مصفوفة'.
def find_disappeared(nums):
# Mark visited by negating the value at the index
for n in nums:
idx = abs(n) - 1
if nums[idx] > 0:
nums[idx] *= -1 # mark as seen
# Indices with positive values are missing
return [i + 1 for i, v in enumerate(nums) if v > 0]
print(find_disappeared([4, 3, 2, 7, 8, 2, 3, 1]))
# [5, 6] -- O(n) time, O(1) extra spaceقائمة التحقق من أنماط مقابلات المصفوفات
قبل كتابة أي مسألة عن المصفوفات، مرّر ذهنيًا قائمة التحقق التالية:
- هل المصفوفة مرتبة؟ (يتيح ذلك استخدام مؤشرين والبحث الثنائي)
- هل العناصر محصورة ضمن نطاق معين (مثل 1..n)؟ (يتيح ذلك حيلًا تعتمد على الفهارس)
- هل يُشترط التعديل داخل المصفوفة نفسها؟ (استخدم مؤشر القراءة والكتابة أو عمليات التبديل)
- هل أحتاج إلى جميع الأزواج أم إلى زوج واحد فقط؟ (يؤثر ذلك في مدى ملاءمة الحلقات المتداخلة)
- الحالات الحدية: مصفوفة فارغة، عنصر واحد، وقيم متطابقة بالكامل
def max_profit(prices):
# Pattern: single scan, track running minimum
# Time: O(n), Space: O(1)
if not prices: return 0 # edge case: empty
min_price = prices[0]
max_prof = 0
for price in prices[1:]: # start at index 1
max_prof = max(max_prof, price - min_price)
min_price = min(min_price, price)
return max_prof
print(max_profit([7, 1, 5, 3, 6, 4])) # 5
print(max_profit([7, 6, 4, 3, 1])) # 0خوارزمية Kadane: المصفوفة الجزئية ذات المجموع الأقصى
تجد خوارزمية Kadane المصفوفة الجزئية المتجاورة ذات أكبر مجموع في زمن O(n) وبمساحة O(1). في كل خطوة، قرّر ما إذا كنت ستمدّد المصفوفة الجزئية الحالية أم ستبدأ مصفوفة جديدة: current = max(num, current + num). إذا كان current + num أصغر من num وحده، فهذا يعني أن المصفوفة الجزئية الحالية تخفض المجموع، لذا نبدأ من جديد. احرص على تتبّع القيمة العظمى العامة طوال العملية.
def max_subarray(nums):
current = global_max = nums[0]
for n in nums[1:]:
current = max(n, current + n) # extend or restart
global_max = max(global_max, current)
return global_max
print(max_subarray([-2, 1, -3, 4, -1, 2, 1, -5, 4]))
# 6 (subarray [4, -1, 2, 1])
print(max_subarray([-1, -2, -3]))
# -1 (all negative: take the least negative)اختبار سريع
اختبر مدى فهمك لمفاهيم Data Structures & Algorithms — Coding Interview Prep الواردة في هذا الدرس.
مراجعة الدرس
تعلّمت في هذا الدرس أن: المصفوفات تتيح الوصول العشوائي في O(1)، لكنها تتطلب O(n) للإدراج والحذف من الوسط — ومعرفة هذا الاختلاف توجه اختيار الخوارزمية، وأن نمط مؤشر القراءة والكتابة يزيل العناصر أو ينقل القيم داخل المصفوفة نفسها في زمن O(n) وبمساحة O(1)، وأن ترميز بت الإشارة وحيل استخدام الفهرس كعلامة يتيحان حلولًا بمساحة O(1) لمسائل كانت ستتطلب مصفوفة مساعدة لولا ذلك. بعد ذلك سنستكشف المجاميع السابقة والمجاميع التراكمية.
الأسئلة الشائعة
هل درس «أساسيات المصفوفات والعمليات في مكانها» مجاني؟
نعم — نص درس «أساسيات المصفوفات والعمليات في مكانها» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Coding Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.
ماذا ستتعلم في «أساسيات المصفوفات والعمليات في مكانها»؟
راجِع الفهرسة والتعديل وأكثر أخطاء مقابلات المصفوفات شيوعًا، مثل أخطاء تجاوز الحدود وتعديل قائمة أثناء التكرار تتمرن على Coding Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Coding Interview Prep؟
لا تُشترط خبرة سابقة. Coding Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «أساسيات المصفوفات والعمليات في مكانها»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Coding Interview Prep هذا؟
نعم. كل درس في Coding Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- أساسيات المصفوفات والعمليات في مكانها
- المجاميع البادئة والإجماليات التراكمية
- مؤشران: الطرفان المتقابلان
- مؤشران: البطيء والسريع