mgr inż. Wacław Libront * Bobowa 2019

ZSO Bobowa, ul. Długoszowskich 1, 38-350 Bobowa, tel: 0183514009, fax: 0183530221, email: sekretariat@zsobobowa.eu, www: zsobobowa.eu

Lekcja 6

Tablice w C++

  1. schemat
  2. losowanie
  3. parzyste
  4. parametr
  5. macierz
  6. sortowanie
  7. zadania
  8. rozwiązania

Po co komu tablice? Jeśli w programie używamy 2 zmiennych, to nie ma problemu. Ale jeśli musimy posłużyć się 1000 zmiennych i na ich podstawie coś tam obliczyć, to pojawiają się problemy. Jak je zadeklarować i jak potem nie pogubić się w gąszczu tylu nazw! Tablice umożliwiają tworzenie struktur podobnych do pokratkowanej kartki – do każdej komórki można wpisać jakieś dane. Każda z komórek opisana jest tzw. indeksem, czyli numerem. Komórki tablic w C++ numerowane są od zera!


Schemat deklaracji tablicy

typ nazwa [liczba elementów];
   
tablica jednowymiarowa
typ nazwa [liczba elementów] [liczba elementów]…;
   
tablica wielowymiarowa

Przykłady deklaracji tablic

// tablica T o 10 elementach typu int numerowanych 0..9 
int T[10];
// tablica ZNAKI o 20 elementach typu char numerowanych 0..19 
char ZNAKI[20];
// tablica TAB o 100 elementach typu int 10 wierszy i 10 kolumn 
int TAB[10][10];

Zapis do tablicy

T[0] = 1; 
T[9] = 10; 
//pojedyncze znaki w apostrofach
ZNAKI[1] = ’A’;     
TAB[0][0] = 1; 
TAB[9][7] = 63; 

Deklaracja i inicjowanie tablicy

Każda komórka tablicy po zadeklarowaniu zawiera tzw. dane nieokreślone i dlatego (podobnie jak to czyniliśmy ze zmiennymi) powinny być zainicjowane, czyli w większości przypadków zerujemy je.

int T[10] = {0,1,2,3,4,5,6,7,8,9}; 
char ZNAKI[20]={'a','l','a',' ','m','a',' ','k','o','t','a'}; 
//Zerowanie za pomocą pętli 
//(numerujemy od 0 do 9 – nie ma komórki o numerze 10) 
for (int i=0; i < 10; i++) 
  T[i]=0; 

Zadanie – Losowanie liczb
Wypełnij tablicę liczbami losowymi. Losowane są liczby z przedziału 0..32768.

Zakres losowanych liczb zależy to od rodzaju użytego kompilatora.

const int N=10;
int LOS[N];
for (int i=0; i < N; i++) 
  LOS[i]=rand();
for (int i=0; i < N; i++) 
  cout << "LOS[" << i <<"]=" << LOS[i] << endl;

//losowe z przedziału 0..9 
LOS[i]=rand() % 10; 
//losowe oceny 1..6 
LOS[i]=rand() % 6 + 1 

Zwróć uwagę, że za każdym razem gdy uruchamiamy program losowane są te same liczby! Rozwiązaniem jest zainicjowanie tzw. „maszyny losującej” – która może być powiązania na przykład  z czasem systemowym komputera (biblioteka <time> i funkcje srand i time).


Zadanie - Funkcja losująca
Napisz funkcję, która wylosuje liczbę całkowitą z przedziału POCZĄTEK..KONIEC

#include <ctime>
int LOSUJ(int poc, int kon){
  return rand() % (kon-poc+1) + poc;
}

...
srand(time(NULL));
cout << LOSUJ(1,6);

Zadanie – Liczby parzyste
Zadeklaruj tablicę PARZ[100] o elementach typu int i wpisz do niej 100 pierwszych liczb parzystych. Następnie wypisz elementy tej tablicy, od tyłu, po 10 w każdym wierszu.

#include <iomanip>
#include <ctime>
...
srand(time(NULL));
int PARZ[100];
for (int i=0; i < 100; i++)
  PARZ[i]=i*2;
for (int i=99; i >= 0; i--){
  cout.width(4);
  cout << PARZ[i];
  if (i%10 == 0) cout << endl;
}

Tablica jako parametr funkcji

Tablica przekazywana za pomocą parametru nie musi mieć podanej konkretnej wielkości – wystarczą same nawiasy kwadratowe. Wyjątkiem są tablice wielowymiarowe, z przekazywaniem których, zwłaszcza tych dużych, mogą pojawić się problemy. W C++ przyjęto domyślnie, że tablice ZAWSZE będą przekazywane przez referencję – zawsze działamy na rzeczywistej tablicy.

Wszystkie działania na tablicach (zwłaszcza pętle) muszą „wiedzieć”, kiedy zakończyć działanie. Niestety w C++ nie ma gotowej funkcji, która podawałaby liczbę komórek tablicy i dlatego tworząc wszelkiego rodzaju funkcje, prócz samej tablicy podawać jeszcze należy zakres komórek do przeszukania!


Zadanie – Minimum-Suma
Wypełnij tablicę liczbami losowymi i wyszukaj najmniejszą (największą) oraz wylicz sumę wylosowanych liczb. Napisz trzy funkcje Tmin (Tmax) i Tsuma, których parametrem jest tablica z liczbami.

//MINIMUM
int Tmin(int poc, int kon, int t[]){
  int m=t[poc];
  for (int i=poc+1; i <= kon; i++)
    if (t[i] < m) m=t[i];
  return m;
}
//SUMA
int Tsuma(int poc, int kon, int t[]){
  int s=0;
  for (int i=poc;  i<= kon; i++)
    s=s+t[i];
  return s;
}
...
//LOSOWANIE
srand(time(NULL));
const int N=10;
int LOS[N];
for (int i=0; i < N; i++) LOS[i]=rand();
cout << Tmin(0,9,LOS) << endl;
cout << Tsuma(0,9,LOS) << endl;

Podczas wyszukiwania minimalnej i maksymalnej liczby, porównujemy w pętli komórki z tablicy z tymczasową zmienną: MIN lub MAX, która powinna być zainicjowana – najlepiej pierwszym elementem z tablicy.


Zadanie - Macierz
Macierze wykorzystywane są w matematyce, m.in. do rozwiązywania równań liniowych. Macierz jest tablicą dwuwymiarową. Jedną z ważniejszych operacji na macierzach jest transpozycja, czyli zamiana wierszy z kolumnami, np. tablica 4x5 zamienia się w tablicę 5x4.

  1. Zadeklaruj dwie tablice A 4x5 i B 5x4 typu int.

  2. Wypełnij tablicę A liczbami losowymi z przedziału -9..9.

  3. Wyświetl na ekranie tablicę A.

  4. Dokonaj transpozycji macierzy A do B.

  5. Wyświetl tablicę B na ekranie.

int A[5][4];
int B[4][5];
//losowanie
for (int w=0; w < 5; w++){
  for (int k=0; k < 4;k++){
    A[w][k]=LOSUJ(-9,9);
  }
}
//wyswietlanie
for (int w=0; w < 5; w++){
  for (int k=0; k < 4; k++){
    cout.width(4);
    cout << A[w][k];
  } 
  cout << endl;
} 
//transpozycja
for (int w=0; w < 5; w++){
  for (int k=0; k < 4; k++){
    B[k][w]=A[w][k];
  }
}
//wyswietlanie
for (int w=0; w < 4; w++){
  for (int k=0; k < 5; k++){
    cout.width(4);
    cout << B[w][k];
  }
  cout << endl;
}

Zadanie - Sortowanie naiwne
W tablicy znajdują się losowe liczby. Należy je uporządkować wykorzystując tzw. sortowanie naiwne według poniższego algorytmu:

  1. szukamy minimalnej liczby w zakresie pierwsza .. ostatnia komórka tablicy

  2. zamieniamy liczby w komórkach: znaleziona liczba minimalna – pierwsza

  3. zwiększamy numer pierwszej komórki o 1 i powtarzamy proces o punktu 1

Potrzebne będą dwie funkcje pomocnicze: SZUKAJ i ZAMIANA.

Funkcja pomocnicza ZAMIANA zamienia liczby w dwóch zmiennych „a” i „b” – konieczna jest jeszcze jedna zmienna – tzw. zmienna pomocnicza. Parametry funkcji przekazywane są przez tzw. referencję – po wywołaniu funkcji w parametrach będą zamienione liczby.

Funkcja pomocnicza SZUKAJ wyszukuje element minimalny, ale zwraca numer komórki, w której znajduje się minimalna wartość tablicy.

void ZAMIANA (int &a, int &b) {
  int p=a;  a=b; b=p;
}
int SZUKAJ(int poc, int kon, int t[]){
  int m=t[poc];
  int min=poc;
  for (int i=poc+1;  i<= kon; i++)
      if (t[i] < m) {
  	  m=t[i];
  	  min=i;
  	}
  return min;
}

Funkcja działa według algorytmu:

  1. przeglądamy tablicę t[] w pętli od poc do kon

  2. wartość minimalną zapisujemy w zmiennej m, pozycję wartości minimalnej w min

  3. funkcja zwraca numer minimalnego elementu w tablicy

Funkcja główna SORTUJ wykorzystuje dwie funkcje pomocnicze. Sortujemy całą tablicę, dlatego jako parametrem jest tablica i ilość elementów tablicy. Funkcja zwraca posortowaną tablicę, ponieważ tablice przekazywane są jako referencje.

void SORTUJ(int ile, int t[]){
  for (int p=0; p < ile-1; p++)
    ZAMIANA(t[SZUKAJ(p,ile-1,t)], t[p]);
}
...
const int N=10;
int LOS[N];
//losowanie
for (int i=0; i < N; i++)
  LOS[i]=rand();
for (int i=0; i < N; i++) 
  cout << "LOS[" << i << "]=" << LOS[i ]<< endl;
//sortowanie
SORTUJ(10,LOS);
for (int i=0; i < N; i++) 
  cout << "LOS[" << i << "]=" << LOS[i] << endl;

Algorytm funkcji:

  1. zmienna ile zawiera liczbę elementów tablicy, dlatego pętla kręci się w zakresie 0..ile-1

  2. funkcja ZAMIANA zamienia miejscami dwie komórki tablicy (jeśli jest to konieczne) komórkę z liczbą minimalną (funkcja Tmin) i pierwszą badaną komórkę

  3. funkcja zwraca posortowaną tablicę

Bardziej „zrozumiała” wersja sortowania –wyszukiwanie minimum i zamiana liczb w osobnych wierszach.

void SORTUJ(int ile, int t[]){
  int min;
  for (int p=0; p < ile-1; p++){
    min=SZUKAJ(p,ile-1,t);
    ZAMIANA(t[min], t[p]);
  }
}

Uwaga. Jeśli przekazywanie tablicy jako parametr funkcji sprawia problemy (zbyt duża, albo wielowymiarowa), to może (co nie jest rozwiązaniem „edukacyjnym”) w funkcji odwołuj się bezpośrednio do niej!


Zadania

1) Do tablicy znakowej wpisz kolejne litery alfabetu

2) Zmodyfikuj funkcję SORTUJ tak, aby sortowała w kolejności malejącej

3) Do tablicy dwuwymiarowej MN[10][10] wpisz tabliczkę mnożenia w zakresie 1..10

4) Tablica trójwymiarowa OCENY zawiera losowe oceny semestralne dla 10 klas, 15 przedmiotów i 30 uczniów OCENY[10][15][30].
a) wyzeruj tablicę OCENY
b) rozlosuj oceny (0..6) do tablicy
c) wylicz średnią z ocen dla wybranej klasy, ucznia, przedmiotu
d) wypisz na ekranie oceny wybranej klasy, ucznia, przedmiotu (również prostokątne)

5) Wylosuj liczby do tablicy, a następnie wypisz osobno parzyste i nieparzyste

6) Wylosuj liczby do tablicy. Wylicz różnicę między MAX i MIN

7) Wylosuj liczby do tablicy. Wylicz średnią i element najbliższy średniej


Przykładowe rozwiązania zadań
Jedynie ciężka umysłowa praca i zrozumienie działania programu może nauczyć programowania!

//zadanie 1
char ASCI[256];
for (int i=33; i < 256; i++)
	ASCI[i]=char(i);
for (int i=33; i < 256; i++){
	cout.width(4);
	cout  << ASCI[i];
	cout.width(4);
	cout << i;
	if (i % 8 == 0) cout << endl;
}
cout << endl;
//zadanie 2
void ZAMIANA (int &a, int &b) {
  int p=a;  a=b; b=p;
}

int SZUKAJ(int poc, int kon, int t[]){
  int m=t[poc];
  int min=poc;
  for (int i=poc+1; i <= kon; i++)
      if (t[i] > m) {
  	    m=t[i];
  	    min=i;
  	  }
  return min;
}

void SORTUJ(int ile, int t[]){
  int min;
  for (int p=0; p < ile-1; p++){
    min=SZUKAJ(p,ile-1,t);
    ZAMIANA(t[min], t[p]);
  }
}
...
int T[20];
for (int i=0; i < 20; i++)
	T[i]=rand() % 10;
for (int i=0; i < 20; i++)
	cout << T[i] << " ";
cout << endl;
SORTUJ(20,T);
for (int i=0; i < 20; i++)
	cout << T[i] << " ";
cout << endl;
//zadanie 3
int MN[10][10];
for (int i=1; i <= 10; i++){
	for (int j=1; j <= 10; j++){
		MN[i-1][j-1]=i*j;
	}
}	
for (int i=1; i <= 10; i++){
	for (int j=1; j <= 10; j++){
		cout.width(4);
		cout << MN[i-1][j-1];
	}
	cout << endl;
}
//zadanie 4
int KLA=10;
int PRZ=15;
int UCZ=30;
int OCENY[KLA][PRZ][UCZ];
//zarowanie
for (int k=0; k < KLA; k++)
	for (int p=0; p < PRZ; p++)
		for (int u=0; u < UCZ; u++)
			OCENY[k][p][u]=0;
//losowanie ocen
for (int k=0; k < KLA; k++)
	for (int p=0; p < PRZ; p++)
		for (int u=0; u < UCZ; u++)
			OCENY[k][p][u]=rand() % 6 +1;
//wypisz klasami
for (int k=0; k < KLA; k++){
	cout << endl << "KLASA " << k << endl;
	for (int p=0; p < PRZ; p++){
		for (int u=0; u < UCZ; u++)
			cout << OCENY[k][p][u] << " ";
		cout << endl;
	}
}

//srednia dla klasy
for (int k=0; k < KLA; k++){
  int sumK=0;
  for (int p=0; p < PRZ; p++){
    for (int u=0; u < UCZ; u++){
      sumK=sumK+OCENY[k][p][u];
    }
  }
  double sreK;
  //nalezy zamienić sumę typu int na typ double bo zle liczy
  sreK=double(sumK)/(PRZ*UCZ);
  cout << fixed << setprecision(2);
  cout << "Średnia klasy " << k << " = " << sreK << endl;
}
//srednia dla ucznia
for (int u=0; u < UCZ; u++){
  int sumU=0;
  for (int p=0; p < PRZ; p++){
    for (int k=0; k < KLA; k++){
      sumU=sumU+OCENY[k][p][u];
    }
  }
  double sreU;
  //nalezy zamienić sumę typu int na typ double bo zle liczy
  sreU=double(sumU)/(PRZ*KLA);
  cout << fixed << setprecision(2);
  cout << "Średnia ucznia " << u << " = " << sreU << endl;
}
//srednia dla przedmiotu
for (int p=0; p < PRZ; p++){
  int sumP=0;
  for (int u=0; u < UCZ; u++){
    for (int k=0; k < KLA; k++){
      sumP=sumP+OCENY[k][p][u];
    }
  }
  double sreP;
  //nalezy zamienić sumę typu int na typ double bo zle liczy
  sreP=double(sumP)/(UCZ*KLA);
  cout << fixed << setprecision(2);
  cout << "Średnia przedmiotu " << p << " = " << sreP << endl;
}

//srednia dla wybranego
cout << "Wybierz literę KPU ";
char z;
cin >> z;
//zamieniamy na duzy znak jesli byl mały
//w C++ nie trzeba zamieniać na char 
//aby dodawać w kodzie ascii
//if (z >= 97) z=z-32;
//tak powinno byc w normalnym jezyku
//zamieniamy na kod a potem spowrotem na znak
if (int(z) >= 97) z=char(int(z)-32);
//cout << z << endl;
cout << "Wybierz numer " << z << " ";
int n;
cin >> n;
for (int k=0; k < KLA; k++){
  for (int p=0; p < PRZ; p++){
    for (int u=0; u < UCZ; u++){
      if ((z == 'K' && k == n) || 
        (z == 'P' && p == n) || 
        (z == 'U' && u == n))
      cout << OCENY[k][p][u] << " ";
    }
  }
}
cout << endl;


//SZABLON
#include <stdlib.h>	//system
#include <iostream>	//cout
#include <iomanip>	//fixed
#include <cmath>	//pow
using namespace std;
int main(){
  setlocale(LC_ALL, "");


  system("pause");
}