Abstrakcyjne struktury danych w praktyce Wykład 13 7.4 notacja polska A.Szepietowski Matematyka dyskretna rozdział.8 stos kompilacja rozłączna szablony funkcji
Przypomnienie Drzewo binarne wyrażenia arytmetycznego Notacja polska Mówimy, że drzewo T(X) składa się z korzenia (wierzchołek X) oraz lewego i prawego poddrzewa. Lewe poddrzewo oznaczymy T(X) 0 Prawe poddrzewo oznaczymy T(X) 1 Sposoby reprezentowania wyrażeń arytmetycznych bez użycia nawiasów: postfiksowo: pierwszy argument -> drugi argument -> operacja lewy potomek -> prawy potomek -> operacja x 4 x y + * prefiksowo: operacja -> pierwszy argument -> drugi argument operacja -> lewy potomek -> prawy potomek Drzewo wyrażenia arytmetycznego oddaje logiczną strukturę i sposób obliczania danego wyrażenia. (x+4)*(2+y) * * - x 4 + x y infiksowo: pierwszy argument -> operacja -> drugi argument lewy potomek -> operacja-> prawy potomek x 4 * x + y - + Zadanie: Zbuduj drzewo podanego wyrażenia, podaj wszystkie jego beznawiasowe reprezentacje x 4 x y 2(a-3)/ (b+4) D. Makowiec: Programowanie dla I BioInf 2
Odwrotna notacja polska Odwrotna notacja polska: postfiksowa - najpierw dzieci potem ojciec Obliczenie wartości wyrażenia zadanego w postaci postfiksowej Dane : wyrażenie w postacie postfiksowej Zmienna pomocnicza : STOS, element wstaw pierwszy element wyrażenia na STOS dopóki nie osiągniesz końca wyrażenia pobierz kolejny element z wyrażenia jeżeli element jest stałą lub zmienną, to wstaw element na STOS jeżeli element jest znakiem operacji to zdejmij dwie wartości ze STOS-u wykonaj operacje na tych wartościach pamiętając o odwróceniu kolejności obliczoną wartość wstaw na STOS Przykład: wyrażenie w postaci postfiksowej: abc- * de-/ jeśli a=2,b=3, c=2, d=5,e=4 STOS = 2 STOS= 2,3 STOS= 2,3, 2 STOS= 2,1 STOS= 2 STOS= 2, 5 STOS= 2,5,4 STOS= 2,1 STOS= 2 D. Makowiec: Programowanie dla I BioInf 3
Drzewo poszukiwań binarnych Drzewo poszukiwań binarnych (Binary Search Tree) to drzewo binarne o następującej własności: dla dowolnego węzła X - lewe poddrzewo T(X) 0 zawiera tylko węzły o kluczach mniejszych niż dany węzeł; - prawe poddrzewo T(X) 1 zawiera tylko węzły o kluczach większych lub równych niż dany węzeł. 128 Drzewo poszukiwań binarnych: dla ciągu liczb : 128, 76,106 402 dla zbioru liczb : { 128, 76, 106, 402 76 402 Czy te reprezentacje są unikalne? 106 Algorytm wstawiania elementu e do BST T(X) Wstaw_element ( e, T(X) ) : jeżeli drzewo T(X) jest puste to key(x)=e inaczej jeżeli e < key(x) to Wstaw_element (e, T(X) 0 ) inaczej Wstaw_element ( e, T(X) 1 ) e wstawiany element T(X) drzewo o korzeniu X key(x) klucz przechowywany w X 4
Drzewo poszukiwań binarnych Drzewo poszukiwań binarnych dla ciągu liczb : 128, 76,106, 402, 100, 46, 354, 1018, 112, 28, 396, 35 128 76 402 46 106 354 1018 28 100 112 396 35 Zawartość drzewo poszukiwań binarnych opisana w porządku inorder: 28, 35, 46, 76, 100, 106, 112, 128, 354, 396, 402, 1018 5
implementacja stosu - tablicowa Operacje na stosie: dołożyć nowy element na stos zdjąć element ze stosu sprawdzić, co jest na stosie sprawdzić, ile jest elementów na stosie obsługa błędu StackSize(S) zwroć top[s] StackWhat(S) zwróć S[top[S]] Stos S: top[s] =5 13 44 8 9 10 1 2 3 4 5 POP( S) PUSH( S,x) Jeśli top[s] <= MAX top[s]=top[s]+1 S[top[S]]=x inaczej StackError(S) jeśli StackEmpty(S) to StackError(S) zwróć BŁĄD inaczej top[s]=top[s]-1 zwróć S[top[S]+1] MAX StackEmpty(S) jeśli top[s]=0 to PRAWDA inaczej FAŁSZ D. Makowiec: Programowanie dla I BioInf 6 2013-05-13
Implementacja STOSU jako tablicy Implementacja stosu przechowującego liczby int i realizowanego przez tablicę: C++ zachęca do lokowania funkcji składowych w oddzielnych plikach kodu źródłowego #define BLAD -32767 void push(int dane[], int N, int x); int pop(int dane[], int N); int stack_size(int dane[], int N); int stack_what (int dane[], int N); bool stack_empty(int dane[], int N); void stack_error(int dane[], int N); int main (){ const int MAX= 1000; int dane[max]; int top=0;.. return 0; void push (int dane[], int N, int x){. 7
Kompilacja rozłączna Zasady podziału: plik nagłówkowy zawierający deklaracje struktur wspolnych dla całego programu i prototypy funkcji korzystających z tych struktur stos.h plik kodu źródłowego z kodem funkcji zadeklarowanych w pliku nagłówkowym stos.cpp #include stos.h plik kodu źródłowego z kodem wykorzystującym funkcje zadeklarowane w pliku nagłówkowym test_stos.cpp #include stos.h 8
Dlaczego rozbijać??? Usprawnia zarządzanie programem Ochrania dane Ukrywa implementacje 9
Fragment kodu z implementacjami funkcji #include "stos.h" const int MAX=10; static int stos_error = 0; // flaga bledu static int data[max]; // STOS static int ptop; // licznik stosu void push(int x) { if (ptop < MAX) { data[ptop++] = x; stos_error = 0; else stos_error = 1; int pop(void) { if (ptop > 0) { int x = data[--ptop]; stos_error = 0; return x; else { stos_error = 1; return BLAD; Dane mogą być przechowywane w pamięci automatycznej w pamięci statycznej w pamięci dynamicznej Implementacja STOSU jako tablicy zmienne globalne: zmienne zdefiniowane poza wszelkimi funkcjami Zmienne zdefiniowane POZA funkcjami 1. Jeśli ze static to mają zasięg plikowy 2. Jeśli bez static, to mogą być wykorzystane w innych plikach ale poprzez deklarację extern 10
Plik nagłówkowy Technika unikania włączania tego samego pliku wielokrotnego nagłówkowego // stos.h: deklaracje funkcji dla stosu liczb int #ifndef STOS_H_ #define STOS_H #define BLAD -32767 void push(int); int pop(void); int top(void); int size(void); int error(void); #endif cmd.exe Stos.dev Etapy budowy kodu wynikowego: Preprocessing przetwarzanie wstępne. Przed właściwą kompilacją tekst programu jest przekszałcany zgodnie z dyrektywami programu. Przekształcenie jest czysto teksowe. Podstawowe dyrektywy to: #include włączyć plik #define zastąpić w tekście programu #ifndef kompilować warunkowo (1) preprocessing: przekszałcanie wstępne (2) kompilacja każdego pliku z osobna (3) konsolidacja skompilowanych plików 11
#define #define ILE 100 for ( i=0; i<ile; ++i) { #define BLAD przepelnienie stosu cout << BLAD; #define kwadrat(x) (x) * (x) double x=5; double y= kwadrat (x+2.0); #define DLA(I,N) for(i=0; I< N; ++I) w=1 ; DLA(i,N) w=2*w; w=1; DLA(i,N) w=(i+1)*w; DLA(i,I) DLA (j,j) tab[i][j]=0; 12
#ifndef / #ifdef #ifndef CECHA program_na_tak #else program_na_nie #endif Jeśli CECHA nie jest zdefiniowana w tym momencie to kompilowany jest program_na_tak #else jest opcjonalne Jeśli CECHA jest zdefiniowana to kompilowany jest program_na_nie #endif musi być 13
Parametry wywołania programu Przetwarzanie argumentów wiersza poleceń int main (int argc, char *argv[]) wskaźnik na tablicę znaków wskaźnik na argv[0] : nazwa uruchamianego programu argv[1] : pierwszy parametr wywołania argv[2] : pierwszy parametr wywołania argc : liczba parametrów podanych przy wywołaniu 14
namespace: przestrzeń nazw Przestrzeń nazw namespace to jeszcze jeden sposób na ustalanie zakresów nazw. Nazwy zadeklarowane w jednej przestrzeni nazw nie mają nic wspólnego z identycznymi nazwami zadeklarowanymi w innej przestrzeni nazw. Dostęp do nazw: operator zasięgu :: deklaracja using dyrektywa using 15
Szablony funkcji Automatyzacja w generowaniu odmian funkcji. obligatoryjne Jeśli potrzebne są funkcje stosujące ten sam algorytm do różnych typów danych, to stosujemy szablony Szablony można przeciążać. template <class Any> void Swap(Any &a, Any &b) { Any temp; temp = a; a = b; b = temp; template <class Any> void Swap(Any a[], Any b[], int n){ Any temp; for (int i = 0; i < n; i++) { temp = a[i]; a[i] = b[i]; b[i] = temp; 16