0Pricing
DSA Interview Prep · درس

مقابلة تجريبية محددة بوقت: مسائل سهلة ومتوسطة

حلّ ثلاث مسائل خلال 45 دقيقة، واشرح تفكيرك بصوت مسموع كما تفعل في مقابلة حقيقية، ثم راجِع الحلول المثلى بعد ذلك.

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

كيفية استخدام هذه المقابلة التجريبية

يحاكي هذا الدرس جلسة مقابلة برمجية فعلية. لكل مشكلة، ينبغي أن: (1) تقرأها مرة واحدة، (2) تحدد النمط خلال 60 ثانية، (3) تذكر نهجك وتعقيده، (4) تكتب الحل، و(5) تختبره باستخدام أمثلة. اضبط مؤقتًا. ينبغي أن تستغرق المشكلة السهلة من 10 إلى 15 دقيقة، والمشكلة المتوسطة من 20 إلى 25 دقيقة.

لا تنظر إلى الحل مسبقًا، فهذا يُفقد التدريب هدفه. إذا واجهتك مشكلة بعد 5 دقائق، فأعد قراءة نص المشكلة وابحث عن الكلمة التي تكشف النمط (مرتبة؟ الحد الأدنى؟ جميع التركيبات؟ المصفوفة الفرعية؟). فالقدرة على إخراج نفسك من حالة التعثر لا تقل أهمية عن القدرة على الحل بسرعة.

# Mock interview timer simulation
import time

class InterviewTimer:
    def __init__(self, total_minutes):
        self.total = total_minutes * 60
        self.start = None

    def begin(self, problem_name):
        self.start = time.time()
        print(f'TIMER STARTED: {problem_name}')
        print(f'You have {self.total//60} minutes. Go!')

    def checkpoint(self, label):
        if self.start:
            elapsed = time.time() - self.start
            remaining = self.total - elapsed
            print(f'[{label}] Elapsed: {elapsed:.0f}s, Remaining: {remaining:.0f}s')

# Usage in real practice:
timer = InterviewTimer(15)  # 15-minute easy problem
timer.begin('Two Sum')
time.sleep(1)
timer.checkpoint('Identified pattern')

المشكلة السهلة 1: الأقواس الصحيحة

المشكلة: معطاة سلسلة نصية تحتوي فقط على '(' و')' و'{' و'}' و'[' و']'، حدّد ما إذا كانت سلسلة الإدخال صحيحة. تكون السلسلة صحيحة إذا أُغلق كل قوس مفتوح بقوس من النوع نفسه وبالترتيب الصحيح.

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

def is_valid(s):
    stack = []
    matching = {')': '(', '}': '{', ']': '['}

    for char in s:
        if char in '({[':
            stack.append(char)
        else:
            if not stack or stack[-1] != matching[char]:
                return False
            stack.pop()
    return len(stack) == 0

# Test cases
test_cases = [
    ('()', True),
    ('()[]{}'  , True),
    ('(]', False),
    ('([)]', False),
    ('{[]}', True),
    ('', True),        # empty string is valid
    ('(((', False),    # unmatched opens
    (')]', False),     # close without open
]
for s, expected in test_cases:
    result = is_valid(s)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: is_valid({repr(s)}) = {result} (expected {expected})')

المشكلة السهلة 2: أفضل وقت لشراء الأسهم وبيعها

المشكلة: معطاة مصفوفة prices حيث تمثل prices[i] سعر السهم في اليوم i، أوجد أكبر ربح من عملية شراء وعملية بيع واحدة (ويجب أن يسبق الشراءُ البيعَ). أعد القيمة 0 إذا لم يكن تحقيق ربح ممكنًا.

الإشارة: أكبر فرق مع ضرورة أن يسبق الموضع الأيسر الموضع الأيمن → تتبّع الحد الأدنى المتراكم أثناء المسح من اليسار إلى اليمين. في كل يوم، يكون الربح المحتمل هو current_price - min_so_far. حدّث أكبر ربح. التعقيد O(n)/O(1)، وهذه حالة خاصة من خوارزمية Kadane.

def max_profit(prices):
    if not prices:
        return 0
    min_price = float('inf')
    max_profit = 0

    for price in prices:
        if price < min_price:
            min_price = price
        elif price - min_price > max_profit:
            max_profit = price - min_price
    return max_profit

# Test cases
test_cases = [
    ([7, 1, 5, 3, 6, 4], 5),   # buy at 1, sell at 6
    ([7, 6, 4, 3, 1], 0),      # monotonically decreasing: no profit
    ([2, 4, 1], 2),             # buy at 2, sell at 4
    ([1], 0),                   # single price: no transaction possible
    ([3, 3, 3], 0),             # flat: no profit
]
for prices, expected in test_cases:
    result = max_profit(prices)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: max_profit({prices}) = {result} (expected {expected})')

المشكلة المتوسطة 1: مجموع ثلاثة عناصر

المشكلة: معطاة مصفوفة، أوجد جميع الثلاثيات الفريدة التي يساوي مجموعها صفرًا. يجب ألا يحتوي الحل على ثلاثيات مكررة.

النمط: توسيع استخدام المؤشّرين ليشمل ثلاثة عناصر. رتّب المصفوفة. لكل عنصر nums[i]، استخدم المؤشّرين left = i+1 وright = n-1 للعثور على أزواج مجموعها يساوي -nums[i]. تخطَّ التكرارات بالانتقال إلى ما بعد القيم المتطابقة. الزمن O(n²)، والمساحة O(1) باستثناء مساحة الإخراج. يجعل الفرز التعامل مع التكرارات واضحًا وسهلًا.

def three_sum(nums):
    nums.sort()
    result = []
    n = len(nums)

    for i in range(n - 2):
        # Skip duplicate values for the first element
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        left, right = i + 1, n - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total == 0:
                result.append([nums[i], nums[left], nums[right]])
                while left < right and nums[left] == nums[left + 1]:
                    left += 1      # skip duplicate lefts
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1     # skip duplicate rights
                left += 1; right -= 1
            elif total < 0:
                left += 1
            else:
                right -= 1
    return result

print(three_sum([-1, 0, 1, 2, -1, -4]))  # [[-1,-1,2],[-1,0,1]]
print(three_sum([0, 0, 0, 0]))            # [[0,0,0]]
print(three_sum([]))                       # []
print(three_sum([1, 2, -2, -1]))           # []

المسألة المتوسطة 2: أطول سلسلة فرعية دون أحرف مكررة

المسألة: بالنظر إلى سلسلة نصية، أوجد طول أطول سلسلة فرعية لا تحتوي على أحرف مكررة.

النمط: نافذة منزلقة باستخدام مجموعة (أو قاموس لآخر مواضع الظهور). حافظ على نافذة [left, right]. وسّع right بإدراج كل حرف. إذا تكرر حرف (أي كان موجودًا في النافذة)، فقلّص النافذة من اليسار حتى إزالة الحرف المكرر. تتبّع أكبر حجم للنافذة. الزمن O(n)، والمساحة O(min(n, alphabet_size)).

def length_of_longest_substring(s):
    char_index = {}    # character -> last seen index
    left = 0
    max_len = 0

    for right, char in enumerate(s):
        if char in char_index and char_index[char] >= left:
            left = char_index[char] + 1  # shrink window past duplicate
        char_index[char] = right
        max_len = max(max_len, right - left + 1)
    return max_len

# Test cases
test_cases = [
    ('abcabcbb', 3),   # 'abc'
    ('bbbbb', 1),       # 'b'
    ('pwwkew', 3),      # 'wke'
    ('', 0),            # empty string
    ('au', 2),          # full string
    ('dvdf', 3),        # 'vdf' (skip the first d)
]
for s, expected in test_cases:
    result = length_of_longest_substring(s)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: len_longest({repr(s)}) = {result} (expected {expected})')

المسألة المتوسطة 3: تبديل العملات

المسألة: بالنظر إلى فئات من العملات ومبلغ مستهدف، أوجد الحد الأدنى لعدد العملات اللازمة لبلوغ المبلغ. أعد -1 إذا كان ذلك مستحيلًا.

النمط: البرمجة الديناميكية أحادية البعد (نسخة من مسألة حقيبة الظهر غير المحدودة). dp[i] = الحد الأدنى لعدد العملات اللازمة للمبلغ i. هيّئ dp[0] = 0، واجعل قيمة جميع العناصر الأخرى ما لا نهاية. لكل مبلغ من 1 إلى target، جرّب جميع فئات العملات. طبّق dp[i] = min(dp[i], dp[i - coin] + 1) لكل عملة صالحة. الزمن O(amount × len(coins))، والمساحة O(amount).

def coin_change(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0   # 0 coins to make amount 0

    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i and dp[i - coin] + 1 < dp[i]:
                dp[i] = dp[i - coin] + 1

    return dp[amount] if dp[amount] != float('inf') else -1

# Test cases
test_cases = [
    ([1, 5, 11], 15, 3),      # 11+1+1+1+1... wait: 11+1+1+1+1=5 coins? No: 5+5+5=3
    ([2], 3, -1),              # impossible (only even coins)
    ([1], 0, 0),               # 0 coins for amount 0
    ([1, 2, 5], 11, 3),        # 5+5+1
    ([186, 419, 83, 408], 6249, 20),  # stress test
]
for coins, amount, expected in test_cases:
    result = coin_change(coins, amount)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: coin_change({coins}, {amount}) = {result} (expected {expected})')

سير عمل حل المسائل تحت ضغط الوقت

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

إذا أدركت أن حلك بتعقيد O(n²) خاطئ، فلا تتخلَّ عنه في منتصف الطريق — أتممه، واختبره، ثم اعرض تحسينه إذا تبقى وقت. فالحل الأمثل المكتوب جزئيًا يحقق تقديرًا أقل من حل كامل لكنه دون المستوى الأمثل.

# Priority order when time runs out
priority = [
    ('First priority',  'Correct brute-force that passes all test cases'),
    ('Second priority', 'Optimal solution with bugs is WORSE than suboptimal correct'),
    ('Third priority',  'Edge cases handled visibly (empty input, single element, negatives)'),
    ('Fourth priority', 'Clean variable names and readable code'),
    ('Fifth priority',  'Add complexity statement as a comment at the top'),
]
print('Under time pressure, prioritise:')
for priority_level, desc in priority:
    print(f'  {priority_level}: {desc}')

# Adding complexity as a comment
def two_sum_commented(nums, target):
    # Time: O(n), Space: O(n)
    seen = {}
    for i, n in enumerate(nums):
        complement = target - n
        if complement in seen:
            return [seen[complement], i]
        seen[n] = i
    return []

مراجعة حلك: خمسة أسئلة

قبل أن تقول «انتهيت»، اطرح على نفسك هذه الأسئلة الخمسة:

  1. هل يتعامل مع الإدخال الفارغ؟ []، ''، None، n=0
  2. هل يتعامل مع عنصر واحد؟ مصفوفات بحجم 1، وأشجار تحتوي على عقدة واحدة
  3. هل يتعامل مع العناصر المتطابقة جميعًا؟ [5, 5, 5, 5]، 'aaaa'
  4. هل يتعامل مع القيم الدنيا والعظمى؟ أعداد سالبة، وأعداد صحيحة كبيرة جدًا، و0
  5. هل ذكرت التعقيد الزمني وتعقيد المساحة؟ Big-O مع تبرير موجز

تكشف هذه الفحوصات الخمسة غالبية الأخطاء في حلول المقابلات. يتوقع المُحاوِرون من المرشحين اختبار حلولهم بأنفسهم — ولن يخبروك بوجود خطأ في حلك إلا إذا طلبت ملاحظاتهم.

# The five edge-case categories with examples
edge_cases = {
    'Empty input':     ['[] empty array', '"" empty string', 'None / null'],
    'Single element':  ['[42]', 'single node tree', 'n=1'],
    'All same':        ['[3,3,3,3]', '"aaaa"', 'uniform grid'],
    'Extreme values':  ['[-10^9, 10^9]', 'INT_MAX + 1 overflow check', '0 as input'],
    'Already sorted':  ['ascending + descending', 'already optimal input'],
}
for category, examples in edge_cases.items():
    print(f'{category}:')
    for ex in examples:
        print(f'  - {ex}')
    print()

# Template for self-testing:
def test_my_solution(fn, test_cases):
    for inputs, expected in test_cases:
        result = fn(*inputs) if isinstance(inputs, tuple) else fn(inputs)
        status = 'PASS' if result == expected else 'FAIL'
        print(f'{status}: {inputs} => {result} (expected {expected})')

التعامل مع أسئلة المتابعة

بعد أن تحل المسألة، يطرح المُحاوِرون عادةً أسئلة متابعة. ومن أنواعها الشائعة:

  • «هل يمكنك حلها باستخدام مساحة O(1)؟» → ابحث عن تعديل داخل المصفوفة أو حيل رياضية
  • «ماذا لو كانت قيمة n كبيرة جدًا؟» → ناقش أساليب المعالجة المتدفقة أو تقسيم الصفحات أو أخذ العينات
  • «ماذا لو كانت المصفوفة مرتبة مسبقًا؟» → غالبًا ما توجد خوارزمية أبسط
  • «هل يمكنك تنفيذ ذلك بالتوازي؟» → حدّد المسائل الفرعية المستقلة وناقش MapReduce أو التوازي على مستوى المهام

تختبر أسئلة المتابعة عمق معرفتك وقدرتك على التكيف. قل «دعني أفكر للحظة» بدلًا من التخمين فورًا. فالتوقف المدروس أفضل من إجابة خاطئة تُقدَّم بثقة.

# Follow-up answers for classic problems
follow_ups = [
    {
        'problem': 'Find duplicate in array 1..n (space O(n) solution uses set)',
        'follow_up': 'Can you do it in O(1) space without modifying input?',
        'answer': 'Floyd cycle detection: treat array as linked list (slow/fast pointer)',
    },
    {
        'problem': 'Reverse a string (space O(n) with new array)',
        'follow_up': 'Can you do it in-place?',
        'answer': 'Two pointers from both ends, swap until they meet: O(n) time O(1) space',
    },
    {
        'problem': 'Find max in array: O(n) single pass',
        'follow_up': 'What if the array is streamed one element at a time?',
        'answer': 'Same algorithm works! Running maximum handles infinite streams',
    },
    {
        'problem': 'Merge sorted arrays O(n+m)',
        'follow_up': 'What if you have K sorted arrays?',
        'answer': 'Use a min-heap of (value, array_idx, element_idx): O(n log k)',
    },
]
for fu in follow_ups:
    print(f'Problem: {fu["problem"]}')
    print(f'Follow-up: {fu["follow_up"]}')
    print(f'Answer: {fu["answer"]}\n')

مسألة تدريبية: تجميع الكلمات المتناظرة

المسألة: بالنظر إلى مصفوفة من السلاسل النصية، اجمع معًا الكلمات التي تتكون من الأحرف نفسها بترتيب مختلف. أعد قائمة بالمجموعات.

النمط: خريطة تكرار الأحرف كمفتاح. لكل سلسلة، رتّب أحرفها (أو احسب مجموعة تكرارات الأحرف) لاستخدامها مفتاحًا معياريًا. اجمع السلاسل وفق هذا المفتاح باستخدام خريطة تجزئة للقوائم. الزمن O(n × m log m)، حيث m هو أقصى طول للسلسلة، والمساحة O(n × m). لا حاجة إلى حلقات متداخلة — يكفي مرور واحد على المصفوفة.

from collections import defaultdict

def group_anagrams(strs):
    # Method 1: sort each string as key
    groups = defaultdict(list)
    for s in strs:
        key = ''.join(sorted(s))   # canonical form
        groups[key].append(s)
    return list(groups.values())

def group_anagrams_v2(strs):
    # Method 2: character count tuple as key (avoids sorting)
    groups = defaultdict(list)
    for s in strs:
        count = [0] * 26
        for c in s:
            count[ord(c) - ord('a')] += 1
        key = tuple(count)   # immutable, hashable
        groups[key].append(s)
    return list(groups.values())

test = ['eat', 'tea', 'tan', 'ate', 'nat', 'bat']
result = [sorted(g) for g in group_anagrams(test)]
result.sort()
print('Groups:', result)
# [['ate','eat','tea'], ['bat'], ['nat','tan']]

print('V2:', [sorted(g) for g in sorted(group_anagrams_v2(test), key=len)])

التقييم الذاتي بعد مقابلة تجريبية

بعد كل مقابلة تجريبية، قيّم نفسك وفق الأبعاد التالية:

  • سرعة التعرّف على النمط: هل حددت النمط في <60 ثانية؟
  • صحة الشيفرة: هل اجتاز حلك الأول جميع حالات الاختبار؟
  • التعامل مع الحالات الحدية: هل اختبرت المدخلات الفارغة والمفردة والقصوى؟
  • التواصل: هل شرحت منهج تفكيرك طوال الوقت؟
  • الوعي بالتعقيد: هل ذكرت التعقيد الزمني وتعقيد المساحة؟
  • التعافي: عندما علقت، هل غيّرت منهجك بسلاسة أم تجمّدت؟

قيّم نفسك من 1 إلى 5 في كل بُعد. ركّز تدريبك في الأسبوع التالي على البُعد الذي حصل على أدنى تقييم. يحتاج معظم المرشحين إلى تحسين التعرّف على الأنماط أو التواصل — ونادرًا ما يحتاجون إلى تحسين كليهما.

# Self-assessment scoring template
def self_assess(pattern_speed, code_correctness, edge_cases,
                communication, complexity, recovery):
    scores = {
        'Pattern recognition (< 60s)': pattern_speed,
        'Code correctness (all tests pass)': code_correctness,
        'Edge case handling': edge_cases,
        'Communication (thinking aloud)': communication,
        'Complexity stated correctly': complexity,
        'Recovery when stuck': recovery,
    }
    total = sum(scores.values())
    max_total = len(scores) * 5
    print('Self-Assessment Results:')
    print('-'*50)
    for dim, score in scores.items():
        bar = '#' * score + '-' * (5 - score)
        print(f'{dim:45s} [{bar}] {score}/5')
    print(f'\nTotal: {total}/{max_total} ({total/max_total*100:.0f}%)')
    weak = min(scores, key=scores.get)
    print(f'Focus area: {weak}')

self_assess(4, 3, 4, 3, 5, 2)  # example scores

اختبار سريع

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

مراجعة الدرس

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

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

هل درس «مقابلة تجريبية محددة بوقت: مسائل سهلة ومتوسطة» مجاني؟

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

ماذا ستتعلم في «مقابلة تجريبية محددة بوقت: مسائل سهلة ومتوسطة»؟

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

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

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

كم من الوقت يستغرق درس «مقابلة تجريبية محددة بوقت: مسائل سهلة ومتوسطة»؟

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

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

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

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

  1. ورقة غش للتعرّف على الأنماط
  2. مقابلة تجريبية محددة بوقت: مسائل سهلة ومتوسطة
  3. التعامل مع الحالات الحدّية والتواصل في المقابلة
  4. شرح مسائل صعبة: Word Ladder II وAlien Dictionary
← العودة إلى DSA Interview Prep