Sortowanie bąbelkowe porównuje sąsiednie elementy i zamienia je, jeśli stoją w złej kolejności; po każdym przebiegu największy element „wypływa” na koniec. Jest powolne, ale doskonale pokazuje, jak działa algorytm krok po kroku. Na lekcji uczniowie śledzą kolejne przebiegi, liczą porównania i zamiany i sprawdzają przypadki skrajne.
Scenariusz jest przeznaczony na 45 minut dla klasy po lekcjach o wyszukiwaniu w C++ i funkcjach. Ten sam algorytm w Pythonie omawia lekcja o sortowaniu bąbelkowym. Dane są wpisane w kodzie.
Przygotowanie stanowisk
Potrzebny jest kompilator C++ (na przykład g++ z MinGW-w64, Code::Blocks lub Dev-C++) i edytor tekstu. Rozpakuj pakiet z sekcji materiałów do osobnego folderu. W folderze projektu skompiluj program poleceniem g++ -std=c++17 -Wall start.cpp -o program, a potem uruchom go: ./program (Linux, macOS) albo program.exe (Windows). W środowisku z przyciskiem Build and Run (Code::Blocks) wystarczy otworzyć plik .cpp i je użyć. Rozwiązanie jest w pliku rozwiazanie.cpp, zadanie dla chętnych — w rozszerzenie.cpp.
Cele i plan lekcji
Po zajęciach uczeń potrafi:
- zapisać dwie zagnieżdżone pętle sortowania bąbelkowego i
swap; - prześledzić zawartość tablicy po każdym przebiegu;
- policzyć porównania i zamiany;
- dodać wczesne zakończenie, gdy w przebiegu nie było zamian;
- wskazać przypadek najgorszy (dane odwrócone) i najlepszy (posortowane).
| Czas | Praca na lekcji |
|---|---|
| 0–5 min | Problem: jak ustawić ludzi według wzrostu, porównując sąsiadów? |
| 5–15 min | Pętla w pętli, swap |
| 15–22 min | Liczniki i wczesne zakończenie |
| 22–37 min | Trzy zadania samodzielne |
| 37–42 min | Omówienie wyników |
| 42–45 min | Pytania kontrolne |
Jak to działa
W jednym przebiegu porównujemy pary sąsiednie v[i] i v[i + 1] i zamieniamy je (swap), jeśli v[i] > v[i + 1]. Po pierwszym przebiegu największy element jest na końcu, po drugim — dwa największe, więc w kolejnych przebiegach pętla wewnętrzna może być coraz krótsza (i < n - 1 - przebieg).
Wczesne zakończenie
Jeśli w całym przebiegu nie było żadnej zamiany, tablica jest już posortowana i można przerwać. Dla danych posortowanych wystarczy jeden przebieg.
Koszt
Bez wczesnego zakończenia liczba porównań to zawsze n(n−1)/2 (dla 5 elementów 10). Liczba zamian zależy od danych: dla posortowanych 0, dla ułożonych odwrotnie — również n(n−1)/2. Dlatego sortowanie bąbelkowe stosuje się głównie w nauce.
Zadania
Zadanie 1. Śledzenie. Dla {5, 1, 4, 2, 8} wypisz tablicę po każdym przebiegu oraz liczbę zamian, porównań i przebiegów.
Zadanie 2. Wczesne zakończenie. Posortuj te same dane bez wczesnego zakończenia i z nim; porównaj liczby porównań i przebiegów.
Zadanie 3. Przypadki skrajne. Sprawdź dane posortowane {1, 2, 3, 4, 5} i odwrócone {5, 4, 3, 2, 1}: ile zamian, porównań i przebiegów?
Dla chętnych: posortuj alfabetycznie pięć imion (string można porównywać operatorem >; plik rozszerzenie.cpp).
Rozwiązanie i wyniki
#include <iostream>
#include <vector>
using namespace std;
void wypisz(const vector<int>& v) {
for (int x : v) {
cout << x << " ";
}
cout << endl;
}
// Zwraca liczbe zamian; porownania i przebiegi przekazuje przez referencje.
int babelkowe(vector<int> v, int& porownania, int& przebiegi, bool pokaz, bool wczesnyKoniec) {
int zamiany = 0;
porownania = 0;
przebiegi = 0;
int n = static_cast<int>(v.size());
for (int przebieg = 0; przebieg < n - 1; przebieg++) {
bool bylaZamiana = false;
przebiegi++;
for (int i = 0; i < n - 1 - przebieg; i++) {
porownania++;
if (v[i] > v[i + 1]) {
swap(v[i], v[i + 1]);
zamiany++;
bylaZamiana = true;
}
}
if (pokaz) {
cout << "po przebiegu " << przebiegi << ": ";
wypisz(v);
}
if (wczesnyKoniec && !bylaZamiana) {
break;
}
}
return zamiany;
}
int main() {
int por;
int prz;
// Zadanie 1: sledzenie algorytmu
vector<int> dane = {5, 1, 4, 2, 8};
cout << "start: ";
wypisz(dane);
int z = babelkowe(dane, por, prz, true, true);
cout << "zamian: " << z << ", porownan: " << por << ", przebiegow: " << prz << endl;
// Zadanie 2: z wczesnym zakonczeniem i bez
z = babelkowe(dane, por, prz, false, false);
cout << "bez wczesnego konca: zamian " << z << ", porownan " << por << ", przebiegow " << prz << endl;
z = babelkowe(dane, por, prz, false, true);
cout << "z wczesnym koncem: zamian " << z << ", porownan " << por << ", przebiegow " << prz << endl;
// Zadanie 3: przypadki skrajne
vector<int> posortowane = {1, 2, 3, 4, 5};
z = babelkowe(posortowane, por, prz, false, true);
cout << "posortowane: zamian " << z << ", porownan " << por << ", przebiegow " << prz << endl;
vector<int> odwrotne = {5, 4, 3, 2, 1};
z = babelkowe(odwrotne, por, prz, false, true);
cout << "odwrotne: zamian " << z << ", porownan " << por << ", przebiegow " << prz << endl;
return 0;
}
Program wypisze:
start: 5 1 4 2 8
po przebiegu 1: 1 4 2 5 8
po przebiegu 2: 1 2 4 5 8
po przebiegu 3: 1 2 4 5 8
zamian: 4, porownan: 9, przebiegow: 3
bez wczesnego konca: zamian 4, porownan 10, przebiegow 4
z wczesnym koncem: zamian 4, porownan 9, przebiegow 3
posortowane: zamian 0, porownan 4, przebiegow 1
odwrotne: zamian 10, porownan 10, przebiegow 4
Zadanie dla chętnych (rozszerzenie.cpp):
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<string> imiona = {"Zofia", "Adam", "Marek", "Ola", "Ewa"};
int n = static_cast<int>(imiona.size());
for (int przebieg = 0; przebieg < n - 1; przebieg++) {
for (int i = 0; i < n - 1 - przebieg; i++) {
if (imiona[i] > imiona[i + 1]) {
swap(imiona[i], imiona[i + 1]);
}
}
}
for (const string& s : imiona) {
cout << s << " ";
}
cout << endl;
return 0;
}
Wynik:
Adam Ewa Marek Ola Zofia
Programy kompilowano kompilatorem g++ 15.2 z opcjami -std=c++17 -Wall -Wextra, bez ostrzeżeń.
Typowe błędy
| Objaw | Co sprawdzić |
|---|---|
| Pętla wychodzi poza tablicę | Wewnętrzna pętla musi kończyć się na n - 1 - przebieg, aby v[i + 1] istniało. |
Brak zamiany po swap | Funkcja przyjmuje tablicę przez wartość i sortuje kopię; użyj referencji, gdy ma zmienić oryginał. |
| Zła liczba przebiegów | Potrzeba najwyżej n - 1 przebiegów. |
| Wczesne zakończenie przerywa za wcześnie | Flagę bylaZamiana zeruj na początku każdego przebiegu. |
| Zamiana większych na mniejsze | Odwróć warunek: dla porządku rosnącego zamieniamy, gdy v[i] > v[i + 1]. |
warning: comparison of integer expressions of different signedness | Użyj zmiennych typu int albo size_t spójnie. |
Pytania kontrolne i punktacja
- Co jest na końcu tablicy po pierwszym przebiegu?
- Ile porównań wykona sortowanie bąbelkowe bez wczesnego zakończenia dla 5 elementów?
- Kiedy sortowanie wykona najwięcej zamian?
Odpowiedzi: Największy element. Dziesięć, bo n(n−1)/2 = 5·4/2. Gdy dane są ułożone odwrotnie (malejąco) — wtedy zamian jest tyle samo co porównań.
Proponowana punktacja (razem 10 punktów): zadanie 1 — przebiegi i liczniki 3 pkt; zadanie 2 — porównanie dwóch wersji 2 pkt; zadanie 3 — przypadki skrajne 3 pkt; pytania kontrolne 2 pkt. Zadanie dla chętnych nie jest wymagane do pełnej oceny.
Materiały do pobrania
Rozpakuj ZIP przed otwarciem plików. Materiały są dostępne bez logowania; publiczne rozwiązanie służy nauce i samodzielnemu sprawdzeniu, a nie poufnemu sprawdzianowi.
Co dalej?
Następny krok to liczby pierwsze i sito Eratostenesa. Ten materiał rozwija dział o algorytmach w kursie C++ w TablicaPro, gdzie znajdziesz prezentacje, ćwiczenia i testy do prowadzenia lekcji.
Źródła techniczne: Sortowanie bąbelkowe w Wikipedii oraz std::swap w cppreference.com. Zobacz też lekcję o wyszukiwaniu w C++.
Poprowadź kolejne lekcje C++
Pełny kurs: prezentacje, ćwiczenia i testy do pracy z klasą.
Kup dostęp do kursu C++ →