مرتب‌سازی سریع
کوییک‌سورت ، یکی از الگوریتم‌های مرتب‌سازی است که به‌دلیل مصرف حافظه کم، سرعت اجرای مناسب و پیاده‌سازی ساده بسیار مورد قبول واقع شده‌است.
هر پیاده‌سازی این الگوریتم به‌صورت کلی از دو بخش تشکیل شده‌است. یک بخش تقسیم‌بندی آرایه (partition) و قسمت مرتب کردن. روش مرتب‌سازی سریع (Quick Sort) یکی از الگوریتم‌های مشهور مرتب‌سازی داده‌ها است. این الگوریتم طی مراحل بازگشتی زیر یک روش تقسیم و غلبه برای مرتب کردن داده‌ها ارائه می‌نماید:
۱- انتخاب عنصر محوری: یکی از عناصر آرایه به عنوان عنصر محوری (pivot) - به عنوان مثال عنصر اول - انتخاب می‌شود.
۲- تقسیم آرایه: چینش عناصر آرایه به قسمی تغییر داده می‌شود که تمامی عناصر کوچکتر یا مساوی محور در سمت چپ آن، و تمامی عناصر بزرگتر در سمت راست آن قرار بگیرند. این دو قسمت زیر آرایه‌های چپ و راست نامیده می‌شوند.
۳- مرتب‌سازی بازگشتی: زیرآرایه‌های چپ و راست به روش مرتب‌سازی سریع مرتب می‌شوند.
مراحل مختلف (Partion (1,10 را بر روی داده‌های زیر بنویسید.
i، ۱، ۲، ۳، ۴، ۵، ۶، ۷، ۸، ۹، ۱۰، i، P
A[i]، ۶۵، ۷۰، ۷۵، ۸۰، ۸۵، ۶۰، ۵۵، ۵۰، ۴۵، ∞، ۲، ۹
جا به جایی ۴۵ با ۷۰: ۹ ،۲، ∞ ،۷۰ ،۵۰ ،۵۵ ،۶۰، ۸۵ ،۸۰ ،۷۵، ۴۵ ،۶۵
جا به جایی ۷۵ با ۵۰: ۸ ،۳، ∞ ،۷۰ ،۷۵ ،۵۵ ،۶۰ ،۸۵ ،۸۰ ،۵۰ ،۴۵ ،۶۵
جا به جایی ۸۰ با ۵۵: ۷ ،۴، ∞، ۷۰ ،۵۰ ،۸۰ ،۶۰ ،۸۵ ،۵۵ ،۷۵ ،۴۵ ،۶۵
جا به جایی ۸۵ با ۶۰: ۶ ،۵، ∞ ،۷۰ ،۵۰ ،۸۰ ،۸۵ ،۶۰ ،۵۵ ،۷۵ ،۴۵ ،۶۵
جا به جایی ۶۵ با ۶۰: ۵ ،۶، ∞ ،۷۰ ،۵۰ ،۸۰ ،۸۵ ،۶۵ ،۵۵ ،۷۵ ،۴۵ ،۶۰
فرض کنید که بعد از فراخوانی الگوریتم Partition عنصر افراز در مکان j ام قرار بگیرد، در این صورت بدیهی است که j-1 عنصر آرایهٔ کوچکتر یا مساوی A[j] است و n-j عنصر باقی‌مانده بزرگتر یا مساوی آن خواهد بود؛ بنابراین سه حالت زیر امکان‌پذیر است:
اگر kj آنگاه kامین عنصر کوچک‌تر آرایه برابر k-jامین عنصر کوچکتر آرایهٔ A[j+1...n] خواهد بود.
مطالب گفته شده توسط الگوریتم Selection در زیر ارائه شده‌است:
آرایه[A[p..r، عنصر آخر هربار به عنوان pivot قرار می‌گیرد.
Partition(A,p,r)
quickSort(A,p,r)
نمونه‌ای از این پیاده‌سازی به زبان ++C به صورت زیر است.
int partition(int array[] , int left , int right){
پیاده‌سازی مشابه ولی فشرده‌تر به زبان pascal به صورت زیر می‌تواند باشد procedure Sort(l, r: Integer); var i, j, x, y: integer; begin i := l; j := r; x := a[(l+r) DIV 2]; repeat while a[i] < x do i := i + 1; while x < a[j] do j := j - 1; if i <= j then begin y := a[i]; a[i] := a[j]; a[j] := y; i := i + 1; j := j - 1; end; until i > j; if l < j then Sort(l, j); if i < r then Sort(i, r); end;
و یا این سورس پاسکال که بیشتر به کد سی این مثال شبیه است:
Function partion(ilow, ihigh: Integer): Integer; Var Begin
End;
Procedure QuickSort(p, q: Integer); Var Begin End; در این پیاده‌سازی به جای این که همیشه از[ A[r به عنوان عنصر محوری استفاده کنیم، از عنصری که به‌طور تصادفی از زیر آرایهٔ [A[p..r انتخاب می‌شود استفاده می‌کنیم. این کار با تعویض عنصر [ A[r با عنصری که از [A[p..r به‌طور تصادفی انتخاب می‌شود انجام می‌دهیم. این تغییر که در آن به‌طور تصادفی از ادامه r...p یک نمونه انتخاب می‌کنیم، اطمینان می‌دهد که عنصر محوری [x= A[r با احتمال برابر می‌تواند هر یک از r-p+1 عنصر زیر آرایه باشد. چون عنصر محوری به شکلی تصادفی انتخاب می‌شود انتظار داریم تقسیمات آرایه ورودی در حالت میانگین به شکل مناسبی متوازن باشد. زمان اجرای مرتب‌سازی سریع به زمانی که در روال partition صرف می‌شود بستگی دارد. هر بار که روال partition فراخوانی می‌شود، یک عنصر محوری انتخاب می‌شود و این عنصر هیچ‌گاه در فراخوانی‌های بعدی مرتب‌سازی سریع و partition ظاهر نمی‌شود. بنابراین حداکثر n فراخوانی Partition در کل اجرای الگوریتم مرتب‌سازی سریع وجود دارد و یک فراخوانی Partition دارای زمان (O(1 به اضافه مقدار زمانی که متناسب با تعداد تکرارهای حلقه for در الگوریتم است. هر تکرار حلقه for یک مقایسه انجام می‌دهد، مقایسه بین عنصر محوری و عنصر دیگری از آرایه A. بنابراین اگر بتوانیم تعداد کل دفعاتی که این مقایسه‌ها اجرا می‌شود را محاسبه کنیم، می‌توانیم کل زمانی را که در حلقه for در طی اجرای کامل quickSort صرف می‌شود را محدود کنیم.
الگوریتم مرتب‌سازی در دنیای واقعی برای آرایه نسبتاً کوچک مناسب نیست. به علاوه بخش پارتیشن خود نیز مشکل بزرگی در زمان اجرا می‌باشد. برای همین پیشنهاد می‌گردد برای آرایه‌هایی از طول کمتر از ۷ از مرتب‌سازی‌های دیگر مانند مرتب‌سازی درجی یا حبابی استفاده شود. به علاوه به جای پیاده‌سازی بخش partition به صورت عادی با احتمالاتی می‌توان از میانه ۹ برای آرایه‌های بزرگ (بیش از ۴۰ درایه) و میانه ۳ برای ارایه‌های متوسط (کمتر از ۴۰ درایه) و عضو وسط برای آرایه‌های کوچک استفاده کرد. به علاوه در چنین پیاده‌سازی‌هایی ابتدا اعداد صفر (برای آرایه از اعداد مثبت) را ابتدا به شروع آرایه منتقل می‌کنند. و همچنین درایه‌های غیر عددی را نیز هندل می‌کنند تا در اجرای الگوریتم اختلالی به وجود نیاورد.
برای توضیحات بیشتر دربارهٔ نسخه‌های بهینه مرتب‌سازی سریع می‌توانید به مرجع بنتلی و مک ایلوری مراجعه نمایید. پیاده‌سازی بسیار خوبی از این الگوریتم را می‌توانید در کد منبع جاوا و در کلاس java.util.Array بیابید.
مرتب‌سازی سریع چه در پیاده‌سازی عادی و چه در پیاده‌سازی احتمالی در حالت متوسط در زمان اجرای formula_1 اجرا می‌شود.
ویژگی‌های مرتب‌سازی سریع
۱- پیچیدگی زمانی اجرای الگوریتم در بهترین حالت (Ө(n log n و در بدترین حالت (Ө(n2 است. با استفاده محاسبات ریاضی می‌توان نشان داد در حالت متوسط نیز مرتبه اجرا (Ө(n log n است.
۲- این الگوریتم یک مرتب‌سازی درجا است. یعنی میزان حافظه مصرفی الگوریتم مستقل از طول آرایه است.
۳- زمانی که تعداد عناصر آرایه کم باشد، سرعت اجرای مرتب‌سازی درجی بهتر از مرتب‌سازی سریع است. به همین دلیل طی مراحل بازگشتی مرتب‌سازی سریع، اگر طول بازه عدد کوچکی باشد، معمولاً بازه با مرتب‌سازی درجی مرتب می‌شود.
۴- الگوریتم مرتب‌سازی سریع با پیاده‌سازی فوق یک روش ناپایدار است. چنین الگوریتمی لزوما ترتیب عناصر با مقدار یکسان را پس از مرتب‌سازی حفظ نمی‌کند.
۵- انتخاب عنصر محوری بحث مفصلی دارد. اما در کل یک انتخاب آزاد است. می‌توان عنصر اول، عنصر آخر، یا هر عنصر دیگری را به عنوان عنصر محوری انتخاب کرد. حتی لازم نیست از ابتدا تا انتها از یک روش انتخاب استفاده کرد. یکی از روش‌های رایج، انتخاب یک عنصر تصادفی به عنوان عنصر محوری است. اگرچه انتخاب عنصر محوری مناسب باعث بالا رفتن کارایی الگوریتم می‌شود، اما عموماً هزینه لازم برای یافتن چنین محوری بالا بوده و مقرون به صرفه نیست.