برای روشن کردن ترمینال، کلیک کن یا هر کلیدی را بزن

پرش به محتوای اصلی
>_
درس ۲۱متوسط

مرتب‌سازی: Bubble Sort و Selection Sort

دو الگوریتم کلاسیک مرتب‌سازی آرایه‌ها به همراه پیاده‌سازی صحیح و تحلیل پیچیدگی

معمولی
آرایه‌ها و رشته‌ها•صفحات ۳۳-۳۴•۳۵ دقیقه•۱٬۵۲۰ کلمهزمان مطالعه: ۰ دقیقه
فهرست این درس
// اهداف۵
  • درک مفهوم مرتب‌سازی و تفاوت روش‌های صعودی و نزولی
  • پیاده‌سازی صحیح الگوریتم Bubble Sort با جلوگیری از خطای خارج از محدوده
  • پیاده‌سازی الگوریتم Selection Sort برای مرتب‌سازی صعودی
  • تحلیل پیچیدگی زمانی هر دو الگوریتم
  • مقایسه مدیریت رشته‌ها در زبان C و ⁦C++⁩

مقدمه‌ای بر مرتب‌سازی

الگوریتم‌های مرتب‌سازی (Sorting) از پرکاربردترین الگوریتم‌ها در علوم کامپیوتر هستند. هدف آن‌ها چینش عناصر یک آرایه به ترتیب صعودی (کوچک به بزرگ) یا نزولی (بزرگ به کوچک) است.

در این درس دو الگوریتم کلاسیک و ساده را بررسی می‌کنیم:

  • Bubble Sort (مرتب‌سازی حبابی): در هر مرحله، بزرگ‌ترین عنصر باقیمانده مانند حباب به انتهای آرایه می‌رود.
  • Selection Sort (مرتب‌سازی انتخابی): در هر مرحله، کوچک‌ترین (یا بزرگ‌ترین) عنصر باقیمانده پیدا شده و در جای مناسب قرار می‌گیرد.

مثال عددی: ورودی 12 10 70 1 5 → خروجی مرتب صعودی: 1 5 10 12 70.

الگوریتم مرتب‌سازی حبابی (Bubble Sort)

در Bubble Sort، آرایه در چند مرحله پیمایش می‌شود. در هر مرحله، جفت‌های مجاور از ابتدا تا انتهای بخش نامرتب مقایسه شده و اگر عنصر اول بزرگ‌تر از عنصر دوم بود، جای آن‌ها عوض می‌شود.

پس از پایان هر مرحله، بزرگ‌ترین عنصر باقیمانده در انتهای آرایه قرار می‌گیرد و در مرحله بعدی نیازی به بررسی آن نیست.

ویژگی‌ها:

  • ساده‌ترین الگوریتم مرتب‌سازی برای آموزش
  • پایدار (Stable) است: ترتیب عناصر برابر حفظ می‌شود
  • درجا (In-place) است: به حافظه اضافی نیاز ندارد

پیاده‌سازی Bubble Sort (اصلاح‌شده)

مرتب‌سازی صعودی آرایه با Bubble Sortcpp

نمونه اجرا

خروجی
ورودی:
5
12 10 70 1 5

خروجی:
1 5 10 12 70

فلوچارت Bubble Sort

flow.svg

تفاوت سبکی حلقه داخلی: i > 0 یا i >= 0؟

در نسخه‌ی اولیه‌ی جزوه، حلقه بیرونی به‌صورت for(int i=n-1; i>=0; i--) و حلقه درونی for(int j=0; j<i; j++) نوشته شده بود. این کد از نظر کارکرد درست است: چون j < i، بزرگ‌ترین مقدار j برابر i-1 می‌شود و a[j+1] حداکثر به a[i] می‌رسد که همیشه داخل محدوده است — هیچ خطای Out of Bounds رخ نمی‌دهد. تنها اشکالش یک تکرار بی‌اثر است: در تکرار i=0 حلقه درونی اصلاً اجرا نمی‌شود. قرارداد تمیزتر این است که حلقه بیرونی تا i>0 برود تا آن تکرار بی‌فایده حذف شود و خواننده بلافاصله بفهمد در i=0 کاری انجام نمی‌شود.

نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاح‌شده و استاندارد
کد صحیحcpp

الگوریتم مرتب‌سازی انتخابی (Selection Sort)

در Selection Sort، آرایه به دو بخش تقسیم می‌شود: بخش مرتب (ابتدای آرایه) و بخش نامرتب (انتهای آرایه). در هر مرحله، کوچک‌ترین عنصر از بخش نامرتب پیدا شده و با اولین عنصر بخش نامرتب تعویض می‌شود.

پس از n-1 مرحله، کل آرایه مرتب می‌شود.

ویژگی‌ها:

  • تعداد تعویض‌ها کم است (حداکثر n-1 تعویض)
  • پایدار نیست: ممکن است ترتیب عناصر برابر به هم بریزد
  • درجا (In-place) است

پیاده‌سازی Selection Sort (اصلاح‌شده)

مرتب‌سازی صعودی با انتخاب کوچک‌ترین عنصرcpp

فلوچارت Selection Sort

flow.svg

اصلاح منطق Selection Sort در جزوه

کد اصلی جزوه به دنبال بزرگ‌ترین عنصر (با شرط a[j] > a[m]) می‌گشت و سپس آن را با a[i-1] تعویض می‌کرد؛ یعنی در واقع آرایه را به‌صورت نزولی مرتب نمی‌کرد چون بزرگ‌ترین را به انتها می‌برد و حلقه بیرونی از i=n شروع می‌شد که از محدوده خارج است. همچنین در تصویر اصلی به‌جای a[m] اشتباهاً a] تایپ شده بود.

نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاح‌شده و استاندارد
کد صحیحcpp

تحلیل پیچیدگی زمانی (Time Complexity)

هر دو الگوریتم Bubble Sort و Selection Sort دارای پیچیدگی زمانی O(n²) در حالت میانگین و بدترین حالت هستند.

الگوریتمبهترین حالتحالت میانگینبدترین حالتحافظه
Bubble SortO(n)O(n²)O(n²)O(1)
Selection SortO(n²)O(n²)O(n²)O(1)
  • بهترین حالت Bubble Sort وقتی رخ می‌دهد که آرایه از قبل مرتب باشد و از بهینه‌سازی «بدون تعویض = پایان زودهنگام» استفاده کنیم.
  • Selection Sort همیشه تعداد مقایسه‌های ثابت n(n-1)/2 دارد ولی تعداد تعویض‌های آن کم است.

برای داده‌های بزرگ، الگوریتم‌های سریع‌تری مانند Quick Sort (میانگین O(n log n)) یا Merge Sort (O(n log n) تضمینی) پیشنهاد می‌شوند.

مقایسه مدیریت رشته‌ها در C و ⁦C++⁩

در جزوه (صفحه ۳۳) جدولی برای مقایسه دو سبک مدیریت رشته آورده شده است. در زبان C از آرایه کاراکتری استفاده می‌شود، اما در ⁦C++⁩ کلاس std::string امکانات بسیار بیشتری می‌دهد.

موضوعزبان C (char[])⁦C++⁩ (std::string)
تعریفchar ch[100];string s;
ورودی سادهcin >> ch;cin >> s;
ورودی با فاصلهcin.getline(ch, 100);getline(cin, s);
طول رشتهstrlen(ch);s.length();
خروجیcout << ch;cout << s;
انتسابstrcpy(ch2, ch1);s2 = s1;
الحاقstrcat(ch1, ch2);s2 = s2 + s1;
مقایسه برابریstrcmp(ch1, ch2) == 0s1 == s2

نکته: در ⁦C++⁩ مدرن، عملگر‌های ==, <, > و + روی std::string به‌درستی کار می‌کنند و نیازی به توابع string.h نیست.

جلوگیری از VLA: استفاده از std::vector

در جزوه از int a[n]; استفاده شده که با n متغیر است. این قابلیت Variable Length Array (VLA) در استاندارد ISO ⁦C++⁩ مجاز نیست (هرچند GCC آن را پشتیبانی می‌کند).

به جای آن، از std::vector استفاده کنید:

main.cppcpp

مزیت‌ها: مدیریت خودکار حافظه، امکان تغییر اندازه، عدم خطر نشت حافظه.

هدر <utility> برای تابع swap

تابع swap در ⁦C++⁩ مدرن در هدر <utility> تعریف شده است. در بسیاری از کامپایلر‌ها به‌صورت ضمنی از طریق <iostream> نیز قابل دسترس است، اما برای اطمینان کامل، در ابتدای برنامه‌ای که از swap استفاده می‌کند، این خط را اضافه کنید:

main.cppcpp

مرور سریع مطالب

۱۴ بخش در این درس

تمرین‌ها

01
تمرین•حل کامل دارد

برنامه‌ای بنویسید که n عدد را از ورودی بگیرد، آن‌ها را با **Bubble Sort** به‌صورت **نزولی** (بزرگ به کوچک) مرتب کند و سپس چاپ کند.

نمایش راهنما
کافی است جهت علامت مقایسه را از `>` به `<` تغییر دهید: `if (a[j] < a[j+1]) swap(...)`.
02
تمرین•حل کامل دارد

برنامه‌ای بنویسید که n عدد را بگیرد و با **Selection Sort** به‌صورت صعودی مرتب کند. سپس میانگین، کوچک‌ترین و بزرگ‌ترین عدد را چاپ کند.

نمایش راهنما
بعد از مرتب‌سازی، کوچک‌ترین در `a[0]` و بزرگ‌ترین در `a[n-1]` خواهد بود.
03
تمرین•حل کامل دارد

برنامه‌ای بنویسید که آرایه‌ای از n رشته (کلمه) را بگیرد و آن‌ها را بر اساس **طول** به‌صورت صعودی مرتب کند. اگر طول دو کلمه برابر بود، ترتیب الفبایی اعمال شود.

نمایش راهنما
از `vector<string>` و تابع عضو `.length()` استفاده کنید. مقایسه مستقیم `s1 < s2` ترتیب الفبایی می‌دهد.
04
تمرین•حل کامل دارد

بهینه‌سازی Bubble Sort: پرچمی به نام `swapped` اضافه کنید که اگر در یک مرحله کامل هیچ تعویضی انجام نشد، الگوریتم زودتر خاتمه یابد. پیاده‌سازی کنید.

نمایش راهنما
در ابتدای هر مرحله `swapped = false` و در صورت تعویض `swapped = true`. بعد از حلقه داخلی اگر `!swapped` بود، `break` کنید.
// نکات-کلیدی۶
  • 01Bubble Sort با مقایسه و تعویض جفت‌های مجاور کار می‌کند و بزرگ‌ترین عنصر را در هر مرحله به انتها می‌برد.
  • 02Selection Sort در هر مرحله کوچک‌ترین عنصر باقیمانده را پیدا کرده و در جای مناسب قرار می‌دهد.
  • 03هر دو الگوریتم پیچیدگی O(n²) دارند و برای داده‌های بزرگ مناسب نیستند.
  • 04در Bubble Sort نوشتن `i > 0` به‌جای `i >= 0` در حلقه بیرونی فقط تمیزتر است: تکرار `i=0` هیچ کاری انجام نمی‌دهد (حلقه داخلی اجرا نمی‌شود) و حذفش کد را روشن‌تر می‌کند — نه به‌خاطر خطای Out of Bounds، چون چنین خطایی در این الگو رخ نمی‌دهد.
  • 05به جای VLA مانند `int a[n]` از `std::vector<int>` یا تخصیص پویا استفاده کنید.
  • 06در ⁦C++⁩ مدرن از کلاس `std::string` به‌جای آرایه کاراکتری استفاده کنید تا عملگر‌های `=`, `+`, `==` پشتیبانی شوند.
گام بعدی پیشنهادی

توابع کتابخانه‌ای رشته C (string.h)

آشنایی با strlen، strcpy، strcat و strcmp به همراه اصلاح اشتباهات رایج جزوه

۳۰ دقیقهمتوسط۱۶ بخش

شروع درس

جستجوی سریع در درس‌ها

نام درس، موضوع یا کلمه کلیدی را بنویسید و با Enter به آن بروید