ساختارهای داده مجموعه‌های مجزا: Union-Find با Rank و Path Compression

بسیاری الگوریتم‌ها نیاز دارند یک مجموعه پویا از مجموعه‌های مجزا را ردیابی کنند، و به‌طور مکرر مجموعه‌ها را ادغام کرده و پرس‌وجو کنند کدام مجموعه یک عنصر متعلق به آن است. این راهنمای جامع نمایش جنگل مجموعه‌مجزا، دو بهینه‌سازی حیاتی union by rank و path compression، و زمان اجرای مستهلک تقریباً-ثابتی که این بهینه‌سازی‌ها با هم دست می‌یابند، نتیجه‌ای مرکزی برای الگوریتم‌هایی مانند درخت پوشای کمینه کروسکال، را پوشش می‌دهد.

مجموعه مجزاUnion-Findفشرده‌سازی مسیر

~3 دقیقه مطالعه · آخرین به‌روزرسانی ۱۶ شهریور ۱۴۰۵

نوع داده انتزاعی مجموعه‌مجزا

یک ساختار داده Disjoint-Set (مجموعه‌مجزا) (که Union-Find نیز نامیده می‌شود) یک مجموعه از مجموعه‌های بدون‌هم‌پوشانی را نگه می‌دارد، و سه عملیات را پشتیبانی می‌کند: MAKE-SET(x) یک مجموعه جدید شامل فقط x می‌سازد، UNION(x, y) مجموعه‌های شامل x و y را در یکی ادغام می‌کند، و FIND-SET(x) یک عنصر نماینده را برمی‌گرداند که مشخص می‌کند x در حال حاضر به کدام مجموعه تعلق دارد.

نمایش مجموعه‌ها به‌عنوان درخت‌ها

پیاده‌سازی استاندارد، به نام Disjoint-Set Forest (جنگل مجموعه‌مجزا)، هر مجموعه را به‌عنوان یک درخت نمایش می‌دهد، جایی که هر گره فقط به والدش اشاره می‌کند، و ریشه درخت به‌عنوان نماینده مجموعه عمل می‌کند.

MAKE-SET(x):
  x.p = x           // x والد خودش است، یک درخت تک‌گره تشکیل می‌دهد
  x.rank = 0

FIND-SET(x):
  اگر x ≠ x.p:
      FIND-SET(x.p) را برگردان
  x را برگردان

UNION(x, y):
  LINK(FIND-SET(x), FIND-SET(y))

LINK(x, y):
  x.p = y           // y را والد x می‌کند، دو درخت را ادغام می‌کند

این نسخه ساده به‌درستی کار می‌کند، اما یک ضعف جدی دارد: اتصال مکرر درخت‌ها به ترتیب بدشانس می‌تواند یک درخت تولید کند که به یک زنجیره طولانی تنزل یابد، که FIND-SET را در بدترین‌حالت زمان Θ(n) می‌کند — همان خطر ساختار نامتوازن که درخت‌های قرمز-سیاه که پیش‌تر در این مجموعه بحث شد را انگیزه داد.

بهینه‌سازی اول: Union by Rank

Union by Rank یک کران بالای تقریبی روی ارتفاع هر درخت، به نام Rank، ردیابی می‌کند، و همیشه درخت کوتاه‌تر را زیر ریشه درخت بلندتر در طول یک union متصل می‌کند، به‌جای انتخاب دلخواه یک جهت.

LINK(x, y):
  اگر x.rank > y.rank:
      y.p = x
  در غیر این صورت:
      x.p = y
      اگر x.rank == y.rank:
          y.rank = y.rank + 1

از آنجا که درخت کوتاه‌تر همیشه زیر بلندتر متصل می‌شود، ارتفاع درخت حاصل فقط زمانی افزایش می‌یابد که دو درخت در حال ادغام رتبه برابر داشته باشند، و حتی آنگاه، فقط یک سطح. این از نظر روحی مشابه با بینش تقسیم-متوازن که پیش‌تر در این مجموعه درباره درخت‌های B و درخت‌های قرمز-سیاه بحث شد است: ترتیب اتصال دقیق از ساختار زنجیره‌ای آسیب‌شناختی که در غیر این صورت ممکن بود جلوگیری می‌کند.

با استفاده فقط از union by rank، می‌توان اثبات کرد یک درخت با رتبه r حداقل 2^r گره دارد، که یعنی حداکثر رتبه ممکن برای n عنصر O(log n) است، که هر عملیات FIND-SET را به زمان O(log n) محدود می‌کند — از قبل بهبود چشمگیری نسبت به بدترین‌حالت O(n) بهینه‌نشده.

بهینه‌سازی دوم: Path Compression

Path Compression (فشرده‌سازی مسیر) در طول خود FIND-SET اعمال می‌شود: همان‌طور که الگوریتم در درخت بالا می‌رود تا ریشه را پیدا کند، هر گره بازدیدشده در طول مسیر را طوری می‌کند که مستقیماً به ریشه اشاره کند، و درخت را برای هر پرس‌وجوی آینده شامل آن گره‌ها صاف می‌کند.

FIND-SET(x):
  اگر x ≠ x.p:
      x.p = FIND-SET(x.p)     // به‌طور بازگشتی ریشه را پیدا کن،
                                // سپس x را مستقیماً به آن متصل کن
  x.p را برگردان

پیش از FIND-SET(d)، با زنجیره a-b-c-d:
a → b → c → d(ریشه)

پس از FIND-SET(d):
a → d(ریشه)
b → d(ریشه)
c → d(ریشه)
(هر گره روی مسیر اکنون مستقیماً به ریشه اشاره می‌کند)

این بهینه‌سازی

نوشته و پژوهش‌شده توسط دکتر شاهین صیامی

مقالات مرتبط

الگوریتم‌های نظریه اعداد: GCD، توان‌رسانی پیمانه‌ای، و RSA

رمزنگاری مدرن و بی‌شمار کاربرد الگوریتمی به تعداد کمی الگوریتم ظریف نظریه اعداد متکی است. این راهنمای جامع الگوریتم اقلیدس برای محاسبه بزرگ‌ترین مقسوم‌علیه مشترک، توان‌رسانی پیمانه‌ای سریع برای محاسبه کارآمد توان‌های بزرگ، و پایه ریاضی رمزنگاری RSA، یکی از پراستفاده‌ترین سیستم‌های رمزنگاری در جهان، را پوشش می‌دهد.

ادامه

اصول هندسه محاسباتی: جهت، تقاطع پاره‌خط، و پوسته محدب

الگوریتم‌های هندسی مسائل شامل نقاط، خطوط، و اشکال را حل می‌کنند، که در گرافیک کامپیوتری، برنامه‌ریزی مسیر رباتیک، و سیستم‌های اطلاعات جغرافیایی ظاهر می‌شوند. این راهنمای جامع آزمون جهت مبتنی بر ضرب خارجی که زیربنای تقریباً هر الگوریتم هندسی است، تشخیص تقاطع پاره‌خط ساخته‌شده روی آن آزمون، و الگوریتم اسکن گراهام برای محاسبه پوسته محدب یک مجموعه از نقاط را پوشش می‌دهد.

ادامه

الگوریتم‌های تطبیق رشته: جستجوی ساده، Rabin-Karp، و فراتر از آن

جستجو برای یک الگو درون یک متن بزرگ‌تر یکی از رایج‌ترین عملیات‌ها در محاسبات است، از ویرایشگرهای متن تا تحلیل توالی DNA. این راهنمای جامع الگوریتم تطبیق رشته ساده و بدترین‌حالت درجه‌دومش را پوشش می‌دهد، سپس استفاده ماهرانه الگوریتم Rabin-Karp از هشینگ برای دستیابی به کارایی سریع حالت‌میانگین را توضیح می‌دهد، شامل نحوه مدیریت درست برخوردهای هش.

ادامه

الگوریتم‌های تقریبی: نزدیک‌شدن اثبات‌پذیر به بهینه برای مسائل سخت

وقتی یک مسئله NP-complete اثبات شود، یک راه‌حل کارآمد دقیق بعید است وجود داشته باشد، اما این به معنای رهاکردن کامل مسئله نیست. این راهنمای جامع الگوریتم‌های تقریبی را توضیح می‌دهد، که تضمین بهینه‌بودن را در ازای تضمین کارایی معامله می‌کنند، و مسائل پوشش رأس و فروشنده دوره‌گرد را به‌عنوان مثال‌های کلاسیک با نسبت‌های تقریبی اثبات‌پذیر پوشش می‌دهد.

ادامه

NP-Completeness توضیح داده شده: P، NP، و چرا برخی مسائل در برابر راه‌حل‌های کارآمد مقاومت می‌کنند

برخی مسائل برای دهه‌ها در برابر هر تلاشی برای یک الگوریتم کارآمد مقاومت کرده‌اند، با این حال هیچ‌کس اثبات نکرده یک راه‌حل کارآمد غیرممکن است. این راهنمای جامع کلاس‌های P و NP، مفهوم تقلیل‌های زمان-چندجمله‌ای مورد استفاده برای مقایسه سختی مسئله، و چگونگی اینکه اثبات NP-complete بودن یک مسئله شواهد قوی، هرچند نه اثبات، فراهم می‌کند که هیچ الگوریتم کارآمدی وجود ندارد را توضیح می‌دهد.

ادامه

جریان بیشینه: فورد-فالکرسون و قضیه برش-کمینه/جریان-بیشینه

مسائل جریان بیشینه بیشترین توان عملیاتی ممکن از میان یک شبکه با اتصالات محدود-به-ظرفیت را مدل‌سازی می‌کنند، از لوله‌های آب تا شبکه‌های داده. این راهنمای جامع شبکه‌های جریان را معرفی می‌کند، روش فورد-فالکرسون برای یافتن جریان بیشینه با استفاده از مسیرهای تقویتی را مرور می‌کند، و قضیه ظریف برش-کمینه/جریان-بیشینه که دو مسئله به‌ظاهر متفاوت را به یکی متصل می‌کند را توضیح می‌دهد.

ادامه