Wstęp do programowania Wykład 8 Łańcuchy, struktury i pliki Metoda dziel i zwyciężaj Metoda zachłanna
Łańcuchy string – typ danych służący do przechowywania napisów, czyli ciągów znaków. Napisy nazywa się też łańcuchami. string tekst = ”ala ma kota”; Zmienna tekst jest tablicą znaków. Jeśli napiszemy cin << tekst[4], to wyświetli się litera m. Ze zmiennymi typu string związana jest cała klasa operacji. Aby z nich korzystać należy na początku programu umieścić dyrektywę #include. Niektóre kompilatory umieszczają klasę string automatycznie.
Wczytywanie łańcuchów z klawiatury string tekst; cin >> tekst; Jeśli napiszemy ala ma kota, to na zmienną tekst wczyta się ala. Jeśli chcemy wczytać całą linię, to getline(cin, tekst). Wczytać imię, nazwisko i wiek umieszczone w jednej linii. Linia: Jan Kowalski 45 cin >> imię>>nazwisko>>wiek imię, nazwisko to zmienne typu string, wiek typu int.
Operatory łańcuchowe string tekst1, tekst2; Operator przypisania: tekst1=tekst2; Operatory ==, != : równe, różne. Operatory : tekst1<tekst2 jeśli wartość zmiennej tekst1 poprzedza leksykograficznie wartość zmiennej tekst2. ”ala”<”basia”. Operator +: tekst1+tekst2 to złożenie obu łańcuchów. Jeśli tekst jest łańcuchem, to tekst.length() oznacza jego długość.
Przykład Zamienić w łańcuchu litery małe na duże. #include #include //w code:: blocks nie trzeba #include using namespace std; int main () { string str ="Test String."; for (int i=0; i<str.length(); i++) putchar (toupper(str[i])); }
Przykład W tablicy łańcuchów A znaleźć element leksykograficznie minimalny. int main () { string A[6]= {"darek","ala", "Ala", "ijk", "abra", "Abra"}; string min; min=A[0]; for (int i=1; i<8; i++) if (A[i]<min) min=A[i]; cout << min; }
Pojęcie struktury Struktura należy do złożonych typów danych. W odróżnieniu od tablicy, która zawiera elementy tego samego typu, struktura może zawierać elementy różnych typów. Struktury tworzymy za pomocą słowa kluczowego struct podając jej nazwę, a następnie w nawiasie klamrowym definiujemy jej elementy, zwane polami: struct dane_osobowe { string imie, nazwisko; int pesel; };
Tworzenie zmiennych typu strukturalnego Zmienne typu osoba można utworzyć w taki sam sposób jak zmienne innych typów: dane_osobowe x,y,z (utworzono trzy zmienne). Innym sposobem utworzenia zmiennych typu strukturalnego jest umieszczenie ich na końcu definicji struktury: struct dane_osobowe { string imie, nazwisko; int pesel; } x,y,z;
Odwołanie do pola struktury struct dane_osobowe { string imie, nazwisko; int pesel; } x,y,z; x.imie = "Jan"; y.pesel = ;
Struktura jako pole innej struktury Utwórz strukturę pracownik zawierającą: imię, nazwisko, pesel pracownika, zarobki za ostatnie 4 miesiące i średnie zarobki za ostatnie 4 miesiące. struct dane_osobowe { string imie, nazwisko; int pesel; }; struct pracownik { dane_osobowe dane; int zarobki[4]; float srednie_zar; }
Przykład cd. Napisz procedurę wpisz, która wczytuje dane do struktury pracownik, przy czym użytkownik wpisuje dane pracownika oraz wysokość wynagrodzenia za ostatnie 4 miesiące. Średnia wysokość wynagrodzenia powinna być w tej funkcji doliczona i wpisana w odpowiednie pole. void wpisz(pracownik& p) { cin >> p.dane.imie; cin >> p.dane.nazwisko; cin >> p.dane.pesel; {int suma=0, i; for (i=0; i > p.zarobki[i]; suma+=p.zarobki[i];} p.srednie_zar=suma/4; } }
Przykład cd. Napisz procedurę wypisz, króra wypisuje dane osobowe pracownika, jego zarobki z ostatnich 4 mieięcy oraz zarobki średnie void wypisz(pracownik& p) { cout << p.dane.imie << " " << p.dane.nazwisko << " "; cout << p.dane.pesel << endl; cout << "Zarobki: " << endl; for (int i=0; i<4; i++) cout << p.zarobki[i] << " "; cout << endl; cout << "srednie zarobki:" << endl; cout << p.srednie_zar; }
Tablica struktur W funkcji main utwórz tablicę struktur pracownik o nazwie firma, w której zapisane będą informacje o wszystkich pracownikach tej firmy. Następnie wpisz do niej dane pracowników i wypisz je na ekran. main() { const int liczba_prac=3; pracownik firma[liczba_prac]; for (int i=0; i<liczba_prac; i++) wpisz(firma[i]); for (int i=0; i<liczba_prac; i++) wypisz(firma[i]); }
Pliki Jedyna struktura danych, która będzie istnieć po zakończeniu działania programu. Pliki tekstowe i pliki binarne. Aby korzystać z pliku należy umieścić dyrektywę #include. Jeśli z pliku będziemy tylko czytać, można zamienić na. Jeśli na plik będziemy tylko pisać, można zamienić na.
Jak wpisać pracownika na plik? fstream plik; //plik oznacza logiczną nazwę pliku void wpisz_na_plik(pracownik p) { plik << p.dane.imie<< " " << p.dane.nazwisko << " " << p.dane.pesel << endl; for (int i=0; i<4; i++) plik << p.zarobki[i] << " "; plik << endl; } main() { pracownik p; //tu wypełniamy zmienną p plik.open("list.txt", ios:: in | ios:: out | ios:: ate); wpisz_na_plik( p); plik.close(); }
Czy udało się otworzyć plik? plik.open("list.txt", ios:: in | ios:: out | ios:: ate); if (plik.good()) //robimy cos z plikiem; else cout << ”nie udało się otworzyć pliku”; return 1; Nie uzyskamy dostępu do pliku jeśli: chcemy odczytać i plik nie istnieje lub nie mamy uprawnień do odczytu; chcemy zapisać, a plik jest tylko do odczytu lub nie posiadamy uprawnień do katalogu, w którym chcemy utworzyć plik (lub w którym się plik znajduje).
Zapisywanie na pliku tekstowym #include string imie, nazwisko; int nr_tel; main() { fstream plik; cout > imie; cout > nazwisko; cout > nr_tel; plik.open("wizytowka.txt",ios:: out); plik << imie << endl; plik << nazwisko << endl; plik << nr_tel << endl; plik.close(); }
Czytanie z pliku #include using namespace std; int main() { string linia; fstream plik; plik.open(„list.txt", ios::in); if(!plik.good()) {cout << "not ok"; return 1;} else while(!plik.eof()) { getline(plik, linia); cout << linia << endl; } plik.close(); }
Metoda dziel i zwyciężaj (divide and conquer) Polega na podzieleniu problemu na podproblemy, które rozwiązujemy rekurencyjnie, a następnie „scalamy”. Metoda działa dobrze jeśli scalanie jest łatwe oraz rozmiar podproblemów jest istotnie mniejszy od rozmiaru problemu. (Ciąg Fibonacciego – wersja rekurencyjna -- nie spełnia tego warunku!)
Przywódca tablicy Przywódcą tablicy jest element, który występuje więcej razy niż połowa długości tej tablicy. Rozwiązanie rekurencyjne: int tab[n]; if (n==1) przywódcą jest jedyny element tablicy; else { podziel tablicę na dwie połowy; rekurencyjnie oblicz przywódców obu podtablic; sprawdź, który z nich jest przywódcą całości; } Uwaga: jeśli w podtablicy nie ma przywódcy, to jako przywódcę można wziąć dowolny element
Sortowanie przez scalanie Jeśli sortowana tablica ma jeden element, to nic nie rób. W przeciwnym przypadku: Podziel sortowaną tablicę na dwie połowy. Posortuj każdą połowę tą metodą Scal obie posortowane połówki tablicy. Algorytm scalania był na wykładzie 3.
Algorytmy zachłanne (greedy algorithms) Metoda ta dobrze działa w sytuacjach, gdy maksymalizujemy lub minimalizujemy pewną wartość. Algorytm w każdej iteracji ma do wyboru pewną liczbę "lokalnych" akcji. W przypadku maksymalizacji wybiera tę, która lokalnie maksymalizuje wartość docelową. W przypadku minimalizacji wybiera akcję o minimalnej wartości. Algorytmy zachłanne nie zawsze prowadzą do rozwiązania zadania.
Wieże na szachownicy Dana jest szachownica S[n][n]. Na polu S[i][j], 0 ≤ i,j ≤ n, znajduje się x[i][j] monet dwuzłotowych. Zadaniem jest ustawienie n wież na szachownicy, tak aby; suma monet na polach zajmowanych przez wieże była maksymalna żadne dwie wieże się nie szachowały. Lokalną akcją jest ustawienie kolejnej wieży na szachownicy. Algorytm zachłanny wybiera spośród dopuszczalnych pól pole z maksymalną liczbą monet. Powyższy algorytm nie gwarantuje rozwiązania optymalnego. Gwarantuje jednak ponad 50% możliwego zysku.
Sklejanie par w ciągu Przypuśćmy, że mamy ciąg n nieujemnych liczb p1,p2,…,pn. Lokalna akcja sklejania polega na pobraniu dwóch elementów z ciągu i zastąpieniu ich przez sumę ich wartości. Kosztem akcji jest suma wartości "sklejanych" elementów. Ciąg operacji sklejania kończy się, gdy skleiliśmy wszystko do jednej wartości. Interesuje nas obliczenie minimalnego sumarycznego kosztu sklejania n elementów w jeden element. Metoda zachłanna zawsze wybiera akcję o minimalnej wartości.
Algorytm Algorytm zachłanny wynik:= 0; while (mamy co najmniej dwa elementy) { zastąp dwa najmniejsze elementy a,b przez a+b; wynik= wynik + a+b; }
Sklejanie par cd. 3, 13, 45, 2, 4, 8, 23, 9, 2 4, 3, 13, 45, 4, 8, 23, 9 7, 4, 13, 45, 8, 23, 9 11, 13, 45, 8, 23, 9 17, 11, 13, 45, 23 24, 17, 45, 23 40, 24, 45, 64, Powyższy algorytm zwraca optymalne rozwiązanie
Sklejanie par sąsiadów Zadanie analogiczne, ale w każdym kroku wybrana para elementów musi sąsiadować ze sobą. W powyższym algorytmie zamiast frazy „zastąp dwa minimalne elementy a,b przez a+b” należy użyć „zastąp minimalną parę sąsiadujących elementów a i b przez „a+b”. Uwaga: Nowy algorytm nie gwarantuje optymalnego rozwiązania. Przykład: 100, 99, 99, 100. Algorytm zachłanny zwraca wartość 894. Właściwy wynik to 796.
Sklejanie par sąsiadów Wersja algorytmu Garsia-Wachsa: wynik:= 0; while (mamy co najmniej dwa elementy) { zastąp dwa sąsiednie elementy a,b o minimalnej sumie przez a+b; przesuń a+b przed najbliższy na prawo (w ciągu) element c większy od a+b (na koniec ciągu, jeśli takiego c nie ma). }