Implementacja algorytmu sortowania przez wstawianie w języku C++

1
Problem 1

Napisz program sortujący niemalejąco tablicę n liczb całkowitych. Wykorzystaj metodę sortowania przez wstawianie. Funkcja powinna sortować wejściową tablicę w miejscusortowanie w miejscuw miejscu. Przetestuj rozwiązanie dla danych:

Linia 1. int tab otwórz nawias kwadratowy zamknij nawias kwadratowy znak równości otwórz nawias klamrowy 15 przecinek 9 przecinek 7 przecinek minus 1 przecinek 1 przecinek 2 przecinek 3 przecinek 4 przecinek 8 przecinek 5 przecinek 7 przecinek 3 przecinek 3 przecinek 0 przecinek minus 3 zamknij nawias klamrowy. Linia 2. int n znak równości sizeof otwórz nawias okrągły tab zamknij nawias okrągły prawy ukośnik sizeof otwórz nawias okrągły tab otwórz nawias kwadratowy 0 zamknij nawias kwadratowy zamknij nawias okrągły.

Specyfikacja problemu:

Dane:

  • n – liczba naturalna

  • tab – tablica zawierająca n liczb całkowitych

Wynik:

  • posortowana niemalejąco tablica n liczb całkowitych tab

R1RP3GvjtdTAX
Wymyśl pytanie na kartkówkę związane z tematem materiału.
Polecenie 1

Porównaj swoje rozwiązanie z przedstawionym poniżej.

Naszym zadaniem jest napisanie programu, który posortuje podaną przez użytkownika tablicę n liczb naturalnych, używając algorytmu sortowania przez wstawianie.

Załączamy niezbędną bibliotekę i zapisujemy funkcję main().

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int main otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. zamknij nawias klamrowy.

Tworzymy cztery zmienne. Pierwsza z nich to zmienna tab, która jest tablicą przechowującą n liczb całkowitych. Druga to zmienna n przechowująca rozmiar tablicy tab. Kolejne dwie zmienne to zmienne pomocnicze pom oraz j.

W zmiennej pom będzie tymczasowo przechowywany analizowany element tablicy.

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int main otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. int tab otwórz nawias kwadratowy zamknij nawias kwadratowy znak równości otwórz nawias klamrowy 15 przecinek 9 przecinek 7 przecinek minus 1 przecinek 1 przecinek 2 przecinek 3 przecinek 4 przecinek 8 przecinek 5 przecinek 7 przecinek 3 przecinek 3 przecinek 0 przecinek minus 3 zamknij nawias klamrowy średnik. Linia 7. int n znak równości sizeof otwórz nawias okrągły tab zamknij nawias okrągły prawy ukośnik sizeof otwórz nawias okrągły tab otwórz nawias kwadratowy 0 zamknij nawias kwadratowy zamknij nawias okrągły średnik. Linia 8. int pom średnik. Linia 9. int j średnik. Linia 11. zamknij nawias klamrowy.

Aby posortować tablicę, konieczne jest przeiterowanie po jej elementach. W tym celu zapiszemy pętlę for, która wykona się o jeden raz mniej niż wynosi liczba elementów do posortowania. Zatem początkowa wartość zmiennej i wynosi 1. Pętla for wykonuje się, dopóki nie zostaną posortowane wszystkie elementy tablicy (czyli i < n).

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int main otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. int tab otwórz nawias kwadratowy zamknij nawias kwadratowy znak równości otwórz nawias klamrowy 15 przecinek 9 przecinek 7 przecinek minus 1 przecinek 1 przecinek 2 przecinek 3 przecinek 4 przecinek 8 przecinek 5 przecinek 7 przecinek 3 przecinek 3 przecinek 0 przecinek minus 3 zamknij nawias klamrowy średnik. Linia 7. int n znak równości sizeof otwórz nawias okrągły tab zamknij nawias okrągły prawy ukośnik sizeof otwórz nawias okrągły tab otwórz nawias kwadratowy 0 zamknij nawias kwadratowy zamknij nawias okrągły średnik. Linia 8. int pom średnik. Linia 9. int j średnik. Linia 11. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny n średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 13. zamknij nawias klamrowy. Linia 15. zamknij nawias klamrowy.

W pętli for zmiennej pom przypiszemy wartość elementu tablicy tab o indeksie i, natomiast wartość zmiennej j ustawimy na i - 1.

W zmiennej j będziemy przechowywać indeksy poszczególnych elementów znajdujących się w lewej części tablicy (czyli tych, które porównujemy z elementem przechowywanym w zmiennej pom).

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int main otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. int tab otwórz nawias kwadratowy zamknij nawias kwadratowy znak równości otwórz nawias klamrowy 15 przecinek 9 przecinek 7 przecinek minus 1 przecinek 1 przecinek 2 przecinek 3 przecinek 4 przecinek 8 przecinek 5 przecinek 7 przecinek 3 przecinek 3 przecinek 0 przecinek minus 3 zamknij nawias klamrowy średnik. Linia 7. int n znak równości sizeof otwórz nawias okrągły tab zamknij nawias okrągły prawy ukośnik sizeof otwórz nawias okrągły tab otwórz nawias kwadratowy 0 zamknij nawias kwadratowy zamknij nawias okrągły średnik. Linia 8. int pom średnik. Linia 9. int j średnik. Linia 11. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny n średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 13. pom znak równości tab otwórz nawias kwadratowy i zamknij nawias kwadratowy średnik. Linia 14. j znak równości i minus 1 średnik. Linia 16. zamknij nawias klamrowy. Linia 18. zamknij nawias klamrowy.

Następnie definiujemy pętlę wewnętrzną while, która będzie wykonywana, dopóki spełnione będą naraz dwa warunki (warunki połączone operatorem logicznym &&).

Pierwszy warunek to j >= 0. Oznacza porównanie wartości elementów wszystkich  tablicy z wartością przechowywaną przez zmienną pom.

W momencie, gdy warunek nie będzie już spełniony (j < 0), będzie to oznaczać, że każda liczba w posortowanej części tablicy została porównana z wartością zmiennej pom i była od niej mniejsza, czyli dotarliśmy na początek tablicy.

Drugi warunek (tab[j] > pom) sprawdza, czy element tablicy tab o indeksie j jest większy od elementu, którego wartość jest przechowywana w zmiennej pom. Jeśli warunki są spełnione, wykonywane jest przesunięcie elementu na miejsce j + 1.

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int main otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. int tab otwórz nawias kwadratowy zamknij nawias kwadratowy znak równości otwórz nawias klamrowy 15 przecinek 9 przecinek 7 przecinek minus 1 przecinek 1 przecinek 2 przecinek 3 przecinek 4 przecinek 8 przecinek 5 przecinek 7 przecinek 3 przecinek 3 przecinek 0 przecinek minus 3 zamknij nawias klamrowy średnik. Linia 7. int n znak równości sizeof otwórz nawias okrągły tab zamknij nawias okrągły prawy ukośnik sizeof otwórz nawias okrągły tab otwórz nawias kwadratowy 0 zamknij nawias kwadratowy zamknij nawias okrągły średnik. Linia 8. int pom średnik. Linia 9. int j średnik. Linia 11. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny n średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 13. pom znak równości tab otwórz nawias kwadratowy i zamknij nawias kwadratowy średnik. Linia 14. j znak równości i minus 1 średnik. Linia 16. while otwórz nawias okrągły j zamknij nawias ostrokątny znak równości 0 ampersant ampersant tablica otwórz nawias kwadratowy j zamknij nawias kwadratowy zamknij nawias ostrokątny a zamknij nawias okrągły otwórz nawias klamrowy. Linia 18. zamknij nawias klamrowy. Linia 20. zamknij nawias klamrowy. Linia 22. zamknij nawias klamrowy.

W pętli while przypisujemy elementowi tablicy tab o indeksie j + 1 wartość elementu o indeksie j. Wykonujemy tę operację, by mieć możliwość zapisania wartości wstawianego elementu do części posortowanej, nie tracąc danych. Pętla jest wykonywana do momentu odnalezienia odpowiedniego miejsca w tablicy dla wstawianego elementu lub do momentu, gdy natrafimy na początek tablicy (czyli j = 0).

Zmniejszamy wartość zmiennej j o jeden i przechodzimy do ponownego sprawdzenia warunku pętli.

Ostatnią operacją wykonywaną w pętli for jest podstawienie w miejsce zmiennej zapisanej pod indeksem j + 1 wartości zmiennej pomocniczej pom. W ten sposób wstawiamy obecnie sprawdzaną liczbę na odpowiednie miejsce.

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int main otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. int tab otwórz nawias kwadratowy zamknij nawias kwadratowy znak równości otwórz nawias klamrowy 15 przecinek 9 przecinek 7 przecinek minus 1 przecinek 1 przecinek 2 przecinek 3 przecinek 4 przecinek 8 przecinek 5 przecinek 7 przecinek 3 przecinek 3 przecinek 0 przecinek minus 3 zamknij nawias klamrowy średnik. Linia 7. int n znak równości sizeof otwórz nawias okrągły tab zamknij nawias okrągły prawy ukośnik sizeof otwórz nawias okrągły tab otwórz nawias kwadratowy 0 zamknij nawias kwadratowy zamknij nawias okrągły średnik. Linia 8. int pom średnik. Linia 9. int j średnik. Linia 11. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny n średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 13. pom znak równości tab otwórz nawias kwadratowy i zamknij nawias kwadratowy średnik. Linia 14. j znak równości i minus 1 średnik. Linia 16. while otwórz nawias okrągły j zamknij nawias ostrokątny znak równości 0 ampersant ampersant tab otwórz nawias kwadratowy j zamknij nawias kwadratowy zamknij nawias ostrokątny pom zamknij nawias okrągły otwórz nawias klamrowy. Linia 17. tab otwórz nawias kwadratowy j plus 1 zamknij nawias kwadratowy znak równości tab otwórz nawias kwadratowy j zamknij nawias kwadratowy średnik. Linia 18. minus minus j średnik. Linia 19. zamknij nawias klamrowy. Linia 20. tab otwórz nawias kwadratowy j plus 1 zamknij nawias kwadratowy znak równości pom średnik. Linia 21. zamknij nawias klamrowy. Linia 23. zamknij nawias klamrowy.

Tym samym posortowaliśmy tablicę liczb przez wstawianie. Teraz pozostaje wypisać posortowaną tablicę za pomocą pętli for.

Oto kod całego programu:

Linia 1. kratka include otwórz nawias ostrokątny iostream zamknij nawias ostrokątny. Linia 2. using namespace std średnik. Linia 4. int main otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. int tab otwórz nawias kwadratowy zamknij nawias kwadratowy znak równości otwórz nawias klamrowy 15 przecinek 9 przecinek 7 przecinek minus 1 przecinek 1 przecinek 2 przecinek 3 przecinek 4 przecinek 8 przecinek 5 przecinek 7 przecinek 3 przecinek 3 przecinek 0 przecinek minus 3 zamknij nawias klamrowy średnik. Linia 7. int n znak równości sizeof otwórz nawias okrągły tab zamknij nawias okrągły prawy ukośnik sizeof otwórz nawias okrągły tab otwórz nawias kwadratowy 0 zamknij nawias kwadratowy zamknij nawias okrągły średnik. Linia 8. int pom średnik. Linia 9. int j średnik. Linia 11. for otwórz nawias okrągły int i znak równości 1 średnik i otwórz nawias ostrokątny n średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 13. pom znak równości tab otwórz nawias kwadratowy i zamknij nawias kwadratowy średnik. Linia 14. j znak równości i minus 1 średnik. Linia 16. while otwórz nawias okrągły j zamknij nawias ostrokątny znak równości 0 ampersant ampersant tab otwórz nawias kwadratowy j zamknij nawias kwadratowy zamknij nawias ostrokątny pom zamknij nawias okrągły otwórz nawias klamrowy. Linia 17. tab otwórz nawias kwadratowy j plus 1 zamknij nawias kwadratowy znak równości tab otwórz nawias kwadratowy j zamknij nawias kwadratowy średnik. Linia 18. minus minus j średnik. Linia 19. zamknij nawias klamrowy. Linia 20. tab otwórz nawias kwadratowy j plus 1 zamknij nawias kwadratowy znak równości pom średnik. Linia 21. zamknij nawias klamrowy. Linia 23. for otwórz nawias okrągły int i znak równości 0 średnik i otwórz nawias ostrokątny n średnik i plus plus zamknij nawias okrągły. Linia 24. cout otwórz nawias ostrokątny otwórz nawias ostrokątny tab otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów cudzysłów średnik. Linia 25. zamknij nawias klamrowy.

Przetestujmy działanie programu dla przykładowych danych.

Wynik działania programu:

Linia 1. minus 3 minus 1 0 1 2 3 3 3 4 5 7 7 8 9 15.

Porównania

Liczba potrzebnych porównań elementów tablicy jest wyrażona wzorami:

  • przypadek optymistyczny: ,

  • przypadek pesymistyczny: .

Uwaga! Dwa porównania na początku pętli while liczymy jako jedno.

Już wiesz

Złożoność czasowa algorytmu dla przypadku typowego to O(n2).

W tabeli przedstawiono porównania.

n

optymistyczna liczba porównań

pesymistyczna liczba porównań

20

19

190

30

29

435

40

39

780

50

49

1225

60

59

1770

70

69

2415

80

79

3160

90

89

4005

100

99

4950

W tabeli przedstawiono przestawienia.

n

optymistyczna liczba przestawień

pesymistyczna liczba przestawień

20

19

209

30

29

464

40

39

819

50

49

1247

60

59

1829

70

69

2484

80

79

3239

90

89

4094

100

99

5049

RyflWAEXWWCOg
Ilustracja przedstawia wykres: Liczba porównań i przestawień w zależności od typu przypadku.
Źródło: Contentplus.pl Sp. z o.o., licencja: CC BY-SA 3.0.

Słownik

przypadek optymistyczny
przypadek optymistyczny

przypadek, w którym podczas sortowania zostanie wykonana najmniejsza możliwa liczba operacji; tablica uporządkowana w oczekiwanej kolejności - czyli dla sortowania w porządku rosnącym będzie to np. tablica {2, 3, 4, 5}

przypadek pesymistyczny
przypadek pesymistyczny

przypadek, w którym podczas sortowania zostanie wykonana największa możliwa liczba operacji; tablica uporządkowana w kolejności odwrotnej od oczekiwanej - czyli dla sortowania w porządku rosnącym będzie to np. tablica {5, 4, 3, 2, 1 }

sortowanie w miejscu
sortowanie w miejscu

inaczej in situ; sortowanie, w którym niezależnie od rozmiaru danych wejściowych potrzebna jest stała ilość pamięci komputera