Hernyák zoltán)
16. fejezet - Listákkal kapcsolatos feladatok (szerző: Hernyák Zoltán)
A feladatok alapszintű listafeldolgozással megoldhatóak. Ennek során kell egy vagy több lista, melynek feltöltése nem feltétlenül fontos része a feladatoknak. A megoldások során a kiinduló adatokat tartalmazó listákat általában nem kell módosítani, új listákat kell előállítani.
16.1. feladat (Maximum – szint: 2). Olvassuk be egy lista tartalmát billentyűzetről valamely, az előző feladatban megadott módszer szerint! Kérjünk be egy újabb értéket (A), mely érték a program kezelője „szerint” a listabeli számok maximuma! A program ellenőrizze, hogy ez valóban a maximum-e!
Magyarázat: Óvatosan lássunk az ellenőrzésnek: nem elég, hogy a listában nem találunk az A értéknél nagyobb számot, az A értéknek szerepelnie kell a listabeli számok között is! Persze nekiláthatunk a megoldásnak oly módon is, hogy meghatározzuk a lista kalkulált maximumát, és összevetjük az A értékkel – de az kevésbé izgalmas (lásd 16.1. forráskód).
16.2. feladat (Páros számok maximuma – szint: 3). Olvassuk be egy lista tartalmát billentyűzetről valamely, az előző feladatban megadott módszer szerint! A program adja meg a listában szereplő páros számok közül a legnagyobb számértéket (ügyeljünk arra az esetre, mikor a listában minden szám páratlan)!
Magyarázat: A maximumkeresés egyszerű feladat, ha tudjuk a megfelelő kezdőértéket. A kezdőértéknek a lista első elemét szoktuk választani, mely választás egyúttal akár a végeredmény, a maximum is lehet. De ez esetben nem tehetjük, mivel nem lehetünk biztosak abban, hogy az első elem páros-e. Azt sem tudhatjuk, hogy a második páros-e. Lehet, hogy egyik sem páros, és nem találunk kezdőértéket.
Meg kellene keresnünk az első páros számot, kiválasztani mint kezdőértéket, majd folytatni a keresést. Ez igazából két ciklust igényel, megoldása a 16.2. forráskódban látható. Kissé erőforrás-pazarlóbb, de algoritmikusan jóval áttekinthetőbb megoldás, ha a lista páros elemeit átmásoljuk egy második listába. A továbbiakban ezen lista elemszáma alapján azonnal megállapítható, hogy van-e egyáltalán páros szám, illetve könnyű megkeresni a maximumot (lásd a 16.3. forráskódot).
16.3. feladat (Unió, metszet – szint: 2). A 15.7. feladatban ismertetett módon olvassunk be egyetlen fájlból két számlistát (A és B)! Fogjuk fel az egyes számlistákat mint egy-egy számhalmazt! Generáljuk le az , számhalmazokat, és írjuk ki az értékeket a képernyőre! Ügyeljünk arra, hogy az unió és metszet listákban ne szerepeljen egyetlen szám sem kétszer!
Magyarázat: Ez az egyszerű alapalgoritmusok használatát jelenti, speciálisan listára kialakítva. Hogy kissé érdekesebb legyen a megoldás, írjuk át olyan függvényekre, melyek a két listát paraméterként megkapva a generált listát adják meg visszatérési értékként! Mindkét részfeladat igényli a listabeli számok egyediségét, így érdemes a 15.11. forráskódban már bemutatott egyediség-ellenőrző függvényt alkalmazni.
Az unio művelet képzése nagyon egyszerű: mindkét listából szükséges minden elemet befogadni, ami még nem szerepelt. A megoldást lásd a 16.4. forráskódban. A metszet kicsit bonyolultabb ellenőrzési mechanizmusú:
olyan elemeket keresünk, amelyek szerepelnek az A és B listában, de nem szerepelnek még a metszetben (16.5.
forráskód).
16.4. feladat (Erdei ösvény – szint: 3). Egy erdei ösvényen vizes pocsolyák és száraz szakaszok váltják egymást. Szimbolizálja a szárazföldet a numerikus 1, a vizes részt a 2 érték!
Töltsünk fel egy listát véletlenszerű 1 és 2 értékekkel oly módon, hogy nagyobb valószínűséggel kerüljön bele víz, mint szárazföld (például 70%-30% arányban)! Legyen a lista első és utolsó eleme garantáltan 1, vagyis szárazföld! A kész listát jelenítsük meg a képernyőn oly módon, hogy a szárazföldet a # (hashmark), a vizet a _ (aláhúzás) karakterrel jelöljük! A konkrét arányokat a program kérje be billentyűzetről!
Az erdei ösvény egyik végén áll Piroska, és szeretne átjutni az ösvény másik végére a nagymamához. Piroska n hosszú pocsolyás szakaszt képes átugorni (n értékét kérjük be
billentyűzetről). A program generálja le a listát adott arányokkal, jelenítse meg a képernyőn, majd adja meg, hogy Piroska át tud-e kelni az ösvényen száraz lábbal (16.1. ábra)!
Magyarázat: A feltöltést adott valószínűséggel a geometriai valószínűségek szerinti módszerrel oldhatjuk meg.
Sorsoljunk véletlen számot intervallumban! Ha az kisebb, mint 30egyenlő, akkor víz. A megjelenítést is egyszerű megoldani a korábbi feladatok alapján.
16.1. ábra. Piroska és az ösvény
Piroska átkelési problémája során érdemes bevezetni egy számlálót, melyben a szomszédos víz elemek számát számoljuk. Ha a lista következő eleme víz – növeljük a számlálót. Ha szárazföld, akkor lenullázzuk. Ekképpen már csak a számláló maximális értékét kell meghatároznunk (a 16.6. forráskód).
16.5. feladat (Szigetek – szint: 3). Tegyük fel, hogy Amerika partjaitól elindul egy repülő Európa partjai felé! A repülő egyenes vonalban egyenletes sebességgel repül végig adott magasságban. A repülés során a felszín magasságát méri, melyet rögzít. A számértékek méterben értendők, és egy listába kerülnek be. A víz felszínén mért magasságérték mindig 0, és negatív magasságot nem lehet mérni. A pozitív értékek a víz fölé kiemelkedő szárazföldet mutatnak. A lista első és utolsó értéke értelemszerűen pozitív szám (Amerika partvidékéről indulunk, és Európa partvidékének elérésekor áll le a mérés). A két pont között vízfelszíni és szárazföldi szakaszok váltják egymást.
A program töltse fel a listát véletlen értékekkel, de a feladat szövegében foglaltaknak megfelelően! Vagyis ne egyszerű nulla és nem nulla értékek váltsák egymást véletlenszerűen, hanem ha vízfelszíni szakasz kezdődik, akkor az folyamatos legyen, valamint a szárazföldi szakasz is felismerhető legyen! A szárazföldi szakaszok mindig egy-egy szigetet képviselnek (16.2. ábra).
A program határozza meg egy feltöltött lista alapján:
• hány sziget található Amerika és Európa között,
• hanyadik sorszámú szigeten található a legmagasabb pont (hegycsúcs),
• milyen hosszú a leghosszabb sziget (egységekben)!
Magyarázat: Ügyeljünk a szélsőséges esetekre: elképzelhető, hogy Amerika és Európa között nincs vizes szakasz (egybefüggő szárazföldet alkot). Másik eset: nincs sziget, a két pont között egybefüggő vizes szakasz terül el. A legmagasabb hegycsúcs esetén elképzelhető, hogy ezen maximális magasság ugyanazon szigeten belül többször vagy több szigeten is előfordulhat. Szintén ügyeljünk arra, hogy a legmagasabb pont ne Európa vagy Amerika partvidékéhez tartozzon, hanem valamely szigeten forduljon elő!
16.2. ábra. A repülőgép útvonala
A lista feltöltése úgy történhet, hogy kisorsoljuk, hogy víz vagy szárazföld következzen, majd a szakasz hosszát.
Ezek után megfelelő mennyiségű 0-t vagy nem 0-t helyezünk el a listában. Ügyeljünk rá, hogy a lista első és utolsó eleme ne 0 legyen! A program maradék részét úgy kell megírnunk, hogy ne függjön a feltöltési mechanizmusunktól (ne építsen semmilyen ott szerzett tudásra)!
Ezek után a piroskás feladatban ismertetett módon meg kell számolni a 0 szakaszok hosszát. Valahányszor 0-ról nem 0-ra váltunk, sziget kezdődik (kivéve az utolsó ilyet, ahol Európa kezdődik). Ügyeljünk rá, hogy ha nem volt, csak 1 ilyen váltás, akkor nincs sziget a két kontinens között! Ha 0 ilyen váltás volt, akkor összefüggő szárazföld van.
A leghosszabb sziget meghatározásához a Piroska leghosszabb vizes szakaszának meghatározásánál leírtakból kell kiindulni. A maximális pont keresése sem problémás, mivel tudjuk, hogy egyik pont sem alacsonyabb 0 méternél, így a maximumkeresés kiinduló értéke lehet a 0. Mivel meg kell számolnunk, hány sziget van, nem nehéz a maximum megtalálásakor feljegyezni a sziget sorszámát is.
1. A fejezet forráskódjai
16.1. forráskód. A lista maximumának ellenőrzése
// List<int> L = new List<int>();
bool nagyobb_volt = false;
bool szerepelt = false;
foreach (int x in L) {
if (x == A) szerepelt = true;
if (x > A) nagyobb_volt = true;
}
if (nagyobb_volt == false && szerepelt == true) Console.WriteLine("eltalalta a maximumot");
else
Console.WriteLine("nem talalta el");
16.2. forráskód. A legnagyobb páros szám keresése
// List<int> L = new List<int>();
int i = 0;
while (i < L.Count && L[i] % 2 != 0) i++;
if (i < L.Count) {
int max = L[i];
for (int j = i + 1; j < L.Count; j++) if (L[j] % 2 == 0 && L[j] > max) max = L[j];
Console.WriteLine("A paros max={0}", max);
}
else Console.WriteLine("Nincs paros eleme a listanak");
16.3. forráskód. Egyszerűbb algoritmus, több memórialekötés
// List<int> L = new List<int>();
List<int> lp = new List<int>();
foreach (int x in L)
if (x % 2 == 0) lp.Add(x);
if (lp.Count == 0)
Console.WriteLine("Nincs paros eleme");
else {
int max = lp[0];
foreach (int x in lp) if (x > max) max = x;
Console.WriteLine("Paros max = {0}", max);
}
16.4. forráskód. Az unió függvénye
static List<int> unio(List<int> A, List<int> B) {
List<int> ret = new List<int>();
foreach (int x in A)
if (szerepel_e(ret, x) == false) ret.Add(x);
foreach (int x in B)
if (szerepel_e(ret, x) == false) ret.Add(x);
return ret;
}
16.5. forráskód. A metszet függvénye
static List<int> metszet(List<int> A, List<int> B) {
List<int> ret = new List<int>();
foreach (int x in A)
if (szerepel_e(ret, x) == false && szerepel_e(B,x)==true) ret.Add(x);
return ret;
}
16.6. forráskód. A leghosszabb vizes szakasz hossza
static int leghosszabb(List<int> L) {
int db = 0;
int max = 0;
foreach (int x in L) {
if (x == 2) db++;
else db = 0;
if (db > max) max = db;
}
return max;
}