الگوریتم مرتب‌سازی

الگوریتم مرتب‌سازی، در دانش رایانه و ریاضی، الگوریتمی است که فهرستی از داده‌ها را به ترتیبی مشخص می‌چیند.
پرکاربردترین ترتیب‌ها، ترتیب‌های عددی و واژه‌نامه‌ای هستند. مرتب‌سازی کارا در بهینه‌سازی الگوریتم‌هایی که به فهرست‌های مرتب شده نیاز دارند (مثل جستجو و ترکیب) اهمیت زیادی دارد.
از آغاز علم رایانه مسائل مرتب‌سازی بررسی‌های فراوانی را متوجه خود ساختند، شاید به این علت که در عین ساده بودن، حل آن به صورت کارا پیچیده است. برای نمونه مرتب‌سازی حبابی در سال ۱۹۵۶ به وجود آمد. در حالی که بسیاری این را یک مسئلهٔ حل شده می‌پندارند، الگوریتم کارآمد جدیدی همچنان ابداع می‌شوند (مثلاً مرتب‌سازی کتاب‌خانه‌ای در سال ۲۰۰۴ مطرح شد).
مبحث مرتب‌سازی در کلاس‌های معرفی علم رایانه بسیار پرکاربرد است، مبحثی که در آن وجود الگوریتم‌های فراوان به آشنایی با ایده‌های کلی و مراحل طراحی الگوریتم‌های گوناگون کمک می‌کند؛ مانند تحلیل الگوریتم، داده‌ساختارها، الگوریتم‌های تصادفی، تحلیل بدترین و بهترین حالت و حالت میانگین، هزینهٔ زمان و حافظه، و حد پایین.

طبقه‌بندی

در علم کامپیوتر معمولاً الگوریتم‌های مرتب‌سازی بر اساس این معیارها طبقه‌بندی می‌شوند:

پیچیدگی (بدترین و بهترین عملکرد و عملکرد میانگین)

با توجه به اندازهٔ فهرست (n). در مرتب‌سازی‌های معمولی عملکرد خوب  و عملکرد بد  است. بهترین عملکرد برای مرتب‌سازی  است. الگوریتم‌هایی که فقط از مقایسهٔ کلیدها استفاده می‌کنند در حالت میانگین حداقل  مقایسه نیاز دارند.

حافظه (و سایر منابع کامپیوتر)

بعضی از الگوریتم‌های مرتب‌سازی «در جا» هستند. یعنی به جز داده‌هایی که باید مرتب شوند، حافظهٔ کمی () مورد نیاز است؛ در حالی که سایر الگوریتم‌ها به ایجاد مکان‌های کمکی در حافظه برای نگه‌داری اطلاعات موقت نیاز دارند.

پایداری[۲]

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

مقایسه‌ای بودن یا نبودن

در یک مرتب‌سازی مقایسه‌ای داده‌ها فقط با مقایسه به وسیلهٔ یک عملگر مقایسه مرتب می‌شوند.

روش کلی

درجی، جابجایی، گزینشی، ترکیبی و غیره. جابجایی مانند مرتب‌سازی حبابی و مرتب‌سازی سریع و گزینشی مانند مرتب‌سازی پشته‌ای.

مرتب‌سازی حبابی

مرتب‌سازی حبابی (به انگلیسی: Bubble sort) یک الگوریتم مرتب‌سازی ساده‌است که لیست را پشت سرهم پیمایش می‌کند تا هر بار عناصر کنارهم را با هم سنجیده و اگر در جای نادرست بودند جابه‌جایشان کند. در این الگوریتم این کار باید تا زمانی که هیچ جابه‌جایی در لیست رخ ندهد، ادامه یابد و در آن زمان لیست مرتب شده‌است. این مرتب‌سازی از آن رو حبابی نامیده می‌شود که هر عنصر با عنصر کناری خود سنجیده‌شده و درصورتی که از آن کوچک‌تر باشد جای خود را به آن می‌دهد و این کار همچنان پیش می‌رود تا کوچک‌ترین عنصر به پایین لیست برسد و دیگران نیز به ترتیب در جای خود قرار گیرند (یا به رتبه‌ای بالاتر روند یا به پایین‌تر لیست رانده شوند) این عمل همانند پویش حباب به بالای مایع است.
این مرتب‌سازی از آن رو که برای کار با عناصر آن‌ها را با یکدیگر می‌سنجد، یک مرتب‌سازی بر مبنای مقایسه‌است.
با فرض داشتن n عضو در لیست، در بدترین حالت  عمل لازم خواهد بود.
یک آرایه با مقادیر “۵, ۱, ۴, ۲, ۸” را در نظر بگیرید. آن را به ترتیب صعودی با استفاده از مرتب‌سازی حبابی مرتب می‌کنیم.
گذر اول:
(۱, ۵, ۴, ۲, ۸) <= (۵, ۱, ۴, ۲, ۸)
در اینجا الگوریتم دو عنصر اول را مقایسه، و جابجا می‌کند.
(۱, ۴, ۵, ۲, ۸) <= (۱, ۵, ۴, ۲, ۸)(۱, ۴, ۲, ۵, ۸) <= (۱, ۴, ۵, ۲, ۸)(۱, 4, 2, ۵, ۸) <= (۱, ۴, ۲, ۵, ۸)
همان‌طور که مشخص است آرایه هنوز مرتب نشده‌است چون مقدار خانهٔ دوم آرایه ۴ از خانهٔ سوم ۲ بزرگتر است بنابراین نیاز به یک گذر دیگر داریم:
گذر دوم:
(۱, ۴, ۲, ۵, ۸) <= (۱, ۴, ۲, ۵, ۸)(۱, ۲, ۴, ۵, ۸) <= (۱, ۴, ۲, ۵, ۸)(۱, ۲, ۴, ۵, ۸) <= (۱, ۲, ۴, ۵, ۸)(۱, ۲, ۴, ۵, ۸) <= (۱, ۲, ۴, ۵, ۸)
حالا آرایه مرتب شده‌است، ولی الگوریتم هنوز نمی‌داند که این کار کامل انجام شده‌است یا نه، که برای فهمیدن احتیاج به یک گذر کامل بدون هیچ جابجایی (swap) داریم:
گذرسوم
(۱, 2, 4, ۵, ۸) <= (۱, 2, 4, ۵, ۸)(۱, ۲, ۴, ۵, ۸) <= (۱, 2, 4, ۵, ۸)(۱, ۲, ۴, ۵, ۸) <= (۱, ۲, ۴, ۵, ۸)(۱, ۲, ۴, ۵, ۸) <= (۱, ۲, ۴, ۵, ۸)
در نهایت آرایه مرتب شده‌است و الگوریتم می‌تواند پایان پذیرد.
بیان سادهٔ شبه کد مرتب‌سازی حبابی:
procedure bubbleSort(A: list of sortable items) defined as:doswapped:= falsefor each i in 0 to length(A) - 2 inclusive do:if A[ i ]> A[ i + 1 ] thenswap(A[ i ], A[ i + 1 ])swapped:= trueend ifend forwhile swappedend procedure

مرتب‌سازی انتخابی

(به انگلیسی: Selection Sort)
معمولاً اطلاعات و داده‌های خامی که در اختیار برنامه‌نویس قرار دارد به صورت نامرتب هستند. مواقعی پیش می‌آید که لازم است این داده‌ها بر حسب فیلد خاصی مرتب بشوند؛ مانند لیست دانش آموزان بر حسب معدل، لیست کارمندان بر حسب شماره پرسنلی، لیست دفترچه تلفن بر حسب نام خانوادگی و… روش‌های متعددی برای مرتب‌سازی وجود دارد. برای شروع روش مرتب‌سازی انتخابی (Selection Sort):
روش انتخابی اولین روشی است که به ذهن می‌رسد: بزرگ‌ترین رکورد بین رکوردهای لیست را پیدا می‌کنیم و به انتهای لیست انتقال می‌دهیم. از بقیه رکوردها بزرگ‌ترین را انتخاب می‌کنیم و انتهای لیست - کنار رکورد قبلی - قرار می‌دهیم و… مثلاً:
۰:  ۹ ۱ ۶ ۴ ۷ ۳ ۵۱:  ۵ ۱ ۶ ۴ ۷ ۳ ۹۲:  ۵ ۱ ۶ ۴ ۳ ۷ ۹۳:  ۵ ۱ ۳ ۴ ۶ ۷ ۹۴:  ۴ ۱ ۳ ۵ ۶ ۷ ۹۵:  ۳ ۱ ۴ ۵ ۶ ۷ ۹۶:  ۱ ۳ ۴ ۵ ۶ ۷ ۹
پیاده‌سازی (مرتب‌سازی انتخابی) در ++C
void selection_sort (int arr[] , int n){
  register int i , j;
  int max , temp;
  for (i = n-1 ; i> 0 ; i--)
  {
    max = 0;
    for (j = 1 ; j <= i ; j++)
      if (arr[max] <arr[j])
        max = j;
    temp = arr[i];
    arr[i] = arr[max];
    arr[max] = temp;
  }}
کد مرتب‌سازی انتخابی همراه با جستجوی باینری به زبان ++C
#include <iostream.h>using namespace std;/*** functions definition ***/int selectionSort(int __array[], int arrayLength);int binerySearch(int __array[], int arrayLength, int __target);int main(){
   // declear arrays
   int numArray[] = {0, 9, 8, 10 , 2, 1};
   // sort arrays
   selectionSort(numArray, 6);
   // print arrays
   cout<<"Selection sort:"<<endl;
   for (int i = 0; i <6; i++) cout<<numArray[i]<<endl;
   if (binerySearch(numArray, 6, 2) != -1)
      cout<<"Your target index is: "<<binerySearch(numArray, 6, 2)<<endl;
   else
      cout<<"Not found !"<<endl;
   return 0;}int selectionSort(int __array[], int arrayLength) // unstable{
   for (int i = 0; i <arrayLength; i++)
      for (int j = 0; j <arrayLength - 1; j++)
          if (__array[j] <__array[j + 1])
          {
             // swap the parameters
             int __tmp = __array[j + 1];
             __array[j + 1] = __array[j];
             __array[j] = __tmp;
          }
    return 0;}int binerySearch(int __array[], int arrayLength, int __target) // you must sort the array first{
    int currentIndex = 0, arrayBegin = 0, arrayEnd = arrayLength;
    do
    {
        currentIndex = (arrayBegin + arrayEnd) / 2;
        if (__array[currentIndex] == __target)
           return currentIndex;
        else if (__array[currentIndex]> __target)
           arrayEnd =currentIndex - 1;
        else if (__array[currentIndex] <__target)
           arrayBegin =currentIndex + 1;
    } while (arrayEnd> arrayBegin);
    return -1;}

مرتب‌سازی شل

(به انگلیسی: Shell Sort)
نام این الگوریتم از نام مخترع آن گرفته شده‌است. در این الگوریتم از روش درج استفاده می‌شود.
به عنوان مثال رشته f d a c b e را تحت این الگوریتم مرتب می‌کنیم.
  F d a c b e: شروع

  C b a f d e: مرحله اول

  A b c d e f: مرحله دوم

  A b c d e f: نتیجه
در مرحله اول: داده‌های با فاصله ۳ از یکدیگر، مقایسه و مرتب شده، در مرحله دوم داده‌های با فاصله ۲ از یکدیگر، مقایسه و مرتب می‌شوند و در مرحله سوم داده‌ها با فاصلهٔ یک از یکدیگر مقایسه و مرتب می‌شوند.
منظور از فاصلهٔ سه این است که عنصر اول با عنصر چهارم(۳+۱)، عنصر دوم با عنصر پنجم(۵=۳+۲) و عنصر سوم با عنصر ششم(۶=۳+۳) مقایسه شده در جای مناسب خود قرار می‌گیرد.
برای انتخاب فاصله در اولین مرحله، تعداد عناصر لیست بر ۲ تقسیم می‌شود(n/۲) وفاصله بعدی نیز با تقسیم فاصله فعلی بر ۲ حاصل می‌گردد و الگریتم تا زمانی ادامه پیدا می‌کند که این فاصله به صفر برسد.
برای نمونه اگر تعداد عناصر برابر با ۱۰ باشد، فاصله در مرحله اول برابر با ۵، در مرحله دوم برابر با ۲ ور در مرحله سوم برابر با ۱ و در نهایت برابر با صفر خواهد بود.
زمان مرتب‌سازی shell از رابطه n پیروی می‌کند که نسبت به n^۲ بهبود خوبی پیدا کرده‌است لذا سرعت عمل روش مرتب‌سازی شل از روش‌های انتخابی، در جی و حبابی بیشتر است.

پیاده‌سازی مرتب‌سازی شل در c++

# include<stdio.h># include<conio.h><include<string.h#Void shell(int *, char*, int)Int main(){

  Char s[80];

  Int gap[80];

  (); Clrscr

  ;(": Printf(" Enter a string

 ); Gets(s

 )); Shell(gap,s,strlen(s

 ); Printf("\n the sorted string is: %s" ,s

  Getch();

  Return ۰;}**************************** //Void shell(int gap [], char * item, int count){

  Register int I, j, step, k, p;

  ; Char x

  Gap[۰] =count /2;

  While(gap[k]> 1){

 ++; KGap[k]=gap[k-1]/2;}//end of whileFor (i= ۰; i<=k; i++){Step=gap[i];For(j=step; j<count; j++){X=item[j];P=j-step;

  While(p>=0 && x<item[p]){Item[p+step]=item[p];P=p-step;}Item[p+step]=x;

  }}}

مرتب‌سازی سریع

(به انگلیسی: Quick Sort)
مرتب‌سازی سریع (Quick Sort) از جمله روش‌های محبوب و با سرعت بالا برای مرتب کردن داده‌ها محسوب می‌شود. این روش هم مثل روش ادغام از تقسیم و حل (Divide and Conqure) برای مرتب کردن داده‌ها استفاده می‌کند. به این ترتیب که داده‌ها رو به دو قسمت مجزا تقسیم، و با مرتب کردن آن‌ها کل داده‌ها را مرتب می‌کند. برای این کار یکی از داده‌ها (مثلاً داده اول) به عنوان محور انتخاب می‌شود. داده‌ها بر اساس محور طوری چینش می‌شوند که همه داده‌های کوچک‌تر از محور در سمت چپ و کلیه داده‌های بزرگ‌تر یا مساوی محور، در سمت محور قرار می‌گیرند. با مرتب کردن دو قسمت به دست آمده کل داده‌ها مرتب می‌شوند. در این حالت، مثل روش ادغام نیازی به ادغام کردن داده‌ها نیست. چرا که قسمت سمت راست همگی از قسمت سمت چپ کوچک‌تر هستند و بالعکس. مثلاً اعداد صحیح زیر را در نظر بگیرید:
۵  ۶  ۱  ۹  -۲  ۴  ۵  ۱۵  ۳  ۱  ۴  ۱۰
عدد ۵ را به عنوان محور در نظر می‌گیریم. داده‌ها به این صورت بازچینی می‌گردند:
۱  -۲  ۴  ۳  ۱  ۴  ۵  ۶  ۹  ۵  ۱۵  ۱۰
همان‌طور که مشاهده می‌کنید اعداد سمت چپ عدد ۵ زیر خط دار همگی از ۵ کوچکتر و اعداد سمت راست بزرگ‌تر یا مساوی آن هستند.

پیاده‌سازی مرتب‌سازی Quick sort در c++

تابع partition با دریافت آرایه و حد بالا و پایین تکه‌ای که باید تقسیم بشود عملیات لازم را انجام می‌دهد، و اندیس محل تفکیک را (محل عدد ۵ در مثال بالا) به عنوان نتیجه برمی‌گرداند.
int partition (int arr[ ] , int low , int high){

 int lb = low + 1 , rb = high , temp , pivot = arr[ low ] , p;

 while (lb <= rb)

 {

  while (arr[ lb ] <= pivot && lb <= rb)

   lb++;

  while (arr[ rb ]> pivot && lb <= rb)

   rb--;

  if (lb <rb)

  {

   temp = arr[ lb];

   arr[ lb ] = arr[ rb];

   arr[ rb ] = temp;

 }}

 (if (rb == high

  p = high;else if(rb == low)

  p = low;

 else

  p = lb – 1;

 arr[ low ] = arr[ p];

 arr[ p ] = pivot;

 return p;}
اگر این تابع رو برای مثال بالا استفاده کنیم؛ مقدار ۶ (اندیس ۵ زیرخط دار) برگشت داده می‌شود. با تکرار کردن این عملیات برای دو قسمت به دست آمده، (در مثال بالا از اندیس صفر تا ۵ و از اندیس ۷ تا ۱۱) داده‌ها به صورت کامل مرتب می‌شوند.
بر اساس گفته‌های بالا تابع مرتب‌سازی به این صورت خواهد بود:
void quick_sort (int arr[ ] , int low , int high){if (low <high)

 {

  int p = partition(arr , low , high);

  quick_sort(arr , low , p – 1);

  quick_sort(arr , p + 1 , high);}}
همان‌طور که مشاهده می‌کنید این تابع به صورت بازگشتی نوشته شده‌است. در صورتی که بخواهید آن را به صورت غیر بازگشتی بنویسید باید از پشته به صورت زیر استفاده کنید:
void quick_sort (int arr[ ] ,int n){

 stack st;

 st.push(0);

 st.push(n – 1);

 int low , p , high;

 while(! st.isempty())

 {

  high = st.pop();

  low = st.pop();

  p = partition(arr , low , high);

  if (p> low)

  {

   st.push(low);

   st.push(p – 1);

 }

  if (p <high)

 {

   st.push(p + 1);

   st.push(high);

 }}}

مرتب‌سازی ادغامی

(به انگلیسی: Merge Sort)
روش مرتب‌سازی ادغامی از الگوریتم تقسیم و حل (divide-and-conquer) و همچنین ادغام برای مرتب کردن داده‌ها استفاده می‌کند. در این الگوریتم مسئله به چند جزء کوچک‌تر تقسیم می‌شود. هر کدوم از این قسمت‌ها رو به‌طور مجزا حل کرده، و با ترکیب اون‌ها به مسئله اصلی می‌رسیم؛ و اما طرح کلی مرتب‌سازی ادغام:
در این روش داده‌ها به دو قسمت مساوی تقسیم می‌شوند؛ و هر کدوم از این قسمت‌ها - به صورت بازگشتی - مرتب، و با ادغام آن‌ها دادها به صورت کامل مرتب می‌شوند. اما توجه به این نکته ضروری است که اگر پس از یک بار تقسیم باز هم لیستهای ایجاد شده بزرگ باشند، می‌توانیم برای هر زیر لیست مراحل بالا را دوباره انجام دهیم تا به زیر لیستهایی با تنها ۱ عضو برسیم و واضح است که لیست تک عنصری خود مرتب است.
کلاس merge sort در #C که وظیفهٔ آن مرتب‌سازی n کلید به ترتیب غیر نزولی است:
   class margesort
    {
        public void marge_sort(ref int[] arr, int low, int high)
        {
            if (low <high)
            {
                int mid = (low + high) / 2;
                marge_sort(ref arr, low, mid);
                marge_sort(ref arr, mid + 1 , high);
                merge(ref arr, low, mid, high);
           }
       }
        void merge(ref int[] arr, int low, int mid, int high)
        {
            int i, j, a, b;
            j = low;
            for (i = mid + 1; i <= high; i++)
            {
                for (; arr[j] <= arr[i] && j <i; j++) ;
                if (j!= i)
                {
                    b = arr[i];
                    for (a = i; a> j; a--)
                        arr[a] = arr[a - 1];
                    arr[j] = b;
               }
                else
                    break;
           }

       }
   }
پیاده‌سازی مرتب‌سازی Merge sort در ++C:
void merge_sort (int arr[ ] , int low , int high){

 if (low>= high)
  return;

 int mid = (low + high) / 2;

 merge_sort (arr , low , mid);

 merge_sort (arr , mid + 1 , high);

 merge_array (arr , low , mid , high);}procedure merge_sort (var arr: array of integer ; l: integer ; h: integer);var

 m: integer;begin

 if l>= h then

  exit;

 m:= (l + h) div 2;

 merge_sort (arr , l , m);

 merge_sort (arr , m + 1 , h);

 merge_array (arr , l , m , h);end;
void merge (int arr[ ] , int low , int mid , int high){

 register int i , j , k , t;

 j = low;

 for (i = mid + 1 ; i <= high ; i++)

 {

  while (arr[ j ] <= arr[ i ] && j <i)

   j++;

  if (j == i)

   break;

  t = arr[ i];

  for (k = i ; k> j ; k--)

   arr[ k ] = arr[ k – 1];

  arr[ j ] = t;}}procedure merge_array (var arr: array of integer ; l: integer ; m: integer ; h: integer);var

 i , j , k , t: integer;begin

 j:= l;

 for i:= m + 1 to h do

 begin

  while (arr[ j ] <= arr[ i ]) and (j <i) do

   inc (j);

  if j = i then

   break;

  t:= arr[ i];

  for k:= i downto j + 1 do

   arr[ k ]:= arr[ k – 1];

  arr[ j ]:= t;

 end;End;
تابع merge_array خود آرایه و اندیس‌های بالا، پایین و جداکننده زیر آرایه‌ای را که باید ادغام بشود دریافت می‌کند، و به صورت درجا (بدون استفاده از آرایه کمکی) دو قمست مرتب شده زیر آرایه رو ادغام می‌کند.
مثالی در این مورد را مشاهده می‌کنید:

مرتب‌سازی بوگو

(به انگلیسی: Bugo Sort)
در علوم کامپیوتر، مرتب‌سازی بوگو (که به آن مرتب‌سازی تصادفی، مرتب‌سازی میمونی هم می‌گویند) یک روش غیر مؤثر در الگوریتم‌های مرتب‌سازی محسوب می‌شود. از این مرتب‌سازی برای اهداف آموزشی در تقابل با دیگر روش‌های واقع گرایانه در مرتب‌سازی به کار می‌رود. اگر در مرتب‌سازی دسته‌ای از کارت‌ها از مرتب‌سازی بوگو استفاده شود، بدین صورت خواهد بود که ابتدا بررسی می‌شود که آیا لیست مرتب است یا خیر. اگر نبود، دوباره ترتیب کارت‌ها را تغییر می‌دهیم، و پردازه قبلی را دوباره اجرا می‌کنیم تا زمانی که به یک لیست مرتب از کارت‌ها برسیم. نام این مرتب‌سازی از واژه bogus به معنای جعلی و ساختگی آمده‌است.
شبه کد الگوریتم این مرتب‌سازی به صورت زیر است:
while not InOrder(deck)
  do Shuffle(deck);
زمان اجرای این مرتب‌سازی با فاکتوریل و توابع فرانمایی super-exponential می‌توان بیان کرد. نکته مهم در این است که بدترین حالت ممکن آن می‌تواند زمان اجرای بی‌نهایت داشته باشد.

زمان اجرا در بدترین حالت در بی‌نهایت، در حالت میانگین در (O(n!.n و در حالت کمینه در (O(n خواهد بود.