Tablice danych – przykłady i wykorzystanie w algorytmach

Transkrypt

Tablice danych – przykłady i wykorzystanie w algorytmach
Tablice danych – przykłady i wykorzystanie w algorytmach
Charakterystyka tablic
Wyobraź sobie, że chcesz napisać program, który pobiera od użytkownika jego oceny,
oblicza ich średnią, a na koniec wypisuje wszystkie oceny, z których ta średnia została
obliczona. Przy założeniu, że wprowadzimy k liczb, realizacja tego zadania wymagałaby
zadeklarowania k zmiennych, a operowanie na nich w wypadku dużych k byłoby niezwykle
uciążliwe (zastanów się, jak wyglądałaby deklaracja zmiennych, jeśli wprowadzilibyśmy 100
ocen). Tu właśnie przychodzi nam z pomocą złożona struktura danych, jaką jest tablica.
Definicja
Tablicą nazywamy ciąg ustalonej liczby elementów tego samego typu, do których
odwołujemy się za pośrednictwem wspólnej nazwy. Dostęp do konkretnego elementu
tablicy uzyskuje się za pomocą nazwy i indeksów określających pozycję, jaką element
zajmuje w tablicy.
Tablica może mieć kilka wymiarów lub jeden, w zależności od problemu
algorytmicznego. W tablicy jednowymiarowej każdy element jest identyfikowany przez
nazwę tablicy i jeden indeks. Na przykład lista uczniów w klasie może być przedstawiona
jako jednowymiarowa tablica nazwisk, gdzie indeksem jest numer w dzienniku, czyli jedna
liczba. W tablicy dwuwymiarowej zaś każdy element jest jednoznacznie identyfikowany
przez nazwę tablicy i parę indeksów. Na przykład w tablicy dwuwymiarowej może być
przedstawiony zbiór pionków na szachownicy: każda figura ma podane dwie współrzędne,
czyli dwie liczby.
W C++ pierwszy element tablicy jednowymiarowej ma indeks równy zeru.
Tablice jednowymiarowe
Aby pracować z tablicą, musimy najpierw ją zadeklarować, tak jak każdą zmienną w
programie. Deklaracja tablicy ma postać:
typ_elementow
nazwa_tablicy[liczba_elementow];
Stosowane oznaczenia:
typ_elementow - należy podać nazwę typu elementów w tablicy;
nazw_atablicy - jest dowolną nazwą (podlega tym samym regułom poprawności, co nazwa
każdej innej zmiennej);
liczba_eiementow - w nawiasie kwadratowym należy podać liczbę elementów tablicy; musi
to być liczba podana wprost (tak zwany literat), na przykład 7, lub stała typu całkowitego
(zdefiniowana wcześniej).
Załóżmy, że chcemy zadeklarować tablicę, w której przechowywać będziemy osiem
liczb całkowitych. Jeśli tablica ma mieć nazwę tab, to deklaracja takiej tablicy w programie
będzie wyglądać następująco:
int
tab[8];
Na rycinie 5.1 przedstawiono graficznie część obszaru pamięci komputera, gdzie
zadeklarowana została tablica ośmioelementowa liczb całkowitych. Elementy tablicy
przedstawiono w postaci prostokątów, a nad prostokątami zapisano przykładowe adresy
komórek pamięci komputera - zauważ, że adresy te zmieniają się o 4. Liczba 4 jest rozmiarem
typu int elementów tej tablicy. Elementy tablicy zajmują w pamięci komputera obszar
ciągły, to znaczy, że każdy kolejny element następuje bezpośrednio po poprzednim.
7840
7844
7848
7852
7856
7860
7864
7868
3
247
0
0
12547
-1
1
-247
tab[0]
tab[1]
tab[2]
tab[3]
tab[4]
tab[5]
tab[6]
tab[7]
Ryc. 5.1. Tablica jednowymiarowa ośmioelementowa po deklaracji, przed wyletnieniem Ŝądanymi
wartościami
Kolejne elementy tablicy są identyfikowane jako tab[ 0 ], tab [ 1 ], . . . ,
Pierwszy element ma indeks o wartości 0, dlatego ósmy element ma indeks o
wartości 7.
Rycina 5.1 przedstawia tablicę po deklaracji, ale jeszcze przed przypisaniem wartości
do tablicy. Zauważ, że tablica nie jest pusta, lecz ma już jakieś wartości. Liczby te są
przypadkowymi wartościami, jakie mogą pojawić się w tablicy, zanim wpiszemy do niej
właściwe wartości.
Tablicę można zadeklarować i od razu nadać wartości jej elementom : za pomocą
instrukcji (przy tak skonstruowanej deklaracji nie trzeba wpisywać rozmiaru tablicy,
ponieważ kompilator pozna go po liczbie elementów w klamrze):
tab [ 7 ].
int
tab[]
=
{6,8,7,2,3,5,7,2};
Tablica będzie teraz wyglądać jak na rycinie 5.2:
6
8
7
2
3
5
7
2
Ryc. 5.2. Tablica jednowymiarowa ośmioelementowa po deklaracji i nadaniu wartości elementom tablicy
Ten sposób wypełniania tablicy można wykorzystać tylko na etapie pisania kodu
programu. Do wypełnienia tablicy podczas wykonywania programu używać będziemy
najczęściej sposobu iteracyjnego, przedstawionego na schemacie blokowym (ryc. 5.3).
Schemat przedstawia wypełnianie tablicy o nazwie tab, która ma n elementów (gdzie n oraz
zmienna pomocnicza i mają wartości całkowite dodatnie).
Ryc. 5.3. Schemat blokowy wypełniania tablicy jednowymiarowej
Zmienna i jest licznikową zmienną pomocniczą, która przyjmując wartości indeksów
kolejnych elementów tablicy, pozwala przypisać tym elementom pobrane z zewnątrz
wartości.
Przypominamy, że jeśli tablica o nazwie tab ma n elementów, to jej elementami są:
tab[ 0 ], tab[ 1 ], . . . , tab[ n-1 ]. Indeksami tablicy są liczby ze zbioru {0, ...,
n - 1}. Odwołanie się do elementu o indeksie innym aniżeli liczba z tego zbioru nazywamy
przekroczeniem zakresu tablicy (lub wyjściem poza zakres tablicy). Przekroczenie zakresu,
na przykład przy wypełnianiu tablicy, spowoduje zapis poza obszarem pamięci
przeznaczonym na zadeklarowaną tablicę. W obszarze tym mogą się znajdować inne zmienne
tego programu lub nawet dane innych programów. Wpisanie tam nieprawidłowych wartości
grozi więc błędem w programie, zawieszeniem uruchomionego programu lub nawet całego
systemu. Jest to niezgodne z regułami języka C+ + .
Załóżmy, że tablica jest n-elementowa, gdzie n jest dodatnią liczbą całkowitą, ustaloną
w trakcie deklarowania tablicy. Fragment kodu realizujący wypełnianie tablicy liczbami
podanymi z klawiatury ma postać:
for (int i=0; i<n; i++)
{
cout « "Podaj wartość elementu";
cin » tab[i];
}
Jeśli w programie chcemy wypełnić kilka tablic, to wygodniejsze będzie umieszczenie
powyższego kodu w funkcji, którą nazwiemy na przykład wypełnij. Wówczas parametrami
przekazywanymi do funkcji będą: tablica, którą chcemy wypełnić, oraz liczba elementów
tablicy do wypełnienia.
Funkcja, po otrzymaniu tablicy jako argumentu, ma pełen dostęp do zawartości
komórek, w których przechowywane są elementy tablicy - działa bezpośrednio na
tablicy. Nie jest więc potrzebne odwołanie przez referencję, aby z wnętrza funkcji
modyfikować wartości elementów tablicy.
W celu sprawdzenia efektów działania funkcji wypełnij zdefiniowaliśmy funkcję wyświetl,
która wyświetli nam na ekranie wypełnione tablice:
Zwróć uwagę na postać parametru aktualnego przy wywołaniu funkcji wypełnij oraz
funkcji wyświetl.
Przekazując funkcji tablicę jako argument (czyli w wywołaniu funkcji),
podajemy tylko i wyłącznie jej nazwę, bez nawiasów kwadratowych i bez podawania
rozmiaru.
Do obsługi obydwu tablic zastosowaliśmy zdefiniowane przez nas funkcje. Zauważ,
że argumentem przekazywanym do funkcji jest tu właśnie tablica, na której funkcje wykonają
zdefiniowane operacje: a zatem jedna z nich wypełni tablicę, druga ją wyświetli. Moglibyśmy
za pomocą tych funkcji zainicjować i wyświetlić każdą tablicę o elementach całkowitych, ale
pod warunkiem, że miałaby ona osiem elementów. Co więc w wypadku, gdy chcemy
wypełnić i wyświetlić kilka tablic, ale o różnej liczbie elementów? Czy musimy napisać kilka
różnych funkcji, przeznaczonych dla tablic o różnych liczbach elementów? Oczywiście, nie.
Napiszemy jedną funkcję, ale oprócz tablicy przekażemy do jej wnętrza informację o
rozmiarze tej tablicy:
void wypełnij(int tab[], int rozmiar)
{
for (int i=0; i<rozmiar; i++)
{
cout << "Podaj wartość elementu";
cin » tab[i];
}
}
Zauważ, że deklarując tablicę jako argument funkcji, nie podaliśmy w nawiasach
kwadratowych jej rozmiaru - nie ma takiej potrzeby. W wypadku tablicy jednowymiarowej na
etapie definicji funkcji kompilatorowi wystarczy znajomość typu elementów tablicy (nieco
inaczej wygląda to w tablicach o większej liczbie wymiarów).
Fragment funkcji głównej z zastosowaniem zdefiniowanej funkcji wypełniającej dwie
tablice o różnych liczbach elementów wygląda następująco:
int main()
{
int tablical[10];
int tablica2[4];
wypełnij(tablical,10);
wypełnij(tablica2,4);
return 0;
}
Skoro już wiesz, że tablice mogą być argumentami przekazywanymi do funkcji, to analiza
działania poniższej funkcji pozwoli ci łatwo określić, jakie zadanie ma ona do wykonania:
float oblicz(float tab_l[5], float tab_2[5])
{
float suma = 0;
for (int i=0; i<5; i++)
suma =
suma+tabl[i]+tab2[i] ;
return
suma;
}
Funkcja ta pobiera dwie jednowymiarowe tablice pięcioelementowe, a jej wynikiem
jest suma wszystkich elementów obu tablic. Moglibyśmy napisać funkcję, która sumuje
elementy pojedynczej tablicy, wywołać tę funkcję dla dwóch tablic i zsumować oba wyniki.
My dokonaliśmy tego w jednej funkcji, aby pokazać, że funkcji można przekazać więcej niż
jedną tablicę.

Podobne dokumenty