Rok akademicki 2010/2011, Wykład nr 4 2/50 Plan wykładu nr 4 Informatyka 2 Politechnika Białostocka - Wydział Elektryczny Elektrotechnika, semestr III, studia niestacjonarne I stopnia Rok akademicki 2010/2011 Wykład nr 4 (10.12.2010) dr inż. Jarosław Forenc Dynamiczny przydział pamięci język C: funkcje calloc(), malloc() i free() język C++: operatory new i delete Dynamiczne struktury danych stos, kolejka, lista, drzewo Sortowanie Klasyfikacja algorytmów sortowania Algorytmy sortowania sortowanie przez proste wstawianie sortowanie przez proste wybieranie sortowanie bąbelkowe Rok akademicki 2010/2011, Wykład nr 4 3/50 Rok akademicki 2010/2011, Wykład nr 4 4/50 Dynamiczny przydział pamięci (język C) Dynamiczny przydział pamięci na tablicę dwuwymiarową (macierz): przydział pamięci na tablicę zawierającą N-wierszy i M-kolumn int tab[n][m]; Dynamiczny przydział pamięci (język C) Dynamiczny przydział pamięci na tablicę dwuwymiarową (macierz): zamiast standardowego odwołania do elementów macierzy: tab[i][j] stosujemy odwołania do odpowiednich elementów wektora: *(tab+i*m+j) lub tab[i*m+j] Metoda 1 (wektor N M-elementowy): przydzielamy pamięć na wektor N M-elementowy: int *tab; tab = (int*) calloc(n*m,sizeof(int)); /*... */ free(tab); po wykorzystaniu wektora, przydzieloną pamięć zwalniamy funkcją free() tab M Przykład: tablica tab, dla której N=3, M=4 [0][0] [0][1] [0][2] [0][3] [1][0] [1][1] [1][2] [1][3] [2][0] [2][1] [2][2] [2][3] Mi odwołujemy się do elementu tab[2][2]: *(tab+2*4+2) j
Rok akademicki 2010/2011, Wykład nr 4 5/50 Rok akademicki 2010/2011, Wykład nr 4 6/50 Przykład nr 1 - Dynamiczny przydział pamięci (1/2) Przykład nr 1 - Dynamiczny przydział pamięci (2/2) #include <stdio.h> #include <stdlib.h> #include <time.h> int main() int i, j, n, m, *tab; printf("podaj liczbe wierszy: "); scanf("%d",&n); printf("podaj liczbe kolumn: "); scanf("%d",&m); for (i=0; i<n; i++) for (j=0; j<m; j++) printf("%4d",tab[m*i+j]); printf("\n"); free(tab); return 0; Podaj liczbe wierszy: 4 Podaj liczbe kolumn: 6 4 5 63 38 63 44 35 26 44 72 49 7 63 29 53 31 57 71 72 75 85 54 76 42 tab = (int *) calloc(n*m,sizeof(int)); srand(time(null)); for (i=0; i<n; i++) for (j=0; j<m; j++) *(tab+m*i+j) = rand()%100; Rok akademicki 2010/2011, Wykład nr 4 7/50 Rok akademicki 2010/2011, Wykład nr 4 8/50 Dynamiczny przydział pamięci (język C) Metoda 2 (wskaźnik na tablicę wskaźników): przydzielamy pamięć na N-elementowy wektor wskaźników do typu int do kolejnych elementów tego wektora zapisujemy adresy M-elementowych wektorów liczb typu int (pamięć na wektory jest także przydzielana dynamicznie) Dynamiczny przydział pamięci (język C) Metoda 3 (wektor N M-elementowy + wektor wskaźników): przydzielamy pamięć na N-elementowy wektor wskaźników do typu int przydzielamy pamięć na N M-elementowy wektor elementów typu int, jego adres zapisujemy pod zerowym elementem wektora N-elementowego do pozostałych elementów wektora N-elementowego zapisujemy adresy pierwszych elementów kolejnych wierszy macierzy znajdujących się w wektorze N M-elementowym tab N odwołania do elementów takiej tablicy mają taką samą postać jak w przypadku zwykłych tablic: tab[i][j] M M... M
Rok akademicki 2010/2011, Wykład nr 4 9/50 Rok akademicki 2010/2011, Wykład nr 4 10/50 Dynamiczny przydział pamięci (język C++) Dynamiczny przydział pamięci (język C++) do dynamicznego przydziału pamięci w języku C++ stosowany jest operator new pamięć przydzielona operatorem new istnieje do zakończenia programu lub do chwili zwolnienia tej pamięci za pomocą operatora delete new typ delete ptr; operator ten alokuje obszar pamięci niezbędny dla przechowywania obiektu typu typ i zwraca wskaźnik na początek tego obszaru gdzie ptr jest wskaźnikiem wskazującym na obiekt stworzony przez new int *ptr; ptr = new int; przydział pamięci na tablicę n elementów typu typ: new typ[n] jeśli alokacja pamięci nie jest możliwa, to zwracana jest wartość NULL typ wskaźnika jest odpowiednio dopasowany do typu obiektu, więc nie są konieczne żadne konwersje wskaźnikowe wartością powyższego wyrażenia jest wskaźnik na typ (a nie na tablicę elementów typu typ) jako rozmiar tablicy n można podać dowolne wyrażenie Rok akademicki 2010/2011, Wykład nr 4 11/50 Rok akademicki 2010/2011, Wykład nr 4 12/50 Dynamiczny przydział pamięci (język C++) Przykład nr 2 - operatory new i delete (1/2) przydział pamięci na tablicę n-elementową typu int: int *ptr; ptr = new int[n]; usuwając tablicę, trzeba podać, że chodzi o tablicę, a nie o pojedynczy element: delete [] ptr; gdzie ptr jest wskaźnikiem zwróconym przez operator new podczas tworzenia dynamicznej tablicy niestety nie można w ten sposób tworzyć dynamicznych tablic wielowymiarowych #include <stdio.h> #include <stdlib.h> int main() int *tab, i, n; float suma = 0.0; printf("podaj ilosc liczb: "); scanf("%d",&n); tab = new int[n]; if (tab == NULL) printf("nie mozna przydzielic pamieci.\n"); exit(-1);
Rok akademicki 2010/2011, Wykład nr 4 13/50 Rok akademicki 2010/2011, Wykład nr 4 14/50 Przykład nr 2 - operatory new i delete (1/2) Przykład nr 2 - operatory new i delete (1/2) for (i=0; i<n; i++) /* wczytanie liczb */ printf("podaj liczbe nr %d: ",i+1); scanf("%d",&tab[i]); for (i=0; i<n; i++) suma = suma + tab[i]; Podaj ilosc liczb: 5 for (i=0; i<n; i++) /* wczytanie Podaj liczb liczbe */ nr 1: 1 Podaj liczbe nr 2: 2 printf("podaj liczbe nr %d: ",i+1); Podaj liczbe nr 3: 3 scanf("%d",&tab[i]); Podaj liczbe nr 4: 4 Podaj liczbe nr 5: 5 for (i=0; i<n; i++) Srednia 5 liczb wynosi 3.000000 suma = suma + tab[i]; printf("srednia %d liczb wynosi %f\n",n,suma/n); printf("srednia %d liczb wynosi %f\n",n,suma/n); delete [] tab; return 0; delete [] tab; return 0; Rok akademicki 2010/2011, Wykład nr 4 15/50 Rok akademicki 2010/2011, Wykład nr 4 16/50 Dynamiczne struktury danych Stos Dynamiczne struktury danych - struktury danych, którym pamięć jest przydzielana i zwalniana w trakcie wykonywania programu Podstawowe dynamicznych struktur danych: stos, kolejka lista (jednokierunkowa, dwukierunkowa, cykliczna: jedno- i dwukierunkowa) drzewo Elementy umieszczane w dynamicznych strukturach danych są najczęściej strukturami składającymi się z użytecznych danych (data) oraz z jednego lub kilku wskaźników (next) zawierających adresy innych elementów struct element typ data; struct element *next; ; data next stos (ang. stack) - struktur składająca się z elementów, z których każdy posiada tylko adres następnika dostęp do danych przechowywanych na stosie jest możliwy tylko w miejscu określanym mianem wierzchołka stosu (ang. top) wierzchołek stosu jest jedynym miejscem, do którego można dołączać lub z którego można usuwać elementy każdy składnik stosu posiada wyróżniony element (next) zawierający adres następnego elementu wskaźnik ostatniego elementu stosu wskazuje na adres pusty (NULL) podstawowe operacje na stosie to: dodanie elementu do stosu - funkcja push() zdjęcie elementu ze stosu - funkcja pop() tutaj dodajemy i usuwamy elementy data next data next data next NULL top wierzchołek
Rok akademicki 2010/2011, Wykład nr 4 17/50 Rok akademicki 2010/2011, Wykład nr 4 18/50 Zastosowania stosu - notacja polska Zastosowania stosu - odwrotna notacja polska notacja polska (zapis przedrostkowy, Notacja Łukasiewicza) jest to sposób zapisu wyrażeń arytmetycznych, podający najpierw operator, a następnie argumenty odwrotna notacja polska - ONP (ang. Reverse Polish Notation, RPN) jest sposobem zapisu wyrażeń arytmetycznych, w którym operator umieszczany jest po argumentach wyrażenie arytmetyczne: wyrażenie arytmetyczne: 4 / (1 + 3) (1 + 3) / 2 ma w notacji polskiej postać: ma w odwrotnej notacji polskiej postać: / 4 + 1 3 1 3 + 2 / wyrażenie w notacji polskiej nie wymaga nawiasów, ponieważ przypisanie argumentów do operatorów wynika wprost z ich kolejności w zapisie odwrotna notacja polska została opracowana przez australijskiego naukowca Charlesa Hamblina notacja polska była podstawą opracowania tzw. odwrotnej notacji polskiej Rok akademicki 2010/2011, Wykład nr 4 19/50 Rok akademicki 2010/2011, Wykład nr 4 20/50 Zastosowania stosu - odwrotna notacja polska obliczenie wartości wyrażenia przy zastosowaniu ONP wymaga: zamiany notacji konwencjonalnej (nawiasowej) na ONP obliczenia wartości wyrażenia arytmetycznego zapisanego w ONP oba powyższe algorytmy wykorzystują stos Zamiana wyrażenia z notacji konwencjonalnej na ONP: zamiana wykonywana jest przy zastosowaniu algorytmu Dijkstry nazywanego stacją rozrządową czytając wyrażenie arytmetyczne od strony lewej do strony prawej operatory odkładamy na stos a liczby na wyjście Zastosowania stosu - odwrotna notacja polska Reguły zamiany wyrażenia z notacji konwencjonalnej na ONP: operator odkładamy na stos tylko wtedy, jeśli ostatnim elementem stosu jest operator o niższym priorytecie jeżeli ma on wyższy lub równy priorytet to zdejmujemy ze stosu dotąd elementy i wysyłamy na wyjście, aż ostatni operator będzie miał niższy priorytet lub stos będzie pusty jeśli kolejnym elementem jest nawias otwierający (, to odkładamy go na stos, bez względu na to co znajduje się w danym momencie na stosie i bez względu na to czy stos jest pusty powyższy nawias traktujemy jak dno stosu i odczytujemy kolejne elementy wyrażenia według standardowego algorytmu jeśli dojdziemy do nawiasu zamykającego ), to zdejmujemy kolejne operatory ze stosu i wysyłamy na wyjście, aż dojdziemy do nawiasu otwierającego, który również zdejmujemy ze stosu jeśli dojdziemy do końca wyrażenia arytmetycznego, to zdejmujemy ze stosu pozostałe operatory i wysyłamy je na wyjście
Rok akademicki 2010/2011, Wykład nr 4 21/50 Rok akademicki 2010/2011, Wykład nr 4 22/50 Odwrotna notacja polska - przykład wyrażenie w notacji konwencjonalnej: (2+1)*3-4*(7+4) = -35 Krok Wejście Stos Wyjście 1 ( ( NULL 2 2 ( NULL 2 3 + + ( NULL 4 1 + ( NULL 1 5 ) NULL + 6 * * NULL 7 3 * NULL 3 8 - - NULL * 9 4 - NULL 4 10 * * - NULL 11 ( ( * - NULL 12 7 ( * - NULL 7 13 + + ( * - NULL 14 4 + ( * - NULL 4 15 ) * - NULL + 16 Koniec - NULL * 17 NULL - wyrażenie w ONP: 2 1 + 3 * 4 7 4 + * - Zastosowania stosu - odwrotna notacja polska Operacje wykonywane przy obliczaniu wartości wyrażenia w ONP: pobieramy kolejny element wyrażenia jeśli elementem jest liczba to odkładamy ją na stos jeśli elementem jest operator, to pobieramy ze stosu tyle liczb, aby można było zastosować operator na tych liczbach, np. dla dodawania, odejmowania, mnożenia i dzielenia są to dwie kolejne liczby wykonujemy operację na liczbach i jej wynik odkładamy na stos jeśli dotrzemy do końca wyrażenia, to pobieramy wynik ze stosu, który jest wartością wyrażenia arytmetycznego Uwaga: jeśli nie ma jeszcze końca, to wracamy na początek algorytmu jeśli np. stos ma postać: 2 4 NULL i mamy wykonać operację dzielenia /, to operacja ta ma postać: 4 / 2, czyli do wykonania operacji argumenty brane są w odwrotnej kolejności Rok akademicki 2010/2011, Wykład nr 4 23/50 Rok akademicki 2010/2011, Wykład nr 4 24/50 Odwrotna notacja polska - przykład Kolejka równanie w ONP: 2 1 + 3 * 4 7 4 + * - wynik: -35 Krok Wejście Stos Działanie 1 2 2 NULL 2 1 1 2 NULL 3 + 3 NULL 2 + 1 4 3 3 3 NULL 5 * 9 NULL 3 * 3 6 4 4 9 NULL 7 7 7 4 9 NULL 8 4 4 7 4 9 NULL 9 + 11 4 9 NULL 7 + 4 10 * 44 9 NULL 4 * 11 11 - -35 NULL 9 44 12 Koniec NULL wynik: -35 kolejka (ang. queue) - składa się z liniowo uporządkowanych elementów elementy dołączane są tylko na końcu kolejki (wkaźnik tail) elementy usuwane są tylko z początku kolejki (wskaźnik head) powiązanie między elementami kolejki jest takie samo, jak w stosie kolejka nazywana jest stosem FIFO (ang. First In First Out)
Rok akademicki 2010/2011, Wykład nr 4 25/50 Rok akademicki 2010/2011, Wykład nr 4 26/50 Lista jednokierunkowa Lista dwukierunkowa organizacja listy jednokierunkowej podobna jest do organizacji stosu i kolejki w liście dwukierunkowej każdy węzeł posiada adres następnika, jak i poprzednika dla każdego składnika (poza ostatnim) jest określony następny składnik (lub poprzedni - zależnie od implementacji) w strukturze tego typu wygodne jest przechodzenie pomiędzy elementami w obu kierunkach (od początku do końca i odwrotnie) zapamiętywany jest wskaźnik tylko na pierwszy element listy (first) lub wskaźniki na pierwszy (first) i ostatni element listy (last) elementy listy można dołączać/usuwać w dowolnym miejscu listy Rok akademicki 2010/2011, Wykład nr 4 27/50 Rok akademicki 2010/2011, Wykład nr 4 28/50 Lista cykliczna Drzewo lista cykliczna powstaje z listy jednokierunkowej lub dwukierunkowej, poprzez połączenie ostatniego element z pierwszym Jednokierunkowa: Dwukierunkowa: drzewo - najbardziej ogólna dynamiczna struktura danych, może być reprezentowane graficznie na różne sposoby na górze znajduje się korzeń drzewa (a) skojarzone z korzeniem poddrzewa połączone są z korzeniem liniami zwanymi gałęziami drzewa potomkiem węzła w nazywamy każdy, różny od w, węzeł należący do drzewa, w którym w jest korzeniem węzeł, który nie ma potomków, to liść drzewa
Rok akademicki 2010/2011, Wykład nr 4 29/50 Rok akademicki 2010/2011, Wykład nr 4 30/50 Drzewo binarne Binarne drzewo wyszukiwawcze drzewo binarne jest szczególnym przypadkiem ogólnej struktury zwanej drzewem każdy wierzchołek drzewa ma co najwyżej dwóch potomków jest to drzewo binarne, w którym dla każdego węzła w i : wszystkie klucze w lewym poddrzewie węzła w i są mniejsze od klucza w węźle w i wszystkie klucze w prawym poddrzewie węzła w i są większe od klucza w węźle w i największą zaletą takiej struktury jest szybkość wyszukiwania informacji Rok akademicki 2010/2011, Wykład nr 4 31/50 Rok akademicki 2010/2011, Wykład nr 4 32/50 Sortowanie Sortowanie sortowanie polega na uporządkowaniu zbioru danych względem pewnych cech charakterystycznych każdego elementu tego zbioru najczęstszym przypadkiem jest sortowanie względem wartości każdego elementu, np. sortowanie liczb, sortowanie słów w przypadku liczb, sortowanie polega na znalezieniu kolejności liczb zgodnej z relacją lub w przypadku słów (nazw) sortowanie polega na ustawieniu ich w porządku słownikowym (lub alfabetycznym) zwanym także leksykograficznym Przykład: tablica nieposortowana: Przykład: tablica nieposortowana: tablice posortowane: tablica posortowana zgodnie z relacją (od najmniejszej do największej liczby): tablica posortowana zgodnie z relacją (od największej do najmniejszej liczby): porównanie nazw może być sprowadzone do porównywania ich liczbowych kodów
Rok akademicki 2010/2011, Wykład nr 4 33/50 Rok akademicki 2010/2011, Wykład nr 4 34/50 Sortowanie w praktyce sortowanie sprowadza się do porządkowanie danych na podstawie porównania - element stosowany w porównaniu to klucz Przykład: tablica nieposortowana (imię, nazwisko, wiek): Sortowanie Po co stosować sortowanie? posortowane elementy można szybciej zlokalizować posortowane elementy można przedstawić w czytelniejszy sposób kolejność alfabetyczna nazwisk przedstawienie cen produktów od najniższej do najwyższej tablica posortowana (klucz - nazwisko): tablica posortowana (klucz - wiek): Klasyfikacje algorytmów sortowania złożoność obliczeniowa algorytmu - zależność liczby wykonywanych operacji w stosunku do liczebności sortowanego zbioru n oceniając wydajność algorytmu często analizuje się przypadki: najgorszy (złożoność pesymistyczna) najlepszy średni (złożoność średnia) Rok akademicki 2010/2011, Wykład nr 4 35/50 Rok akademicki 2010/2011, Wykład nr 4 36/50 Notacja O notacja O stosowana jest do porównywania wydajności algorytmów, wyraża złożoność matematyczną algorytmu w notacji tej po literze O występuje wyrażenie w nawiasach zawierające literę n oznaczającą liczbę elementów, na której działa algorytm do wyznaczenia złożoności obliczeniowej algorytmu przyjmuje się liczbę wykonywanych w nim elementarnych operacji, np. porównywanie Przykład: Notacja O porównanie najczęściej występujących złożoności: Elementy O(log n) O(n) O(n log n) O(n 2 ) O(2 n ) 10 3 10 33 100 1024 100 7 100 664 10 000 1,27 10 30 1 000 10 1 000 9 966 1 000 000 1,07 10 301 10 000 13 10 000 132 877 100 000 000 1,99 10 3010 O(n) O(n 2 ) - złożoność algorytmu jest prostą funkcją liczby elementów - (jeśli sortowanie 1000 elementów zajmuje 1 s, to sortowanie - (2000 elementów zajmie 2 s) - czas konieczny do wykonania algorytmu rośnie wraz z kwadratem liczby elementów O(log n) - złożoność logarytmiczna O(n) - złożoność liniowa O(n log n) - złożoność liniowo-logarytmiczna O(n 2 ) - złożoność kwadratowa O(2 n ) - złożoność wykładnicza
Rok akademicki 2010/2011, Wykład nr 4 37/50 Rok akademicki 2010/2011, Wykład nr 4 38/50 Klasyfikacje algorytmów sortowania Złożoność pamięciowa: Klasyfikacje algorytmów sortowania Stabilność algorytmu: jest to wielkość zasobów zajmowanych przez algorytm w algorytmach wykorzystujących technikę sortowania w miejscu wielkość zbioru danych podczas sortowania nie zmienia się (lub jest tylko nieco większa) algorytmy nie wykorzystujące techniki sortowania w miejscu wymagają podczas sortowania znaczniej więcej miejsca w pamięci komputera do przechowywania danych Sortowanie zewnętrzne i wewnętrzne: algorytm jest nazywany stabilnym, jeśli podczas sortowania zachowuje kolejność występowania elementów o tym samym kluczu, np. tablica nieposortowana (imię, nazwisko, wiek): tablica posortowana algorytmem stabilnym (klucz - wiek): sortowanie zewnętrzne - jest stosowane, gdy nie jest możliwe jednoczesne umieszczenie wszystkich elementów zbioru sortowanego w pamięci komputera (np. sortowanie plików) tablica posortowana algorytmem niestabilnym (klucz - wiek): sortowanie wewnętrzne - odbywa się w pamięci komputera Rok akademicki 2010/2011, Wykład nr 4 39/50 Rok akademicki 2010/2011, Wykład nr 4 40/50 Sortowanie przez proste wstawianie Sortowanie przez proste wstawianie sortowanie przez proste wstawianie (ang. insertion sort) - polega na pobieraniu kolejnego elementu z danych wejściowych i wstawianiu go na odpowiednie miejsce w wynikach Algorytm metody: elementy są umownie podzielone na ciąg wynikowy: a 1,a 2,,a i-1 i ciąg źródłowy: a i,a i+1,,a n w i-tym kroku pobieramy element z ciągu źródłowego a i porównujemy ten element z kolejnymi elementami z ciągu wynikowego: a i-1,a i-2, porównywanie kończymy, gdy porównywany element jest mniejszy lub równy elementowi a i lub dojdziemy do początku ciągu wynikowego wstawiamy element a i w odpowiednim miejscu ciągu wynikowego Przykład: Funkcja w języku C: void InsertionSort(int tab[]) int i,j,tmp; for (i=1; i<n; i++) j=i; tmp=tab[i]; while (tab[j-1]>tmp && j>0) tab[j]=tab[j-1]; j--; tab[j]=tmp;
Rok akademicki 2010/2011, Wykład nr 4 41/50 Rok akademicki 2010/2011, Wykład nr 4 42/50 Sortowanie przez proste wstawianie Uwagi: złożoność algorytmu: O(n 2 ) w najgorszym przypadku każdy element powoduje jednokrotne przestawienie wszystkich poprzedzających go elementów, a wtedy liczba porównań wynosi: n (n-1) liczba porównań zależna jest od początkowego rozmieszczenia elementów w tablicy + wydajny dla danych wstępnie posortowanych + wydajny dla zbiorów o niewielkiej liczebności + małe zasoby zajmowane podczas pracy (sortowanie w miejscu) + stabilny - zachowuje oryginalną kolejność takich samych elementów + prosty w implementacji Sortowanie przez proste wybieranie sortowanie przez proste wybieranie (ang. selection sort) - polega na wyszukaniu elementu, który ma się znaleźć na zadanej pozycji i na zamianie miejscami z tym elementem, który jest tam obecnie Algorytm metody: zaczynając od elementu pierwszego, szukamy w tablicy elementu o najmniejszej wartości i zamieniamy go miejscami z pierwszym elementem następnie szukamy elementu najmniejszego zaczynając od drugiego elementu i zamieniamy go z drugim elementem powtarzamy powyższe operacje do momentu, aż w tablicy pozostanie tylko jeden element mała efektywność dla normalnej i dużej ilości danych Rok akademicki 2010/2011, Wykład nr 4 43/50 Rok akademicki 2010/2011, Wykład nr 4 44/50 Sortowanie przez proste wybieranie Przykład: Funkcja w języku C: void SelectionSort(int tab[]) int i,j,k,tmp; for (i=0;i<n-1;i++) k=i; for (j=i+1; j<n; j++) if (tab[k]>=tab[j]) k = j; tmp = tab[i]; tab[i] = tab[k]; tab[k] = tmp; Sortowanie przez proste wybieranie Uwagi: złożoność algorytmu: O(n 2 ) + szybki w sortowaniu niewielkich tablic + małe zasoby zajmowane podczas pracy (sortowanie w miejscu) + prosty w implementacji liczba porównań elementów jest niezależna od początkowego rozmieszczenia elementów w tablicy (takie zachowanie algorytmu nazywane jest neutralnym lub niewrażliwym) w algorytmie może zdarzyć się, że wykonywana jest zamiana tego samego elementu ze sobą (sytuacja taka występuje, gdy najmniejszy element jest na pierwszej pozycji)
Rok akademicki 2010/2011, Wykład nr 4 45/50 Rok akademicki 2010/2011, Wykład nr 4 46/50 Sortowanie bąbelkowe Sortowanie bąbelkowe sortowanie bąbelkowe (ang. bubble sort), nazywane jest także: sortowaniem pęcherzykowym sortowaniem przez prostą zamianę (ang. straight exchange) metoda ta polega na porównywaniu dwóch kolejnych elementów i zamianie ich kolejności jeśli jest to konieczne nazwa metody wzięła się stąd, że kolejne porównania powodują wypychanie kolejnego największego elementu na koniec ( wypłynięcie największego bąbelka ) Algorytm metody: porównujemy pierwszy i drugi element tabeli i jeśli trzeba to zamieniamy je miejscami, następnie porównujemy drugi i trzeci element i jeśli jest to konieczne, to zamieniamy je miejscami, itd. powyższe operacje wykonujemy, aż dojdziemy do końca tabeli następnie ponownie rozpoczynamy porównywanie elementów od początku tabeli (element pierwszy z drugim, drugi z trzecim, itd. aż dojdziemy do końca tabeli) sortowanie kończymy, gdy podczas kolejnego przejścia przez całą tabelę nie wykonana zostanie żadna zamiana elementów Rok akademicki 2010/2011, Wykład nr 4 47/50 Rok akademicki 2010/2011, Wykład nr 4 48/50 Sortowanie bąbelkowe - przykład Sortowanie bąbelkowe Funkcja w języku C: void BubbleSort(int tab[]) int i,j,tmp,koniec; do koniec=0; for (i=0;i<n-1;i++) if (tab[i]>tab[i+1]) tmp=tab[i]; tab[i]=tab[i+1]; tab[i+1]=tmp; koniec=1; while (koniec);
Rok akademicki 2010/2011, Wykład nr 4 49/50 Rok akademicki 2010/2011, Wykład nr 4 50/50 Sortowanie bąbelkowe Koniec wykładu nr 4 Uwagi: złożoność algorytmu: O(n 2 ) + prosta realizacja + wysoka efektywność użycia pamięci (sortowanie w miejscu) + stabilny algorytm - zachowuje oryginalną kolejność takich samych elementów Dziękuję za uwagę! mała efektywność