Krótki kurs C++ Część IV.doc

(62 KB) Pobierz

Krótki kurs C++ Część IV

1. Tablice

Tablica jest strukturą danych. Tablica to ciągła grupa komórek w pamięci. Grupa ta posiada wspólną nazwę. Komórki pogrupowane są w typ danych, który określamy przy definicji tablicy. Do poszczególnych elementów tablicy odwołujemy się poprzez określenie nazwy tablicy i numeru pozycji odpowiedniego elementu. Elementy tablicy numerujemy od zera ( pierwszym elementem jest element zerowy). Chcąc zdefiniować tablicę 20 liczb całkowitych piszemy:

int tablicaInt[20];

Poszczególne elementy będziemy numerować od 0 do 19. Chcąc się odwołać do pierwszego elementu tablicy piszemy:

tablicaInt[0];

Numer elementu ujmowany w nawiasy kwadratowe nazywany jest indeksem. Indeks musi być liczbą całkowitą lub wyrażeniem całkowitym.

2. Deklaracja tablic

Tablica zajmują miejsce w pamięci. Przy deklaracji tablicy należy określić typ elementów tablicy oraz ich ilość.

Posiadając tą informację kompilator może zarezerwować odpowiednią ilość komórek pamięci. Deklaracja tablicy ma postać:

typ_elementów nazwa_Tablicy [ ilość_elementów ];

Kilka tablic tego samego typu może być deklarowanych w tej samej linii:

int tablicaInt1 [20], tablicaInt [50];

W linii tej deklarujemy dwie tablice typu int. Jedną składającą się z 20 elementów, a drugą z 50 elementów.

3. Inicjowanie tablic

Elementy tablicy mogą być inicjowane w deklaracji tablicy przez umieszczenie po niej znaku równości i rozdzielonej przecinkami listy wartości inicjujących ujętej w nawiasy klamrowe. </br>

int tablicaInt[5]={1,2,3,4,5};

[p]Jeśli wartości inicjujących będzie mniej niż elementów tablicy, pozostałe elementy będą zainicjowane wartością zero.

int tablicaInt[5]={5};

Deklaruje tablicę typu int, która ma pięć elementów. Pierwszy element tej tablicy jest jawnie inicjowany wartością 5.

Pozostałe elementy tej tablicy są inicjowane niejawnie wartością zero. Do inicjacji tablic możemy również wykorzystać pętle for.</br>

#include <iostream.h> int main() { const int wTab=100; int tablicaInt[wTab]; for(int i=0; i<wTab; i++) tablicaInt [i]=i; cout<<"Tablica została zainicjowana liczbami:\n"; for(int i=0; i<wTab; i++) cout<<"Numer elementu:"<<i<<" wartosc elementu" <<tablicaInt [i]<<'\n'; return 0; }

[p]Linia:

const int wTab=100;

stosuje kwalifikator const w celu zadeklarowania stałej symbolicznej wTab, której wartość wynosi 100. Stałe takie muszą być inicjowane wyrażeniem stałym i nie mogą być później modyfikowane. Stałe symboliczne nazywane są również zmiennymi tylko do odczytu. Określając liczbę elementów tablicy kompilator oczekuje wyrażenia całkowitego i stałego, dlatego możemy użyć stałej symbolicznej. Wykorzystywanie stałych symbolicznych do określenia wielkości tablic daje programom większą skalowalność. Nic nie stoi na przeszkodzie aby zamiast liczby 100 elementów typu int inicjowane było 1000 elementów poprzez zmianę stałej symbolicznej. Gdybyśmy nie używali stałej symbolicznej musielibyśmy zmienić program w 3 miejscach.

4. Wykorzystywanie tablic

Zostałeś poproszony o napisanie programu, który zliczałby odpowiedzi na ankietę zamieszczoną w pewnym piśmie. Oto pytania, jakie zadano czytelnikom:

Co Twoim zdaniem powinno się znaleźć w naszym piśmie?

  1. recenzje gier,
  2. opisy sprzętu,
  3. kursy programowania,
  4. nowości ze świata komputerów.

A oto program realizujący to zadanie:

#include <iostream.h> int main() { // inicjacja zmiennych // najpierw inicjujemy stałą symboliczną określającą // wielkość tablicy // potem inicjujemy tablicę liczb typu int o nazwie tabOdp // tablica ma 4 elementy const int wTab=4; int tabOdp[wTab]={0}, licz; // wyświetlamy informacje dla użytkownika cout<<"Co Twoim zdaniem powinno się znalezc w naszym pismie ?\n" <<"1. recenzje gier\n2. opisy sprzętu,\n3. kursy programowania,\n4. nowości ze świata komputerów. \nWprowadz -1 aby zakonczyc."; cin>>licz; // pętla odpowiedzialna za zliczanie odpowiedzi // 1. sprawdzamy jaka wartość została wprowadzona // przez użytkownika // -jeśli użytkownik wprowadził mniej niż 1 lub // więcej niż 4 to // przerywamy pętlę while(licz>0 && licz<5){ cout<<"Co Twoim zdaniem powinno się znalezc w naszym pismie ?\n"<<"1. recenzje gier\n2. opisy sprzętu,\n3. kursy programowania, \n4. nowości ze świata komputerów.\nWprowadz -1 aby zakonczyc."; // zliczenie odpowiedzi indeks tablicy jest obliczany // na przez wyrażenie licz-1 // zwiększenie elementu tablicy przez operator ++ // wprowadzenie kolejnej odpowiedzi ++tabOdp[licz-1]; cin>>licz; } // wyświetlamy wyniki cout<<"Wynik ankiety:\n"<<"1. recenzje gier :"<< tabOdp[0]<<"\n2. opisy sprzętu:"<<tabOdp[1] <<"\n3. kursy programowania:"<<tabOdp[2] <<"\n4. nowości ze świata komputerów:"<<tabOdp[3]<<endl; return 0; }

Generacja liczb losowych

Do generacji liczb losowych służy funkcja rand(). Funkcja ta generuje liczbę całkowitą między 0 i RAND_MAX (stałą symboliczną zdefiniowaną w pliku nagłówkowym stdlib.h. Jeśli funkcja ta wybiera liczby naprawdę losowo to każda z nich ma taką samą szansę bycia trafioną. Zakres wartości wytwarzanych przez funkcję rand() jest bardzo duży. Zwykle nie potrzebujemy aż tylu wartości. Na przykład program, który będzie symulował rzuty kostką będzie potrzebował sześć wartości. Chcąc wytworzyć liczby całkowite w zakresie od 0 do 5 używamy dzielenia modulo (%) w połączeniu z funkcją rand:

1+rand()%6

Nazywamy to skalowaniem. Liczba 6 jest czynnikiem skalowania. A liczba 1 jest wartością przesunięcia. Wartość przesunięcia to liczba równa pierwszej liczbie w pożądanym zakresie kolejnych liczb całkowitych.

Oto program symulujący rzut kostką 20 razy:

#include <iostream.h> #include <stdlib.h> int main() { for(int i=1; i<20; I++) cout<<(1+rand()%6)<<'\n'; cout<<endl; return 0; }

Jeśli uruchomisz ten program kilka razy zobaczysz, że wyświetlana jest taka sama sekwencja wartości. Jest to spowodowane tym, że funkcja rand() generuje liczby pseudolosowe. Konieczne w tym wypadku jest losowanie, które jest realizowane przez funkcje srand(). Funkcja srand() pobiera jako argument liczbę unsigned int i wywołuje funkcję rand() do rozpoczęcia wytwarzania różnych sekwencji liczb losowych. Jeżeli za każdym razem chcemy losować bez konieczności wprowadzania liczby bazowej do generowania liczb losowych, możemy użyć wyrażenia:

srand(time(0));

Wyrażenie to odczytuje zegar, aby otrzymać wartość bazową dla generowania liczb losowych. Funkcja time z argumentem 0 zwraca czas jaki upłynął w sekundach od 1 stycznia 1970 roku.

Oto program zliczający rzut kostką 5000 razy. Wykorzystujący w tym celu tablicę:

#include <iostream.h> #include <stdlib.h> int main() { const int wTab=7; int czestosc[wTab]={0}; srand(time(0)); //ignoruj element zero w tablicy for(int rzut=1; rzut<=5000; rzut++) ++czestosc[1+rand()%6]; cout<<"Scianka: "<<" czestosc wystepowania \n"; for(int scianka=1;scianka<wTab;scianka++) cout<<scianka<<" "<<czestosc[scianka]<<endl; return 0; }

6. Przekazywanie tablic do funkcji

Chcąc przekazać tablicę do funkcji piszemy nazwę tablicy bez nawiasów np.:

#include <iostream.h> void wyswietlTablice (int [], int); main () { const int wTab=10; int tab[ wTab ]={1,4,7,5,100,150,9,34}; cout<<"Wywolanie funkcji wyswietlTablice\n"; wyswietlTablice(tab, wTab); return 0; } void wyswietlTablice (int tablica [], int wielkosc) { for(int i=0; i<wielkosc; i++) cout<<"Element tablicy:"<<(i+1)<<" Wartosc elementu:"<<tablica [i]<<endl; }

Przekazując tablicę do funkcji przekazuj jednocześnie jej rozmiar. Dzięki temu funkcja może przetworzyć odpowiednią ilość elementów. W przeciwnym wypadku musielibyśmy informacje o wielkości tablicę umieścić wewnątrz funkcji lub określić jako zmienną globalną, co jest niezgodne z zasadą najmniejszego przywileju.

C++ automatycznie przekazuje tablice do funkcji przez referencję, w związku z tym funkcja może modyfikować elementy tablicy bezpośrednio. Jest to spowodowane względami wydajnościowymi. Jeśli tablicę przekazywalibyśmy przez wartość. Kopiowanie wszystkich elementów tablicy zabierałoby dużo czasu i potrzebowałoby dużo pamięci. Poszczególne elementy tablicy są przekazywane są przez wartość w związku z tym funkcja nie modyfikuje elementu bezpośrednio, a operuje na kopii wartości bezpośrednio.

Poniższy program demonstruje przekazywanie tablic i poszczególnych elementów do tablic:

#include <iostream.h> void wyswietlTablice (int [], int); void modyfikujTablice (int [], int); void modyfikujElement (int ); main () { const int wTab=10; int tab[ wTab ]={1,4,7,5,100,150,9,34}; cout<<"Wywolanie funkcji wyswietlTablice przed modyfikacja\n"; wyswietlTablice(tab, wTab); modyfikujTablice(tab,wTab); cout<<"Tablica po modyfikacji:\n"; wyswietlTablice(tab, wTab); cout<<"Element 3 tablicy przed wywolaniem funcji modyfikujElement:" <<tab[3]<<endl; modyfikujElement(tab[3]); cout<<"Element 3 tablicy po wywolaniu funcji modyfikujElement: " <<tab[3]<<endl; return 0; } void wyswietlTablice (int tablica [], int wielkosc) { for(int i=0; i<wielkosc; i++) cout<<"Element tablicy:"<<(i+1)<<" Wartosc elementu:" <<tablica [i]<<endl; } void modyfikujTablice (int tablica [], int wielkosc ) { for(int i=0; i<wielkosc; i++) tablica [i]=tablica [i]+10; } void modyfikujElement (int a) { a*=2; cout<<"Wewnatrz funkcji modyfikujElement:"<<a<<endl; }

Funkcja modyfikujTablice() do każdego elementu tablicy dodaje 10.

7. Sortowanie tablic

Sortowanie danych jest jednym z najważniejszych zadań obliczeniowych. Powstało wiele wydajnych algorytmów, których zadaniem jest sortowanie tablic. Tutaj przedstawię dwa najprostsze algorytmy sortowania: bąbelkowe i przez selskcję.

7.1. Sortowanie bąbelkowe

Sortowanie bąbelkowe wymaga kilku przejść przez całą tablicę. W każdym przejściu tablicy kolejne pary są porównywane. Jeżeli para znajduje się w porządku rosnącym lub wartości są identyczne ich wartości są pozostawione. Jeśli para znajduje się w porządku malejącym, wartości zamieniane są miejscami.

#include <iostream.h> void wyswietlTablice (int [], int); void sortBab(int [], int); main () { const int wTab=10; int tab[ wTab ]={1,4,7,5,100,150,9,34,-1,120}; cout<<"Tablica przed posortowaniem:\n"; wyswietlTablice(tab,wTab); sortBab(tab,wTab); cout<<"Tablica po posortowaniu:\n"; wyswietlTablice(tab,wTab); return 0; } void wyswietlTablice (int tablica [], int wielkosc) { for(int i=0; i<wielkosc; i++) cout<<"Element tablicy:"<<(i+1)<<" Wartosc elementu:" <<tablica [i]<<endl; } void sortBab (int tablica [], int wielkosc ) { int temp; for (int i=0; i<wielkosc-1; i++) for (int j=0; j<wielkosc; j++) if(tablica[j]>tablica[j+1]){ temp=tablica[j]; tablica[j]=tablica[j+1]; tablica[j+1]=temp; } }

7.2. Sortowanie przez selekcję

Algorytm ten jest następujący:

·         należy znaleźć najmniejszy element i zmienić go miejscem z pierwszym,

·         znaleźć drugi element w kolejności i zamienić go miejscem w drugim,

·         kontynuować krok drugi aż do posortowania.

#include <iostream.h> void wyswietlTablice (int [], int); void sortSel(int [], int); main () { const int wTab=10; int tab[ wTab ]={1,4,7,5,100,150,9,34,-1,120}; cout<<"Tablica przed posortowaniem:\n"; wyswietlTablice(tab,wTab); sortSel(tab,wTab); cout<<"Tablica po posortowaniu:\n"; wyswietlTablice(tab,wTab); return 0; } void wyswietlTablice (int tablica [], int wielkosc) { for(int i=0; i<wielkosc; i++) cout<<"Element tablicy:"<<(i+1)<<" Wartosc elementu:" <<tablica [i]<<endl; } void sortSel (int tablica [], int wielkosc ) { void zmien( int [], int, int); int min; for (int i=0; i<wielkosc; i++){ min=i; for(int j=i+1; j<wielkosc; j++) if(tablica[j]<tablica[min]) min=j; zmien(tablica,i,min); } } void zmien (int tablica [], int indeks, int indeks1) { int temp; temp=tablica[indeks]; tablica[indeks]=tablica[indeks1]; tablica[indeks1]=temp; }

8. Tablice wielowymiarowe

[p]W C++ tablice mogą być wielowymiarowe. Typowym zastosowaniem tablic wielowymiarowych jest przedstawienie informacji pogrupowanych w wierszach i kolumnach. W tablicach wielowymiarowych chcąc wskazać element tablicy musimy określić dwa indeksy: pierwszy jest indeksem oznacza wiersz, a drugi kolumnę.

Tablice wymagające dwóch indeksów do zidentyfikowania określonego elementu są nazywane tablicami dwuwymiarowymi. Tablice wielowymiarowe mogą mieć więcej niż dwa indeksy. Kompilatory C++ dopuszczają 12 indeksów tablicy.

Wielowymiarowa tablica może być a zainicjowana w podobny sposób do tablicy jednowymiarowej. Chcąc utworzyć dwuwymiarową tablicę typu int, mającą dwa wiersze i dwie kolumny napiszemy (jednocześnie incjując):

int tabInt[2][2]={{1,1},{2,2}};

Wartości w nawiasach klamrowych zgrupowane są według wierszy. Chcąc przekazać tablicę wielowymiarową do funkcji musimy określić wielkość drugiego i ewentualnie następnych indeksów. Kompilator używa tych wielkości do określenia położenia w pamięci elementów wielowymiarowej tablicy. Przykładowo chcąc przekazać do funkcji wyswTab() tablicę tabInt musielibyśmy tą funkcję zadeklarować:

void wyswTab(int [][2],int );

Definicja funkcji wyglądałaby tak:

void wyswTab(int tablica[][2], int wielkosc)

Ćwiczenia

  1. Sortowanie bąbelkowe jest mało wydajne dopracuj ten algorytm. Aby poprawić wydajność( przecież dane w tablicy mogą być już w całości lub w części posortowane, zmodyfikuj sortowanie tak, aby na końcu każdego przebiegu sprawdzano czy zostały dokonane jakieś zmiany, jeżeli nie dane są już uporządkowane i należy zakończyć sortowanie).
  2. Pewne przedsiębiorstwo płaci swoim sprzedawcom wynagrodzenie zależnie od wartości sprzedaży. Sprzedawcy otrzymują 690 zł + 10% swojej sprzedaży brutto w tym miesiącu. Napisz program obliczający pensję sprzedawcy w zależnośći od sprzedaży.
  3. Napisz program symulujący rzuty dwiema kostkami 100 razy. Powinien on wykorzystywać funkcję rand() w połączeniu z funkcją srand(). Suma tych 36 (od 2 do 12) kombinacji winna być wyświetlona w wierszach i kolumnach jak poniżej.

1 2 3 4 5 6 1 2 3 4 5 6

<li>Napisz algorytm sortowania przez selekcję w sposób rekurencyjny.

 

...
Zgłoś jeśli naruszono regulamin