محتويات الدورة

هياكل البيانات والخوارزميات / البنى المرتبة والاستدلال على الرسوم / الدرس 7

الفرز والاستقرار وثوابت الدمج

احفظ تبديلا مرتبا لعناصر المدخل، وحدّد سلوك المفاتيح المتساوية، واحسب كلفتي التهيئة والنسخ.

وقت القراءة التقريبي: 5 دقيقةتاريخ مراجعة المحتوى
في هذه الصفحة

أهداف الدرس

  • يجب أن يحفظ الناتج المرتب تعدد عناصر المدخل أيضا
  • ادمج سابقتين مرتبتين باختيار المرشح التالي
  • اشتق الزمن وأقصى التخزين من التنفيذ الفعلي

يجب أن يحفظ الناتج المرتب تعدد عناصر المدخل أيضا

يتطلب الفرز خاصيتين: ظهور المفاتيح بالترتيب المطلوب، وظهور كل نسخة من عناصر المدخل في الناتج مرة واحدة بالضبط. قد تعيد دالة قائمة فارغة مرتبة، لكنها لا تكون فرزا صحيحا لمدخل غير فارغ. حدّد هل يعدّل الفرز التسلسل الأصلي، وهل تحتفظ السجلات المتساوية في المفتاح بترتيبها النسبي الأصلي. تسمى الخاصية الأخيرة الاستقرار، وتهم عندما تكون السجلات مرتبة مسبقا بمعيار آخر، أو عندما يحسم ترتيب الوصول حالات التعادل. لا يعني الاستقرار أن المدخلات المختلفة تنتج دائما الترتيب نفسه؛ بل يعني حفظ الترتيب المقدم بين العناصر المتساوية في المفتاح.

ادمج سابقتين مرتبتين باختيار المرشح التالي

أثناء الدمج يكون الناتج السابقة المرتبة الصحيحة لجميع النسخ التي عولجت، بينما يشير مؤشر كل مدخل إلى أول نسخة فيه لم تُعالج بعد. يجب أن يكون أصغر مفتاح متبق عند أحد هذين المؤشرين، لأن كلا المدخلين مرتب بالفعل. ويحفظ الأخذ من اليسار عند المساواة ترتيب الوصول بين النصفين. يطبق البرنامج الأول هذه القاعدة عوديا بعد تقسيم المدخل. وتعید حالة الأساس نسخة تحوي صفرا أو عنصرا واحدا. يستخدم هذا التنفيذ شرائح للتوضيح؛ لذلك يشمل التحليل كلفة نسخها، ولا يصف الفرز بأنه يجري في موضعه دون مساحة إضافية.

python مثال مستقل لخوارزمية بلغة Python 3، يستخدم بيانات محلية مصطنعة.
def merge_sort(items, key=lambda item: item):
    if len(items) < 2:
        return list(items)
    middle = len(items) // 2
    left = merge_sort(items[:middle], key)
    right = merge_sort(items[middle:], key)
    i = j = 0
    output = []
    while i < len(left) and j < len(right):
        if key(left[i]) <= key(right[j]):
            output.append(left[i])
            i += 1
        else:
            output.append(right[j])
            j += 1
    output.extend(left[i:])
    output.extend(right[j:])
    return output

records = [(2, "A"), (1, "B"), (2, "C"), (1, "D")]
print(merge_sort(records, key=lambda record: record[0]))
print(records)
الناتج المتوقع
[(1, 'B'), (1, 'D'), (2, 'A'), (2, 'C')]
[(2, 'A'), (1, 'B'), (2, 'C'), (1, 'D')]

تبقى القائمة الأصلية دون تغيير، وتسبق B نظيرتها D، بينما تسبق A نظيرتها C داخل مجموعتي المفاتيح المتساوية.

اشتق الزمن وأقصى التخزين من التنفيذ الفعلي

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

استخدم الفرز القياسي في عمل التطبيقات

يفرز البرنامج الثاني بمفتاح مركب: الدرجة أولا ثم الاسم. يوضح المفتاح المركب الترتيب الكامل، ويظل الاستقرار يحفظ ترتيب المدخل عند تساوي الحقلين معا. تنشئ sorted في Python قائمة جديدة، بينما تعدّل list.sort القائمة وتعيد None. لا تسند القيمة المعادة من list.sort على أنها البيانات المرتبة. وعند الفرز بمرورات مستقرة متعددة، افرز الحقل الأقل أهمية أولا، ثم الأكثر أهمية أخيرا. اختر عقدا صريحا يستطيع المراجع فهمه، وافصل الترتيب اللغوي المراعي للإعداد المحلي عن مقارنة النصوص العادية بنقاط الترميز عندما يهم ترتيب اللغة الظاهر للمستخدم.

python مثال مستقل لخوارزمية بلغة Python 3، يستخدم بيانات محلية مصطنعة.
records = [
    {"name": "Rivers", "score": 2},
    {"name": "Maps", "score": 1},
    {"name": "Atlas", "score": 2},
]
ordered = sorted(records, key=lambda record: (record["score"], record["name"]))
print([record["name"] for record in ordered])
print([record["name"] for record in records])
الناتج المتوقع
['Maps', 'Atlas', 'Rivers']
['Rivers', 'Maps', 'Atlas']

يكسر المفتاح المركب تعادل الدرجات بترتيب الأسماء وفقا لترتيب نصوص Python العادي؛ ولا يُدّعى تطبيق ترتيب لغوي خاص بإعداد محلي.

يجب أن تكون علاقة الترتيب متسقة قبل اعتماد الخوارزمية عليها

يجب أن تحدد دالة المقارنة أو سياسة المفتاح سلوكا لكل مدخل مقبول. وقد تخالف الأنواع المختلطة غير القابلة للمقارنة، والمقارنات المخصصة غير المتسقة، والقيم العائمة NaN افتراضات برهان الترتيب البسيط. طبّع القيم غير المدعومة أو ارفضها عند حدود الإدخال وفقا للمجال. لا تخف الأخطاء بتحويل كل مفتاح إلى نص، لأن القيمتين العدديتين 10 و2 تصبح لهما علاقة ترتيب مختلفة. لا يستطيع الفرز المستقر إصلاح علاقة ترتيب غير صالحة. وأدرج حجم الناتج في عقد النتيجة أيضا: فتجسيد قائمة مرتبة يحتاج إلى مساحة تخزين حتى لو كان المدخل مكررا لا قائمة.

اختبر حفظ العناصر والترتيب والاستقرار والملكية

قارن الفرز المخصص بـ sorted على مصفوفات صغيرة تحوي قيما سالبة ومكررة. افحص تعدد العناصر إلى جانب رتابة الترتيب، واستخدم سجلات ذات تسميات مختلفة ومفاتيح متساوية لكشف معالجة التعادل غير المستقرة. تحقّق من بقاء القائمة الأصلية دون تعديل وفقا لعقد هذه الدالة. يختبر الفراغ والعنصر الواحد حالة الأساس، بينما تختبر البيانات المرتبة عكسيا عمليات الدمج في أنحاء الشجرة. لا تحل مجموعة فحوص تفاضلية محدودة محل ثابت الدمج وبرهان تقلص المسائل الفرعية. وافصل اختبارات الصحة من قياس الأداء؛ فلا تستطيع أمثلة قليلة صغيرة إثبات تفوق زمن التنفيذ مقارنة بتنفيذ آخر.

الأخطاء الشائعة وإصلاحها

تبادل السجلات المتساوية في المفتاح ترتيبها النسبي

لماذا يحدث؟
يختار الدمج النصف الأيمن عند المساواة.
كيف تصلحه؟
اختر النسخة اليسرى عندما يجب أن يحفظ تعادل المفاتيح ترتيب الوصول.
كيف تتجنب تكراره؟
اختبر تسميات مختلفة تشترك في المفتاح نفسه.

تحول المتغير المخصص للبيانات المرتبة إلى None

لماذا يحدث؟
اعتُبرت القيمة المعادة من list.sort قائمة جديدة.
كيف تصلحه؟
استخدم sorted، أو عدّل القائمة الموجودة دون إسناد القيمة المعادة.
كيف تتجنب تكراره؟
وثّق عقد التعديل وعقد القيمة المعادة معا.

تغيير المفتاح للمعنى العددي

لماذا يحدث؟
حُولت القيم إلى نصوص لمجرد تجنب أخطاء المقارنة.
كيف تصلحه؟
تحقّق من الأنواع المقبولة، واختر مفتاحا صحيحا للمجال.
كيف تتجنب تكراره؟
أدرج القيمتين 2 و10 في عينات الترتيب.

اختبر فهمك

1. لماذا يهدد استبدال <= بالعامل < استقرار الدمج؟

اعرض الحل المشروح

عندما تتساوى المفاتيح يصبح الشرط كاذبا، فتُصدر نسخة النصف الأيمن قبل نسخة أقدم من النصف الأيسر. يظل الناتجان مرتبين عدديا، ولذلك لا تكشف الاختبارات القائمة على القيم وحدها العيب. تكشفه السجلات المسماة ذات المفاتيح المتساوية ومواضعها الأصلية.

2. هل يمكن أن يكون ناتج الفرز مرتبا، ومع ذلك يفقد الحل صحته؟

اعرض الحل المشروح

نعم. قد يترك حذف النسخ المكررة ناتجا مرتبا تماما، لكنه يحوي مجموعة متعددة العناصر مختلفة عن المدخل. افحص ترتيب المفاتيح وحفظ كل نسخة معا. وإذا كان العقد يمنع التعديل، فتحقّق أيضا من أن التسلسل الأصلي لم يتغير.

أهم ما تعلمته

  • يجب أن يحفظ الناتج المرتب تعدد عناصر المدخل أيضا
  • اختبر حفظ العناصر والترتيب والاستقرار والملكية

المراجع الرسمية