2. Tablice Tablica to struktura danych przechowująca elementy jednego typu (jednorodna). Dostęp do poszczególnych elementów składowych jest możliwy za pomocą indeksów. Rozróżniamy następujące typy tablic: jednowymiarowe (wektory) - element jest identyfikowany za pomocą pojedynczego indeksu, wielowymiarowe (macierze) - element jest identyfikowany za pomocą większej liczby indeksów. Tablice jednowymiarowe - wektory Deklarowanie wektorów w języku Java: typ_elementu_podstawowego [] identyfikator; int[] wektor; String[] tablica; Powyższe deklaracje tworzą referencję (wskaźnik, uchwyt) do tablicy - nie rezerwują obszaru pamięci służącego do przechowania elementów tablicy. Samo utworzenie wektora (przydzielenie pamięci) realizowane jest za pomocą operatora new. int [] wektor; wektor = new int [10]; int [] wektor = new int[10]; String [] napisy = new String[10]; // deklaracja // tworzenie wektora // deklaracja i tworzenie wektora Tak utworzona tablica zostaje automatycznie wyzerowana (domyślne wartości to zero dla liczb, fałsz dla typu logicznego oraz null dla typów obiektowych). Rozmiar utworzonej tablicy jest stały, a jej elementy indeksowane są od zera. Możliwa jest również bezpośrednia inicjalizacja. int [] wektor = {2, 5, 6, 12, 22, 7; String [] imiona = {"Ala", "Franek", "Wojtek"; Poniżej przykładowe odwołania do elementów wektora: wektor[0] = 1; // przypisanie do pierwszego elementu wartości 1 wektor[1] = wektor[0] + 2; // przypisanie do drugiego elementu wartości pierwszego+2 wektor[5]++; // zwiększenie o jeden wartości szóstego elementu Rozmiar tablicy możemy uzyskać za pomocą pola length. for (i = 0; i < wektor.length; i++) System.out.println(wektor[i]); Ostatni element tablicy ma indeks length-1. 7
Poniższy przykład tworzy wektor oraz wczytuje do niego dziesięć łańcuchów tekstowych, a następnie wyświetla je na ekranie. import java.util.*; public class TablicaLancuchow { public static void main(string[] args) { Scanner klaw = new Scanner(System.in); String[] napisy = new String[10]; // tworzymy tablicę for (int i = 0; i < napisy.length; i++) { System.out.print("Podaj łańcuch nr " + (i+1) + "-> "); napisy[i] = klaw.nextline(); for (int i = 0; i < napisy.length; i++) { System.out.println("Łańcuch nr " + (i+1) + "-> " + napisy[i]); Tablice wielowymiarowe Tablice wielowymiarowe, macierze, nazywane są również wektorem wektorów. Każdy wektor składowy może mieć inną długość (powstają w ten sposób tak zwane tablice szarpane). Deklaracja i tworzenie tablic wielowymiarowych różni się od wektorów tylko podaniem liczby wymiarów macierzy. int [][] tablica = new int[2][3]; Możliwa jest również bezpośrednia inicjalizacja elementów macierzy. int [][] tablica = { {1, 1, 1, {2, 2, 2, 2, {3, 3 ; Poniżej przykładowa aplikacja tworząca tablicę wielowymiarową oraz wyświetlająca jej zawartość na ekranie. public class Macierz { public static void main(string[] args) { int [][] tablica; tablica = new int[3][]; tablica[0] = new int[2]; tablica[1] = new int[4]; tablica[2] = new int[3]; // deklaracja macierzy 8
tablica[0][0] = 1; tablica[0][1] = 1; tablica[1][0] = 2; tablica[1][1] = 2; tablica[1][2] = 2; tablica[1][3] = 2; tablica[2][0] = 3; tablica[2][1] = 3; tablica[2][2] = 3; for (int i = 0; i < tablica.length; i++) { for (int j = 0; j < tablica[i].length; j++) System.out.print(tablica[i][j] + "\t"); System.out.println(); Kopiowanie tablic Pamiętając, że zmienna tablicowa jest referencją do tablicy, należy rozróżnić: kopiowanie referencji (w efekcie mamy dwie referencje wskazujące na ten sam obszar pamięci, na tę samą tablicę), kopiowanie zawartości tablic (w pamięci powstaje kopia tablicy). Zawartość tablicy możemy skopiować za pomocą metody arraycopy() dostępnej w klasie System. Wykorzystanie tablic - Sito Eratostenesa Sito Eratostenesa to algorytm pozwalający na wyznaczenie liczb pierwszych mniejszych lub równych wartości n. Istotą algorytmu jest wykreślanie kolejnych wielokrotności liczby (bez niej samej), która nie została jeszcze wykreślona. Rozpoczynamy od wartości 2. Przyjmijmy n=20. Algorytm prezentuje się następująco. Tworzymy listę wszystkich liczb naturalnych z zakresu od 2 do n. 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Pierwszy element listy (2) jest liczbą pierwszą. Wykreślamy wszystkie jej wielokrotności bez niej samej. 2 3 5 7 9 11 13 15 17 19 Kolejną nie wykreśloną wartością jest liczba 3. Powtarzamy wykreślanie dla trójki. 2 3 5 7 11 13 17 19 Następna nie wykreślona wartość to 5. Powtarzamy wykreślanie itd. Ostatecznie uzyskujemy następujące wyniki. 2 3 5 7 11 13 17 19 Ilość powtórzeń algorytmu można ograniczyć do zaokrąglonego w dół pierwiastka kwadratowego z n. 9
Wykorzystanie tablic - Permutacje bez powtórzeń Niech a oznacza zbiór składający się z n elementów. Permutacją bez powtórzeń elementów zbioru a nazywamy każdy ciąg n-elementowy (a1, a2,..., an), składający się z różnych elementów zbioru a. Ilość permutacji bez powtórzeń elementów zbioru a wynosi n! Rozważmy n równe 3. Liczba wszystkich permutacji elementów zbioru liczb całkowitych obejmującego kolejne wartości od 1 do n to 6 (3!=6). 1, 2, 3 1, 3, 2 2, 1, 3 2, 3, 1 3, 1, 2 3, 2, 1 Algorytm (autor E. Dijkstra) wyznaczania poszczególnych permutacji zakłada, że kolejne permutacje wyświetlane będą w porządku leksykograficznym. Pierwsza permutacja ma postać: 1, 2,... n Przy generowaniu każdej następnej permutacji realizowane są następujące kroki. Na podstawie bieżącej permutacji wyznaczana jest wartość i, która jest maksymalną wartością indeksu, dla której spełniony jest warunek: a[i] < a[i+1]. Przykład: a = [1 4 5 2 9 6 8 7 3] // poszukiwana wartość i wynosi 5 Fragment wektora a od pozycji i+1 do końca nazywany jest ogonem. Następnie poszukujemy wartości j (przy czym j spełniać musi warunek: i+1 j n, czyli jest indeksem elementu znajdującego się w ogonie wektora a), dla której a[j] jest najmniejszą wartością spełniającą warunek a[j] > a[i]. Dla wektora zdefiniowanego powyżej indeks j jest równy 7. Mamy więc wektor a oraz indeksy i oraz j. a = [1 4 5 2 9 6 8 7 3] i j Dokonujemy zamiany elementów znajdujących się na pozycji i oraz j, otrzymujemy: a = [1 4 5 2 9 7 8 6 3] Ostatnią czynnością jest odwrócenie kolejności elementów znajdujących się w ogonie wektora a. Po odwróceniu otrzymujemy kolejną permutacje: a = [1 4 5 2 9 7 3 6 8] Algorytm kończy swoje działanie gdy nie ma już elementów spełniających warunek a[i] < a[i+1]. 10
Zadania do wykonania 1. Napisz program, który zasymuluje n-krotny rzut kostką do gry (wartość n określana jest przez użytkownika w trakcie działania programu). Program ma wyświetlić ile razy wygenerowana została każda z wartości od 1 do 6. Fragment programu generujący 6 liczb losowych z przedziału od 1 do 49: for (i=1; i <= 6; i++) System.out.println((int) (49 * Math.random() + 1)); 2. Napisz metodę, która dla przekazanego parametru (liczba z zakresu od 1 do 12) zwróci nazwę odpowiedniego miesiąca (np. 3-marzec). Sprawdź działanie metody pobierając od użytkownika przykładowe wartości. 3. Utwórz metodę która odwróci elementy w przekazanym jako parametr wektorze. Nie twórz nowego wektora, wykorzystaj zamianę elementów. 4. Napisz metodę, która obliczy odchylenie standardowe dla przekazanego jako parametr wektora liczb rzeczywistych. Wektor ma zawierać dane pobrane od użytkownika z konsoli. - gdzie M jest średnią arytmetyczną ze wszystkich wczytanych liczb 5. Napisz metodę, która obliczy odległość Euklidesową między dwoma n elementowymi wektorami liczb całkowitych. Wykorzystaj poniższy wzór. 6. Napisz program wyznaczający za pomocą algorytmu "Sito Eratostenesa" liczby pierwsze mniejsze lub równe n. Wyznaczone wartości oraz ich ilość wyświetl na ekranie. 7. Napisz program wyświetlający wszystkie permutacje elementów zbioru liczb całkowitych obejmującego kolejne wartości od 1 do n (wartość n ma być podawana w trakcie działania programu). Wprowadzanie danych, obliczenia oraz wyświetlania ma odbywać się z zastosowaniem metod statycznych. 11
8. Napisz metodę, która pomnoży dwie macierzy przekazane jako parametr oraz zwróci, jeśli to możliwe, wynik tej operacji jako macierz wyników. W przypadku błedu danych wejściowych (nieodpowiednie wymiary miacierzy), wygeneruj wyjątek. 9. Gra w życie. Rozważmy następującą symulację w tablicy o wymiarach 15 x 15. W chwili t dany element żyje, jeśli zachodzi jeden z następujących warunków: był martwy w momencie t-1 oraz żyły dokładnie trzy pola sąsiednie, żył w czasie t-1 oraz żyły dwa lub trzy elementy sąsiednie. W pozostałych przypadkach element jest martwy. Zawartość tablicy ma być wyświetlana po upływie każdej jednostki czasu. Program ma umożliwiać dwa sposoby tworzenia początkowej populacji elementów (w czasie t=0): losową - ma wstawiać n elementów w losowo wybranych pozycjach (wartość n ma być ustalana przez użytkownika), określoną przez użytkownika - użytkownik ma możliwość wskazania początkowej lokalizacji elementów. Wykorzystaj macierz wartości logicznych, true komórka żywa, false umarła. Zobacz również: http://projects.abelson.info/life/ http://forrestcoleman.com/life.php 12