دانشکده ی علوم ریاضی ساختمان داده ا بان جلسه ی : درخت دودویی هرم مدر س: دکتر شهرام خزاي ی نگارنده: احمدرضا رحیمی مقدمه الگوریتم مرتب سازی هرمی یکی دیگر از الگوریتم های مرتب سازی است که دارای برخی از بهترین ویژگی های دو الگوریتم مرتب سازی درجی و مرتب سازی ادغامی که قبلا ا نها را مورد بررسی قرار دادیم است برخی از ویژگی های خوب الگوریتم مرتب سازی هرمی در زیر ا ورده شده: زمان اجرای (n O(n lg مثل مرتب سازی ادغامی مرتب سازی درجا مثل مرتب سازی درجی الگوریتم مرتب سازی هرمی را در جلسه بعد مطرح خواهیم کرد برای معرفی این الگوریتم لازم است مقدماتی را در باره ی داده ساختار هرم انواع درخت ها و درخت های دودویی بدانیم که در این جلسه به ا ن می پردازیم معرفی درخت ها تعریف (درخت) درخت یک گراف همبند است که دور ندارد تعریف (درخت دودویی) درخت دودویی به طور بازگشتی تعریف می شود: یک درخت دودویی مجموعه ای از گره هاست که ممکن است تهی باشد اگر تهی نباشد شامل یک گره به نام ریشه و دو درخت دودویی مجزا که زیردرخت های چپ و راست نامیده می شوند است تعریف (اجزای درخت دودویی) فرض کنید T یک درخت دودویی غیر تهی با ریشه r و زیردرخت های چپ و راست L و R باشد (فرزند چپ) اگر L تهی نباشد ریشه ا ن فرزند چپ r است (فرزند راست) اگر R تهی نباشد ریشه ا ن فرزند راست r است (پدر و فرزند) هریک از فرزندان چپ و راست r فرزند ا ن است همچنین r پدر فرزندان خود است HeapSort InsertonSort MergeSort Heap Bnary Trees -
(همزاد) فرزندان چپ و راست یک گره همزاد یکدیگرند (برگ) گره ای که هیچ فرزندی نداشته باشد برگ نامیده می شود (گره میانی) گره ای که ریشه یا برگ نباشد گره میانی نامیده می شود می توان یک درخت دودویی را با یک نمودار با رسم یالی بین پدران و فرزندان و قرار دادن گره ها در سطوح مختلف نشان داد شکل درخت های دودویی را نشان می دهد دقت کنید تنها گره ای که پدر ندارد ریشه درخت است (ا ) یک درخت دودویی با گره (ب) یک درخت دودویی با گره شکل : درخت های دودویی تعریف (ارتفاع درخت) طول طولانی ترین مسیر از ریشه به یک برگ را ارتفاع درخت می گوییم تعریف (ارتفاع گره) طول طولانی ترین مسیر از یک گره به یک برگ را ارتفاع ا ن گره می گوییم تعریف (سطح گره) به هر یک از گره های درخت با ارتفاع h یک شماره سطح در بازه [ h,۰] نسبت می دهیم ریشه در سطح ۰ قرار دارد اگر پدر گرهی در سطح d قرار گرفته باشد خود گره در سطح + d جای دارد تعریف (درخت دودویی کامل) نوعی درخت دودویی است که در ا ن هر گره دقیقا دو فرزند دارد لم برای هر درخت دودویی کامل با n گره و ارتفاع h داریم h+ n = یا معادلا ) + lg(n h = مثال شکل یک درخت دودویی کامل به ارتفاع = h که x ریشه ا ن است و y و z به ترتیب فرزندان چپ و راست x هستند و l ها برگ های درخت هستند ریشه در سطح ۰ گره های y و z در سطح و برگ ها همگی در سطح قرار گرفته اند تعریف (درخت دودویی تقریبا کامل) یک درخت دودویی تقریبا کامل با ارتفاع h نوعی درخت دودویی با ارتفاع h است که دو شرط زیر در ا ن صدق می کنند : در سطح dام درخت h d = ۰,, دقیقا d گره وجود دارند اگر در سطح h ام برگی موجود باشد تمام برگ های سمت چپ ا ن نیز حضور دارند لم برای هر درخت دودویی تقریبا کامل با n گره و ارتفاع h داریم h+ h < n یا معادلا n h = lg مثال شکل یک درخت دودویی تقریبا کامل با = n گره و به ارتفاع = h را نشان می دهد دقت کنید که درخت شکل ب با این درخت متفاوت است و درخت تقریبا کامل محسوب نمی شود -
x y z l l l l شکل : درخت دودویی کامل به ارتفاع شکل : یک درخت دودویی تقریبا کامل با گره نمایش ا رایه در درخت دودویی تقر یبا کامل می توان اعضای یک ا رایه را به جای نمایش برداری-سطری در یک درخت دودویی تقریبا کامل تصور کرد فرض می کنیم که اندیس عناصر ا رایه از شروع می شود برای این کار یک درخت دودویی تقریبا کامل که تعداد گره های ا ن برابر تعداد اعضای ا رایه است در نظر می گیریم سپس گره های درخت را با شروع از سطح صفر و از سمت چپ به راست با شروع از شماره گذاری می کنیم بدین ترتیب به ریشه اندیس فرزند چپ ا ن اندیس و به همین ترتیب ادامه می دهیم سپس عضو ام دنباله را در داخل گره با اندیس در درخت قرار می دهیم که کلید ا ن گره خوانده می شود چنین درختی هرم نامیده می شود مثال شکل یک ا رایه و درخت باینری (هرم) متناظر با ا ن را نشان می دهد بنابراین می توانیم به ا رایه [n A = ] به عنوان یک درخت دودویی نگاه کنیم که هر عضو ا ن متناظر با یک گره از درخت است در این درخت برای دستیابی به گره پدر و یا فرزندان یک گره توابع Parent() RghtChld() و LeftChld() که بر روی گره اجرا می شوند را به این صورت تعریف می کنیم: -
A = شکل : یک ا رایه و هرم متناظر با ا ن الگور یتم توابع Parent() RghtChld() و LeftChld() functon Parent() return 2 functon LeftChld() return 2 functon RghtChld() return 2 + 1 ساختار هرم در علوم کامپیوتر هرم یک داده ساختار بر مبنای درخت دودویی تقریبا کامل است که دارای این خاصیت است که رابطه کوچکتر یا بزرگتری بین کلید گره های پدر و فرزند در طول درخت ثابت است به این خاصیت ويژگی هرم می گویند بسته به رابطه بزرگ تری و یا کوچک تری هرم بیشینه یا کمینه خوانده می شود تعریف (هرم بیشینه و هرم کمینه ( اگر در یک درخت کلید هر گره از کلید فرزندانش بزرگ تر باشد درخت دارای ويژگی هرم بیشینه و اگر کلید هر گره از کلید فرزندانش کوچک تر باشد درخت دارای ويژگی هرم کمینه است به طور خلاصه در هرم بیشینه A[] A[Parent()] و در هرم کمینه A[] A[Parent()] است مثال شکل یک هرم بیشینه و یک هرم کمینه را نشان می دهد الگوریتم مرتب سازی هرمی نیاز به الگوریتمی دارد که ابتدا با استفاده از ا ن بتوان یک هرم را به یک هرم بیشینه تبدیل کرد پس ابتدا لازم است با الگوریتم های زیر ا شنا شویم: ( :MaxHeapfy(array,A ndex در زمان (n O(lg اجرا می شود و با فرض این که زیر درخت های چپ و راست گره هرم بیشینه اند با حفظ ویژگی هرم بیشینه کلید گره را به مکان مناسب می برد و زیر درخت با ریشه را به یک هرم بیشینه تبدیل می کند heap property MaxHeap MnHeap -
(ا ) هرم بیشینه (ب) هرم کمینه شکل : هرم های کمینه و بیشینه (A :BuldMaxHeap(array در زمان O(n) اجرا می شود و از ا رایه ورودی یک هرم بیشینه می سازد الگور یتم MaxHeapfy الگوریتم MaxHeapfy یک ا رایه A و اندیس را به عنوان ورودی می گیرد و فرض می کنیم زیر درخت های چپ و راست گره خود هرم بیشینه باشند اگر A[LeftChld()] A[] یا A[RghtChld()] A[] باشد به A[] اجازه می دهد که در هرم بیشینه به سمت پایین حرکت کند تا ویژگی هرم بیشینه حفظ شود Algorthm 2 Algorthm: Max-Heapfy functon MaxHeapfy(array A, ) l LeftChld() r RghtChld() f l n and A[l] > A[] then largest l else largest f r n and A[r] > A[] then largest r f largest then Exchange A[] wth A[largest] MaxHeapfy(A,largest) در هر مرحله الگوریتم اندیسی متناظر با خانه ی حاوی بیشینه سه عدد A[] A[RghtChld()], A[LeftChld()], را پیدا و ا ن را در largest ذخیره می کند اگر A[] بزرگترین عدد باشد یعنی largest = زیر درخت با ریشه خود یک هرم بیشینه است و نیاز به تغییر ندارد اما اگر A[] بزرگترین نباشد جای ا ن با A[largest] عوض می شود دقت کنید که ویژگی هرم بیشینه ممکن است برای زیر درخت جدید با ریشه largest از بین برود بنابراین MaxHeapfy را بر روی زیر درخت با ریشه largest به صورت بازگشتی اجرا می کنیم زمان یک بار اجرای MaxHeapfy بر روی درخت با انداره n با ریشه در گره برابر ()Θ است علاوه بر این باید زمان اجرای روال بازگشتی MaxHeapfy -
بر روی زیر درخت هایی که فرزندان ریشه های ا ن هستند (همه فراخوانی هایMaxHeapfy ) را حساب کنیم چون n گره دارند-بدترین حالت زمانی رخ میدهد که پایین ترین سطح درخت نیمه پر زیر درخت های فرزندان حداکثر باشد- زمان اجرای MaxHeapfy بر روی درخت از رابطه بازگشتی زیر بدست می ا ید T (n) T ( n ) + Θ() که با توجه به رابطه ی بازگشتی بالا زمان اجرای این الگوریتم (n O(lg است مثال شکل صفحه بعد اجرای الگوریتم MaxHeapfy را روی زیردرخت با ریشه در درخت شکل الف نشان می دهد با توجه به اینکه کلید گره از کلید فرزندانش (گره های و ) کوچک تر است اندیس که فرزند حاوی کلید بزرگ تر است در largest قرار می گیرد و جای کلید گره های و با هم عوض می شوند فراخوان بعدی روی زیردرخت با ریشه درخت شکل ب اجرا می شود و جای کلید گره های و عوض می شود و به درخت شکل ج تبدیل می شود (ا ) (ب) (ج) شکل : اجرای MaxHeapfy -
ساختن هرم با استفاده از MaxHeapfy می توانیم ا رایه [n ]A را به یک هرم بیشینه تبدیل کنیم الگوریتم BuldMaxHeap از گره های داخلی (غیر برگ) درخت می گذرد و برروی هر گره MaxHeapfy را اجرا می کند دقت کنید که عنصر های ]A n تماما برگ های درخت هستند که خود هرم بیشینه هستند بنابراین نیاز به اجرای الگوریتم زیر ا رایه n] + MaxHeapfy روی ا ن ها نیست Algorthm 3 Algorthm: BuldMaxHeap functon BuldMaxHeap(array A[1 n]) for = n 2 downto 1 do MaxHeapfy(A, ) مثال اجرای الگوریتم BuldMaxHeapfy بر روی ا رایه زیر در شکل قابل مشاهده است اثبات درستی BuldMaxHeap قضیه BuldMaxHeap یک هرم بیشینه را تولید می کند برهان برای اثبات درستی الگوریتم ابتدا یک ناوردای حلقه مناسب به صورت زیر در نظر می گیریم: ناوردای حلقه: درست پس از مقداردهی متغیر حلقه ی for با مقدار همه گره های +, + n ریشه های یک هرم بیشینه هستند حال صحت مراحل ا غاز نگهداری و پایان را نشان می دهیم: n یک برگ است و بنابراین ریشه یک هرم ا غاز: قبل از اولین تکرار for هر کدام از گره های n + بیشینه ی بدیهی است نگهداری: فرض کنید درست پس از مقداردهی متغیر حلقه ی for با مقدار ناوردایی حلقه برقرار باشد یعنی همه ی گره های +, +,, n ریشه های یک هرم بیشینه باشند در تکرار بعدی مقدار متغیر حلقه به کاهش می یابد باید نشان دهیم با اجرای ( MaxHeapfy(A, همه ی گره های,,+,+, n ریشه های یک هرم بیشینه هستند توجه کنید که فرزندان گره دارای اندیسی هستند که از بزرگترند و طبق فرض ناوردایی خود ریشه های هرم هایی بیشینه هستند بنابراین شرط لازم برای اجرای صحیح ( MaxHeapfy(A, برقرار است و با اجرای ا ن نیز ریشه ی یک هرم بیشینه می شود بقیه گره های +, +,, n که قبلا ریشه های یک هرم بیشینه بوده اند باز هم دارای این ویژگی خواهند بود (چرا ) پایان: در پایان متغیر حلقه ی برابر = ۰ است و با توجه به ناوردای حلقه همه گره های, n, ریشه یک هرم بیشینه است بالاخص خود درخت نیز یک هرم بیشینه است -
(د) ( ه) (و) (ا ) (ب) (ج) نوچ دنک یم ارجا ار MaxHeapfy(A, ) و دنک یم زاغا ار دوخ راک هرگ زا BuldMaxHeap متیروگلا : لکش و دور یم هرگ هب for ی هقلح ریغتم و دنام یم یقاب رییغت نودب تخرد دنتسه هنیشیب مره هرگ نیا یاه تخرد ریز دنوش یم هنیشیب مره هرگ یاه تخرد ریز و دوش یم ضوع اب هرگ یاج و دوش یم ارجا MaxHeapfy(A, ) تسرد هنیشیب مره کی و دوش یم ارجا MaxHeapfy(A, ) دسر یم هب for ی هقلح ریغتم یتقو بیترت نیمه هب دوش یم -
زمان اجرای BuldMaxHeap زمان اجرای MaxHeapfy برای یک گره با ارتفاع h برابر O(h) است بنابراین می توانیم یک کران بالای ساده برای زمان کل BuldMaxHeap را با توجه به اینکه زمان اجرای MaxHeapfy برابر (n O(lg است و BuldMaxHeap را n بار اجرا می کند پیدا کنیم بنابراین زمان اجرای BuldMaxHeap برابر (n O(n lg است اما این کران یک کران خوب نیست برای محاسبه یک کران بالای بهتر می توانیم به صورت زیر عمل کنیم: می دانیم: lg n h=۰ n ( O(h) = O h+ h=۰ h h = n lg n h=۰ ) h h بنابراین زمان اجرای الگور یتم BuldMaxHeap برابر است با: ( O n ) lg n h h=۰ = O ( n ) h h=۰ h = O (n) h به وضوح زمان اجرای الگوریتم روی هر ا رایه دلخواه از Ω(n) است بنابراین زمان اجرای الگوریتم Θ(n) است -