تصنيف كومة ضعيف


من بين أكوام حديقة الحيوان بأكملها ، ربما يكون هذا الهيكل هو الأكثر غرابة. في نفس الوقت ، فإن البساطة الأنيقة للخوارزمية مطابقة تمامًا لانحرافها المذهل.

عند الفرز باستخدام كومة الذاكرة المؤقتة الضعيفة ، هناك دائمًا مقارنات وتبادلات أقل من استخدام كومة الذاكرة المؤقتة العادية. لذا نعم ، كومة ضعيفة أقوى من كومة عادية.
إديسون برمجيات - تطوير الويب
تمت كتابة هذه المقالة بدعم من إديسون.

نحن منخرطون في إنشاء برامج مدمجة بالإضافة إلى تطوير تطبيقات الويب والمواقع .

نحن نحب نظرية الخوارزميات! ؛-)

كومة ضعيفة


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


يمكن أن يقلل هذا النهج بشكل كبير من تكلفة الحفاظ على مجموعة البيانات في حالة كومة. بعد كل شيء ، من الضروري التأكد من أن مبدأ "السليل ليس أكثر من أحد الوالدين" ليس للبنية بأكملها ، ولكن لنصفها فقط. في نفس الوقت ، كومة ضعيفة ، لا تكون شجرة فرز 100 ٪ ، لا تفرز أسوأ من كومة عادية ، وفي بعض النواحي أفضل. فعلت نصف المعركة - المشي بجرأة!

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

التقليل من عدد المقارنات



قدم لنا رونالد داتون ، المتخصص في الخوارزميات ونظرية الرسم البياني ، مجموعة ضعيفة في عام 1993. يصعب فهم الكومة الضعيفة من الناحية المفاهيمية (ولكن هذه الصعوبة على الأرجح ليست في التعقيد ، ولكن في البذخ ، عليك كسر أنماط وعيك من خلال الركبة) من الكومة العادية ، لذلك لم تحصل على الكثير من التوزيع العملي. ومع ذلك ، عندما اخترع داتون هذا الهيكل ، لم يكن يريد ممارسة التجريد المجرد فحسب ، بل سعى إلى تحقيق هدف عملي تمامًا.

هناك حد أدنى نظريًا لتقدير الحد الأدنى لعدد المقارنات (في تلك الفرز التي تستخدم فيها هذه المقارنات على نطاق واسع):

log n ! = n تسجيل n - n / ln 2 + O (تسجيل n) ، حيث 1 / ln 2 = 1.4426

في الفرز بواسطة كومة ضعيفة ، يتم تقليل عدد المقارنات إلى الحد الأدنى ويقترب بدرجة كافية من الحد الأدنى.

يمكن أن يكون هذا ذا أهمية عملية إذا كنت بحاجة إلى ترتيب الكائنات التي تكون مقارنتها باهظة الثمن ، على سبيل المثال ، إذا كان الأمر يتعلق بفرز سلاسل طويلة.

أحفاد شعوذة


"اليسار" و "اليمين" في كومة ضعيفة هي ظاهرة ظرفية. يمكن أن تكون الشجرة الفرعية إما سلالة يسارية أو يمنى للعقدة الأصلية - علاوة على ذلك ، يمكن لهذه العلاقة "اليسرى / اليمنى" للشجرة الفرعية أن ينتقل الوالد بشكل متكرر من قيمة واحدة إلى العكس أثناء العملية.

للإشارة إلى الوالد الذي لديه ابنه الأيمن والذي هو ابنته اليسرى ، ليس بسيطًا ، ولكنه بسيط جدًا. للقيام بذلك ، تحتاج إلى صورة نقطية إضافية (تتكون من قيم 0/1 فقط) لتلك العقد التي لها أحفاد.

تذكر كيف أن العنصر الرئيسي i- th الفهرس ، نحدد مؤشرات السليل الأيسر والأيمن في كومة تقليدية (مؤشرات الصفيف تقاس من الصفر):

السليل الأيسر 2 × i + 1
السليل الأيمن 2 × i + 2

في كومة ضعيفة ، لدينا الكرز على الكعكة - الجذر الذي يحتوي فقط على الشجرة الفرعية الصحيحة ، لذلك سنقوم بتعديل هذه الصيغ لمؤشرات السليل عن طريق إضافة التحول العكسي إلى الموضع 1:

اليسار المنحدر: 2 × i
السليل الأيمن: 2 × i + 1

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

المتحدر الأيسر: 2 × i + BIT [ i ]
السليل الأيمن: 2 × i + 1 - BIT [ i ]

هكذا تبدو. يتم تمييز العناصر التي تقع أحفادها "بالعكس" باللون الأزرق. القيم في صفيف BIT لها 1.


يمكنك التحقق عن طريق استبدال القيم الأصلية i والقيمة المقابلة 0/1 من صفيف BIT في الصيغ السلالية - ستظهر مؤشرات السلالة كما يجب.

كما ترى ، لكي يتمكن أي من الوالدين من تبديل الشريحتين الفرعيتين اليسرى واليمنى ، في الصفيف نفسه ، لا يلزم نقل مجموعة العناصر إلى أي مكان. يتم فقط تبديل قيمة 0/1 للوالد في صفيف BIT وهذا كل ما في الأمر.

التالي - جلسة سحرية مع تعرضها اللاحق.

بناء كومة ضعيفة


إن تبديل المنحدرات اليسرى واليمنى هو الأداة الرئيسية لتحويل كومة ضعيفة إلى مجموعة من البيانات من مصفوفة.

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

إذا كان العنصر هو السليل الصحيح لشخص ما ، فليس عليك الذهاب بعيدًا. الوالد المباشر هو ما تحتاجه:


إذا كان العنصر سليلًا لشخص ما ، فأنت بحاجة إلى الصعود إلى عدد من المستويات قبل مقابلة الجد المطلوب ، والذي يكون العنصر في الشجرة الفرعية اليمنى:



ثم تحتاج إلى مقارنة السليل والسلف الموجود في مكان ما أعلاه. وإذا كان السليل أكبر من السلف ، فيجب القيام بما يلي:

  1. إذا كان لدى السليل نسله ، فقم بتبديل الشجرة الفرعية اليسرى واليمنى (أي التبديل 0/1 في صفيف BIT لهذا العنصر).
  2. تبادل قيم عقدة السليل والعقدة السلف.

نلقي نظرة على مثال محدد. لنفترض أن الموقف التالي نشأ:


بالنسبة لعنصر المصفوفة A [6] = 87 ، تم العثور على السلف A [1] = 76.
السلف A [1] أصغر من العنصر A [6] (76 <87).
يحتوي العنصر A [6] على شجرتين فرعيتين إلى اليسار واليمين (معلمين بظلال باللون الأخضر).
تحتاج إلى تبديل هذه الشجرات الفرعية
(أي للعنصر A [6] في صفيف BIT ، قم بتغيير القيمة من 0 إلى 1).
من الضروري أيضًا تبادل قيم العنصرين A [6] و A [1].


بعد الانتهاء من الإجراءات اللازمة:


بالنسبة للعنصر A [6] ، تم تبادل الشجرتين الفرعيتين اليسرى واليمنى
(أي في صفيف BIT للعنصر A [6] ، تم تغيير القيمة من 0 إلى 1).
كان هناك أيضًا تبادل للقيم بين A [6] و A [1].


إذا ذهبت من خلال الصفيف من النهاية إلى البداية وطوال تنفيذ هذا الإجراء لجميع العناصر ، فسوف ينتهي بك الأمر إلى كومة ضعف.

سبب عمل هذه الآلية الغريبة هو تفسير أقرب إلى نهاية المقالة.

تحليل كومة ضعيفة


كومة كومة إذا كان العنصر الأقصى في الجذر. باستخدام هذه الحقيقة ، تعمل جميع عمليات فرز الكومة بالطريقة نفسها. يتبادل الجذر (حيث يوجد الحد الأقصى) القيم مع العنصر الأخير من الجزء غير المصنف من الصفيف - ونتيجة لذلك ، ينخفض ​​الجزء غير المصنف من الصفيف ، ويزداد الجزء المصنف من الصفيف. بعد هذا التبادل ، يتوقف كومة الذاكرة المؤقتة عن كومة كومة ، حيث لم يعد العنصر الأقصى الحالي في جذره. يجب استعادة كومة الذاكرة المؤقتة ، أي جعل الشجرة الناتجة كومة كومة مرة أخرى - ابحث عن عنصر أقصى آخر وانقله إلى الجذر.

كيف نستعيد كومة ثنائية منتظمة ، نعلم - بمساعدة المغربل . ولكن كيف يمكن استعادة كومة ضعيفة؟ للقيام بذلك ، قم بما يلي.

من الجذر ننزل إلى أسفل الأحفاد اليسرى (إلى اليمين حتى الأدنى):


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

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

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

ترتيب كومة ضعيف :: ترتيب كومة ضعيف


لذا ، الخوارزمية النهائية:

  • I. :
    • I.1. -.
    • I.2. «» .
    • I.3. .
    • I.4. , :
      • I.4.. ( ⇔ ) , .
      • I.4.. «» .
  • II. , :
    • II.1. .
    • II.2. . .
    • II.3. , . :
      • II.3.. .
      • II.3.. , .
      • II.3.. , , :
        • II.3.c. 1. نقوم بتبديل الشجرة الفرعية (اليسرى - اليمنى) مع المتحدرين للعقدة التي يوجد فيها السليل الأيسر الحالي.
        • II.3.c.2. نغير جذر الكومة والعقدة مع الطفل الأيسر الحالي.
    • II.4. في جذر الكومة الضعيفة هو مرة أخرى العنصر الأقصى للجزء المتبقي غير المصنف من الصفيف. نعود إلى الفقرة II.1 ونكرر العملية حتى يتم فرز جميع العناصر.


الرسوم المتحركة (تبدأ مؤشرات الصفيف في الرسوم المتحركة بواحد):



كود C ++


في الجزء السفلي من قسم "الروابط" ، يمكن للمهتمين التعرف على تنفيذ هذا التصنيف في C ++. هنا أعطي فقط الجزء الذي يوضح الخوارزمية نفسها.

#define GETFLAG(r, x) ((r[(x) >> 3] >> ((x) & 7)) & 1)
#define TOGGLEFLAG(r, x) (r[(x) >> 3] ^= 1 << ((x) & 7))

void WeakHeap::WeakHeapMerge(unsigned char *r, int i, int j) {
  if (wheap[i] < wheap[j]) {//""  ?
    //  ,   
    //( "",   "")
    TOGGLEFLAG(r, j);
    //  ""  
    swap(wheap[i], wheap[j]);
  }
}

void WeakHeap::WeakHeapSort() {
  int n = Size();
  if(n > 1) {
		
    int i, j, x, y, Gparent;
    int s = (n + 7) / 8;
    unsigned char * r = new unsigned char [s];
		
    //  ,    
    // "",   ""
    for(i = 0; i < n / 8; ++i) r[i] = 0;
		
    //   
    for(i = n - 1; i > 0; --i) {
      j = i;
      //    , 
      //   ""  
      while ((j & 1) == GETFLAG(r, j >> 1)) j >>= 1;
      //       ""  
      Gparent = j >> 1;
      //  ,   
      //   ""
      WeakHeapMerge(r, Gparent, i);
    }
		
    //      -->
    //  -->    
    for(i = n - 1; i >= 2; --i) {
      //      
      //       
      swap(wheap[0], wheap[i]);
      x = 1;
      //    "" 
      while((y = 2 * x + GETFLAG(r, x)) < i) x = y;
      //  ""     
      //        
      while(x > 0) {
        WeakHeapMerge(r, 0, x);
        x >>= 1;
      }
    }
    //  -   
    //    
    swap(wheap[0], wheap[1]);
    delete[] r;
  }
}

أحب بشكل خاص كيف يتم اجتياز الشجرة الثنائية بسهولة وبشكل طبيعي باستخدام عمليات أحادية البت.

تعقيد الذاكرة الزائدة


يبدو أن O ( n ) - مطلوب صفيف إضافي ، حيث يتم تثبيت ترتيب الشجرة الفرعية اليسرى / اليمنى للعقد ذات السلالة (يوجد حوالي نصف تلك الموجودة في الصفيف).

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

هناك طريقة أخرى لتحويل O ( n ) إلى O (1) وهي تخزين الأعلام في عدد صحيح. التوسع الثنائي للأرقام - مجموعة من الأصفار والأعداد المسؤولة عن ترتيب الأشجار الفرعية لجميع عناصر المصفوفة. وط- عنصر عشر لليتوافق مجموعة إلى ط- قليلا التاسع من الرقم.

تعقيد الوقت


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

غربلة كاملة


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


درجة تعقيد الوقت لا تزال هي نفسها.

كومة ذات الحدين


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

لفهم هذا النوع ، تحتاج إلى فهم ما هو كومة كومة ضعيفة حقًا.

إذا أخذنا مصفوفة يكون فيها عدد العناصر قوة من اثنين ، فإن كومة الذاكرة الضعيفة وكومة كومة ذات الحدين التي تم إنشاؤها على أساسها متشابهة.


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

ومع ذلك ، فإن كومة الذاكرة الضعيفة وكومة الكومة ذات الحدين ليست 100٪ ، على الرغم من أنهما أقرب الأقرباء. الفرق واضح ، إذا أخذت مصفوفة لا يساوي عدد عناصرها 2 n . سيعطي التحلل ذو الحدين لمثل هذا الصفيف قائمة متصلة بالعديد من أكوام مثالية (عدد العقد في كل منها قوة معينة من اثنين):


ستكون كومة ضعيفة في هذه الحالة شجرة ثنائية غير كاملة:



الكومة ذات الحدين والكومة الضعيفة هما أخوان توأمان. الحمض النووي هو نفسه ، على الرغم من المظهر لا يمكنك معرفة ذلك.

خوارزمية سرية


بالنظر إلى أن الكومة الضعيفة هي كومة كائنات مشفرة ، فإن الترجمة العشوائية تعثر فجأة على تفسير بسيط.


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

في الحقيقة:

  1. لا يوجد "ضعف" ، إنها شجرة فرز كاملة (غير ثنائية) يتم فيها تحقيق مبدأ "أي والد أكبر من أي من نسله" والحفاظ عليه.
  2. في جميع المراحل ، لا نقارن النسل ليس مع أسلافهم ، ولكن مع آبائهم المباشرين.
  3. ما يشبه تبادل القيم بين السليل والسلف + تبادل الأماكن من الشجرة الفرعية في السليل - اتضح أنه تبادل النسبة نفسها (السليل / الأصل). إذا كانت العقدة الأصلية أصغر من السليل بالقيمة ، يصبح الأصل نفسه السليل ، ويصبح السليل الأصل.

فيما يلي تصورات صادقة:



في السلسلة التالية


الكومة التالية التي أود أن أتحدث عنها هي المفضلة لدي - الشجرة الديكارتية. هذه ليست مجموعة فقط ، ولكنها أيضًا شجرة بحث ثنائية . ولكن أولاً ، في المقالة التالية ، يجب توضيح شيء مثير للاهتمام حول أشجار BST. وعندها فقط ، من خلال المقالة ، وحول الحديث الديكارتي.

المراجع


الكومة الضعيفة ، الكومة ذات الحدين / الكومة ذات الحدين

C ++ تنفيذ الكومة الضعيفة

رونالد داتون: الصفحة الشخصية ، ملف تعريف موقع UCF ضعف الكومة

والأصدقاء: التطورات الأخيرة

بنية بيانات الكومة الضعيفة: المتغيرات والتطبيقات

المتعلقة بأداء

التكيف مع WEAK-HEAPSORT heapsort: شفرة المصدر

Sergey Kopeliovich - Lecture Hall - Weak pile (من 48:32 إلى 1:16:06)

مقالات سلسلة:




تمت إضافة تصنيف اليوم إلى تطبيق AlgoLab بواسطة مجموعة ضعيفة تستخدمه - قم بتحديث ملف excel باستخدام وحدات الماكرو.

في التعليقات على الخلية باسم الترتيب ، يمكنك تحديد بعض الإعدادات. إذا قمت بتعيين siftup = 1 ، فسيستخدم الفرز الفحص الكامل في المرحلة الأولى (بشكل افتراضي siftup = 0).

إذا وصفت ذات الحدين = 1 ، فستكون الشجرة عبارة عن "كومة ذات حدين" (بشكل افتراضي ذو الحدين = 0 ، أي مجرد كومة ضعيفة).

All Articles