Quick Sort مرتب سازي سريع
گرد آوري:فاطمه آقامحمدی استاد مربوطه: استاد كسمايي آموزشكده 17 شهريور آبان 88
فهرست الگوريتم مرتب سازي سريع مثال پياده سازي مرتب سازي سريع در c++ :(نوع اول ) پياده سازي مرتب سازي سريع در c++: (نوع دوم ) بررسي پيچيدگي
الگوريتم مرتب سازي سريع: الگوريتم مرتب سازي سريع را هوار (hoar)پي ريزي كرد. مرتب سازي سريع از اين لحاظ مشابه مرتب سازي ادغامي است كه از تقسيم آرايه به دو بخش و سپس مرتب كردن بازگشتي هر يك از بخش ها صورت مي پذيرد. در اين روش در هر گذر يك عنصر به عنوان عنصر محوري يا لولا انتخاب شده و ساير عناصر به گونه اي جابه جا مي شوند كه كليه عناصر بزرگتر از آن در يك طرف و كليه عناصر كوچكتر از آن در طرف ديگر عنصرلولا قرار گيرند (در اين حالت مي گوييم آرايه افزار شده است).سپس دو بردار دو طرف اين عنصر به صورت بازگشتي به همين روش مرتب مي شود. با توجه به توضيحات فوق اين الگوريتم از نوع الگوريتم هاي تقسيم و غلبه است كه با كوچك كردن بازه عمل به چند بازه و سپس تركيب جوابهاي آنها، مسئله را حل مي كند.اين الگوريتم بر اساس جا به جا كردن زياد عناصر عمل مي كند.
پس از افزار آرايه ، ترتيب عناصر در زير آرايه ها مشخص نيست و به نحوه پياده سازي افزار بستگي دارد. ترتيب آنها از چگونگي افزار آرايه ناشي مي شود. نكته مهم اين است كه تمام عناصر كوچكتر از عنصر محوري در طرف چپ آن و همه عناصر بزرگتر از آن در طرف راست واقع هستند. سپس مرتب سازي سريع، يه طور بازگشتي فراخواني مي شود تا هريك از دو آرايه را مرتب كند. آنها نيز افزار مي شوند و اين روال آنقدر ادامه مي يابد تا به آرايه اي با يك عنصر برسيم، چنين آرايه اي ذاتا مرتب است.
با يك مثال مرتب سازي سريع را بيشتر توضيح مي دهيم: اگر آرايه زير موجود باشد : 25 20 10 12 27 13 22 15 عنصر محوري 15 10 12 13 25 20 27 22 عناصر كوچكتر از عنصر محوري عناصر بزرگتر از عنصر محوري
10 12 13 15 25 20 27 22 عنصر محوري 15 25 20 27 22 10 12 13 در اين شرايط بدترين حالت اتفاق مي افتد چون يكي از زير آرايه ها خالي است. اما اگر : 10 12 13 15 25 20 27 22 عنصر محوري 15 25 20 27 22 10 12 13
تذكر 1: در اين الگوريتم انتخاب عنصر محوري تاثير مهمي در سرعت اجرا دارد. اگر اين عنصر، عنصر ميانه آرايه باشد، آرايه را به دو زير آرايه تقريبا مساوي افزار خواهد نمود و مي توان ثابت كرد در اين شرايط بهترين حالت روي داده است. اما اگر عنصر لولا كوچكترين يا بزرگترين عنصر آرايه باشد در اين شرايط يكي از زير آرايه ها تهي خواهد شد و در اين شرايط بدترين حالت روي داده است. از آنجا كه الگوريتم يافتن عنصر ميانه براي هر بار افزار باعث افزايش كلي مرتبه الگوريتم خواهد شد امكان استفاده از آن وجود ندارد. در عمل، عنصر محوري به صورت تصادفي انتخاب مي شود. معمولا براي سادگي عنصر اول آرايه را به عنوان عنصر لولا در نظر مي گيرند ولي هر عنصر ديگر تصادفي نيز مي تواند انتخاب شود.
پياده سازي مرتب سازي سريع در c++ :(نوع اول ) مسئله: مرتب سازي n كليد غير نزولي. ورودي ها: عدد صحيح n، آرايه اي از كليد هاي S كه از 1 تا n انديس گذاري شده اند. خروجي ها: آرايه S حاوي كليدها به ترتيب غير نزولي. Void quicksort ( index low, index high ) { index pivotpoint; if (high > low ) partition (low,high,pivotpoint); quicksort (low,pivotpoint-1); quicksort (pivotpoint+1,high); }
مسئله : افزار آرايه S براي مرتب سازي سريع. ورودي ها : دو انديس low و high و زير آرايه S كه از low تا high انديس گذاري شده اند. خروجي ها : pivotpoint، نقطه محوري زير آرايه اي كه از low تا high انديس گذاري شده است. Void partition (index low, index high, index &pivotpoint) { index i,j; keytype pivotitem; pivotitem = s[low]; // choose first item for pivotitem j=low; for (i=low+1;i<=high;i++) if ( s[i] < pivotitem) j++; exchange s[i] and s[j]; } pivotpoint=j; exchange s[low] and s[pivotpoint]; // put pivotitem to pivot point
پيرو قراردادي كه داشتيم، sو n پارامترهاي روال quicksort نيستند پيرو قراردادي كه داشتيم، sو n پارامترهاي روال quicksort نيستند. اگر الگوريتم با تعريف عمومي S پياده سازي مي شد و n تعداد عناصر مي بود، فراخواني سطح بالاي quicksort يه صورت زير مي باشد: Quicksort(1,n); روال partition با چك كردن هريك از عناصر موجود در آرايه كار مي كند.هرگاه معلوم شود عنصري كوچكتر از عنصر محوري است، به طرف چپ آرايه حركت داده مي شود.
پياده سازي مرتب سازي سريع در c++: (نوع دوم ) Procedure QuickSort (var x:arraylist; left, right: integer); Var i,j,pivot: integer; Begin if left < right then begin i:=left ; j:= righ+1; pivot:= x[left]; repeat i:=i+1; until x[1]>=pivot ; j:=j+1; until x[j]<=pivot ; if i<j then swap (x[i],x[j]); until i>=j; swap (x[left],x[j]); QuickSort (x,left,j-1); QuickSort (x,j+1,right); end ;{if} end;
فرض كنيد آرايه اوليه به صورت زير باشد، انجام يك مرحله از الگوريتم فوق به صورت زير است : در ابتدا left=1 و right=10 و i=1 و j=11 و pivot= 26 مي شود. سپس از سمت چپ مرتبا i زياد شده تا هنگامي كه به اولين عنصر بزرگتر از 26 (يعني 37) برسيم. به همين ترتيب از سمت راست مرتبا j كم مي شود تا به اولين عنصر كوچكتر از 26 (يعني 19) برسيم. در اينحال جاي اين دو خانه يعني 19و37 عوض مي شود: X[1] X[2] X[3] X[4] X[5] X[6] X[7] X[8] X[9] X[10] 26 5 37 1 61 11 59 15 48 19 i=1 i!> 26 i=1 X[i]!>26 i=2 X[i]!>26 i=3 X[i]>26 j=11 X[j]<26 left 26 5 37 1 61 11 59 15 48 19 right
26 5 37 1 61 11 59 15 48 19 19 37 26 5 19 1 61 11 59 15 48 37 15 61 در مرحله بعد حلقه، j به عدد 11 و i به عدد 59 اشاره مي كند و شرط جلوي until i>= j درست بوده و حلقه هاي repeat تمام مي شوند. j i 26 5 19 1 15 11 59 61 48 37 در اين حال x[left] كه همان 26 است با x[j] يعني عدد 11 جابه جا مي شود و داريم: 26 5 19 1 15 11 59 61 48 37 11 26
همانطور كه مشاهده مي كنيد پس از يكبار صدا زدن QuickSort تمام اعداد سمت راست عدد 26 از آن بزرگتر و تمام اعداد سمت چپ عدد 26 از آن كوچكتر هستند، يعني 26 در جاي درست خود قرار گرفته است. حال هريك از دو آرايه سمت راست و چپ 26 را به صورت مجزا با همان روش فوق مرتب مي كنيم. بدين ترتيب به صورت بازگشتي كل آرايه را مي توان مرتب كرد.
بررسي پيچيدگي اين الگوريتم متعادل نيست و در بردارهاي مرتب بدترين عملكرد و در بردارهاي نا مرتب بهترين عملكرد را نشان مي دهد. پيچيدگي اين الگوريتم به صورت زير است : بدترين حالت حالت متوسط بهترين حالت O(n^2) O(n log n) پيچيدگي اجرا نكته : اگر داده ها در هربار به طور مساوي تقسيم شوند حداكثر عمق درخت بازگشتي log n بوده و به فضاي پشته O(log n) نياز خواهد بود. ولي در بدترين حالت اگر در هر سطح بازگشتي داده ها به قسمتي به اندازه n-1 و قسمت ديگر به اندازه صفر تقسيم شود، انگاه فضاي پشته مورد نياز O(n) خواهد بود.
منابع: كتاب ساختمان داده ، تاليف مهندس حميدرضا مقسمي. كتاب طراحی الگوريتم ها با شبه کدها یc++ (ريچارد نيپوليتان ,کيومرث نعيمي پور ,عين الله قمی).