383 lines
20 KiB
Plaintext
383 lines
20 KiB
Plaintext
Adatstruktúrák és algoritmusok
|
|
|
|
A problémamegoldás menete
|
|
Való problémák => absztrakt modellezés => algoritmus => program
|
|
|
|
Algoritmus
|
|
Az algoritmus egy hatékony eljárás egy feladat vagy probléma megoldására, melynek helyessége bizonyítható.
|
|
|
|
Hatékonyság
|
|
A hatékonyságot a futási idő és a memóriaigény határozza meg.
|
|
|
|
|
|
Algoritmusok futásidő elemzése
|
|
Futási idő: Egy bizonyos bemenetre a végrehajtott (gépfüggetlen) alapműveletek, vagy lépések száma.
|
|
|
|
A lépésszám pontos meghatározása helyett általában elegendő a lépésszám nagyságrendjének meghatározása,
|
|
és ebből már (kis óvatossággal) következtetni lehet arra,
|
|
hogy az algoritmus mennyire hatékony, avagy hogyan fog viselkedni nagyobb értékekre.
|
|
|
|
Aszimptotikus Hatékonyság
|
|
Ha a bemenet mérete elég nagy, akkor az algoritmus futási idejének csak a nagyságrendje érdekes
|
|
|
|
Az O ordó jelölés
|
|
Az O jelölést arra használjuk, hogy a futási idő növekedését aszimptotikusan alulról és felülről konstans távolságra behatároljuk.
|
|
|
|
Például a bináris keresés futási ideje legrosszabb esetben O(log n), helytelen lenne azt állítani, hogy a bináris keresés futási ideje O (log n) minden esetben.
|
|
A bináris keresés futási ideje soha nem rosszabb, mint O (log n), de van amikor ennél jobb.
|
|
|
|
Jó lenne egy olyan aszimptotikus jelölés ami azt fejezné ki, hogy "a futási idő maximum ennyivel nő, de ennél lassabban is nőhet." Erre használjuk az O jelölést.
|
|
|
|
Ha a futási idő O(f(n)), akkor elég nagy n esetén a futási idő maximum k*f(n) valamilyen konstans k érték mellett.
|
|
|
|
Azt mondjuk "f(n) ordója" vagy egyszerűen "O(f(n))" (kiejtésben használatos még az "ordó f(n)" is).
|
|
Az O jelölést aszimptotikus felső korlátként használjuk, mivel a futási időt felülről korlátozza, ha az input mérete elég nagy.
|
|
|
|
Bináris keresés futási ideje: O(log<sup>2</sup>n)
|
|
|
|
|
|
|
|
Tömb
|
|
A tömb egy olyan adatszerkezet, amely menet közben nem méretezhető át.
|
|
Tehát ha új elemeket szeretnénk egy meglévő tömbhöz adni, az csak úgy fog működni,
|
|
hogy létrehozunk egy új tömböt, ami az új elemek és a meglévő elemek tárolására alkalmas,
|
|
ezután pedig bemásoljuk a meglévő elemeket és az új elemeket a teljesen új tömbünkbe.
|
|
|
|
Tömbökben referencia típusokat is alkalmazhatunk,
|
|
viszont ebben az esetben nem elég példányosítani a tömböt,
|
|
az egyes elemeket is példányosítani kell, mivel ebben az esetben a tömb csak az objektumra mutató referenciát tárolja,
|
|
így a példányosítás nélkül a tömb elemeinek értéke null lesz.
|
|
|
|
Az osztályokat nem muszáj a konstrukroruk segítségével példányosítani.
|
|
Erre a célra vezették be a nyelvben az Object Initializer szintaxist,
|
|
amivel egy osztály adattagjai úgy adhatóak meg, mint egy tömb elemei.
|
|
|
|
Ez akkor jön jól, ha van egy osztályunk, amely adattagokkal rendelkezik,
|
|
de a konstruktor az objektum minden adattagjának beállításához nagyon összetett és komplikált lenne.
|
|
Ebben az esetben nem érdemes konstruktort írni. Az objektum inicializáló szintaxis a következő:
|
|
|
|
var objektum = new osztály(){
|
|
Adattag = érték,
|
|
Adattag2 = érték,
|
|
Adattag3 = érték
|
|
};
|
|
|
|
Ez a szintaxis csak olyan adattagok esetén alkalmazható, amelyek publikusan is írhatóak.
|
|
Egyéb védelmi szinttel rendelkező adattagok nem írhatóak ezzel a módszerrel.
|
|
Ezen adattagok beállítására továbbra is a konstruktor szintaxis használható.
|
|
|
|
Az osztályokat
|
|
|
|
Tömbök kezelését segítő metódusok
|
|
|
|
Length{ get;}
|
|
|
|
long Length{get;}
|
|
Visszaadja az aktuális tömb elemeinek a számát hosszú egész típusban.
|
|
Akkor jön jól, ha nagyon nagy méretű tömböket szeretnénk kezelni.
|
|
|
|
int Rank{get;}
|
|
Visszaadja a tömb dimenzióinak a számát
|
|
|
|
Ezen tulajdonságokon kívül az Array osztály számos statikus metódust tartalmaz,
|
|
amelyeket felhasználhatunk tömbök kezelésére.
|
|
Ezek közül a leghasznosabbak és legfontosabbak:
|
|
Array.Clear(Array array, int index,int length);
|
|
|
|
Array.Copy(Array sourceArray, Array destinationArray, int length);
|
|
Array.Copy(Array sourceArray, Array destinationArray, long length);
|
|
//harmadik paraméter a másolandó elemek száma
|
|
|
|
Array.Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length);
|
|
|
|
int Array.IndexOf(Array array, object value);
|
|
int Array.IndexOf(Array array, object value, int startIndex);
|
|
|
|
int Array.LastIndexOf(Array array, object value);
|
|
int Array.LastIndexOf(Array array, object value, int startIndex);
|
|
|
|
Array.Reverse(Array array);
|
|
Array.Reverse(Array array, int index, int length);
|
|
// második elem a kezdőelem indexét adja meg, a harmadik az elemk számát adja meg
|
|
|
|
Array.Sort(Array array);
|
|
//Akkor ha az IComperable<T> implem,entálva van
|
|
|
|
Array.Sort(Array keys, Array items);
|
|
//Két tömb elemeinek a sorbarendezése, méghozzá úgy,hogy az első paraméterként megadott tömb kulcsokat tartalmaz,
|
|
//amelyhez a második paraméterként megadott tömb értékek társulnak.
|
|
|
|
Adatstruktúra interfészek
|
|
A kollekciókhoz kapcsolódó interfészek közül az egyik legfontosabb az IEnumerable<T> interfész.
|
|
Ez teszi lehetővé, hpgy mindegyik kollekción implementáéciótól függetlenül végig tudjuk uteráléni egy foreach ciklussal.
|
|
|
|
Az absztrakció következő szintje az IReadOnlyCollection és a ICollection interfészek.
|
|
Az ICollection<T> egy módosítható kollekciót ír le aminek at elemei törölhetőek és bővíthetőek.
|
|
A IReadOnlyCollection<T> pedig egy olyat,amiből csak olvasni tudunk, de tudjuk az elemek számát.
|
|
|
|
Az olyan típusok esetén, mint a lista halmaz és ... meg van valósítva mind a kettő.
|
|
|
|
Láncolt lista
|
|
A tömb adatszerkezet kiváló, ha előre tudjuk, hogy menni elemre van szükségünk.
|
|
A bővítés csak úgy lehetséges,ha létrehozunk egy újabb tömböt, aminek a mérete a hozzáadandó elemek számával meg van növelve.
|
|
Az új tömbbe átmásoljuk a meglévő elemeit, majd az új tömbhöz hozzáadjuk az új elemeket.
|
|
Végezetül pedig töröljük az eredeti tömböt.
|
|
|
|
Az algoritmus leírásából kiolvasható, hogy et nem éppen ideális,mivel a sebességre igen negatív hatással van a másolás.
|
|
Továbbá a másolás folyamán egy rövid időre duplázódik a programunk memóriahasználata.
|
|
|
|
Egy sokkal jobb megoldása lehet nagy mennyiségű előre nem ismert számú adat tárolására a láncolt lista szerkezet.
|
|
|
|
A láncolt lista egy eleme két részből épül fel. Egyrészt tartalmazza a tárolni kívánt adaott,
|
|
vagy adatokat és tartalmaz egy olyan mutatót, ami a lista eg másik elemét mutatja.
|
|
Ha a referencia a következő elemre nem létezik, akkor a lánc végén vagyunk.
|
|
|
|
A láncolt lista a dinamikus tömbhöz képest hátránya a közbülső elemnek elérhatőségéből ered.
|
|
Míg egy tömb esetén ha tudjuk,hogy a k. elemet szeretnénk elérnim akkor a tömb indexelésével rögtön hozzáférhefünk ehhet az adathoz, addog a láncolt listában a lista elejéről indulva a mutatókon keresztül addig kell lépkedni, míg a k. elemhez nem értünk.
|
|
A véletlenszerű lista elem megtalálása a lista hosszával arányos időt igényel.
|
|
|
|
Egyszeresen láncolt lista
|
|
Egyszeresen láncolt listában egy darab mutató jelöli a lista rákövetkező elemét.
|
|
Ha ismerjük a lista legelső elemét (lista feje), akkor abból elindulva a mutatók segítségével végig járhatjuk a listában tárolt elemeket.
|
|
A lista legutolsó elemének mutatójának értéke null, ez jelzi, hohgy tovább nem tudunk haladni a listában.
|
|
Láncolt lista esetén általában egyszeresen láncolt listára gondolunk.
|
|
|
|
Csomopont osztály: az adatot és a következő elemre mutató referenciát tartalmazza.
|
|
LancoltLista osztály: tartalmazza a listához szükséges főbb műveletekez:
|
|
Hozzaad: új elemet ad a lista végéhez
|
|
Torol: töröl
|
|
Kiir: kiír
|
|
|
|
Pogram osztály:
|
|
Teszteli a fenit műveleteket, létrehozza a listát, hozzáad elemeket,
|
|
töröl egy létező és egy nem létező elemet, majd kiírja a lista tartalmát.
|
|
|
|
Kétszeresen láncolt lista
|
|
Kétszeresen láncolt lsuita esetán 2db hivatkozűs van egy csomópontban,
|
|
az egyik az előző a másik a következő csomópontra mutat, C# ban kétszeresen láncolt listák vannak.
|
|
Hasonlóan mint a listák, szintén osztályból vannak létrehozva és ezért referencia típus,
|
|
természetesen referencia másolás történik értékadásnál.
|
|
|
|
Mint a listák esetében is a LinkedList beírása utám a <> jelek közé kerül a láncolt listánk típusa és a megszokott név,
|
|
egyenlőségjel a new operátor valamint újra a LinkedList és el ne felejtsük a zárójeleket!
|
|
|
|
LinkedList<string> lancoltlista = new LinkedList<string>();
|
|
|
|
Ezután a Linkedlist.AddLast(érték) metódussal tudunk a listánk végére beszúrni egy elemet,
|
|
vagy például az AddFirst(érték) metódussal pedig a lista első helyére.
|
|
|
|
Láncolt listák fontos metódusai
|
|
LinkedList.RemoveLast/First törli a lista utolsó vagy első elemét.
|
|
|
|
LinkedListNode<T> = LinkedList.Last/First
|
|
Visszaadja egy adott listában szereplő első vagy utolsó csomópontot, amelyben megtalálhatjuk a következő csinópontra mutató hivatkozást.
|
|
|
|
A LinkedList<T> objektumban minden csomópont LinkedListNode<T> típusú.
|
|
Mivel a LinkedList<T> kétszeresen kapcsolódik, minden csomópont előre mutat a köevtkező csomópontra, illetve hátra az előzőre.
|
|
|
|
LinkedList.AddAfter(LinkedListNode, value) / AddBefore
|
|
Egy csomópont elé vagy után szúr be adatot.
|
|
|
|
LinkedListNode<T> = LinkedList.Find(keresett_ertek)
|
|
Visszaadja a megadott értékhez tartozó csomópontot.
|
|
|
|
Csomópontok - LinkedListNode
|
|
Egy listából létrehozunk egy-egy csomópontot, melyben az adott csomóponthoz tartozó érték és a következő csomópontra mutató hivatkozás van.
|
|
A következő csomópontra való ugráshoz a .Next metódust kell használnunk, hogy visszafele közlekedjünk pedig a .Previous metódust kell használnunk.
|
|
A .Value metódus az adott csomópontban eltárolt adatot adja vissza.
|
|
Mivel tudjuk, hogy a láncolt listák utolsó, illetve első eleme null értékű, így egy while ciklussal is végig tudunk menni az adott láncolt listán,
|
|
a léptetéséről a Next gondoskodik.
|
|
|
|
Verem (stack)
|
|
A verem egy olyan adatszerkezet, amelyben az utoljára betett elemet tudjuk feldolgozni.
|
|
(LIFO)
|
|
A veremmutató mindig a legfelső elemre mutat.
|
|
A push a tetejére rak , a pop onnan vesz el.
|
|
|
|
A Stack<T> osztály fontosabb tulajdonságai és metódusai:
|
|
Stack(int capacity)
|
|
Paraméteres konstruktor. A paraméter a kiindulásként tárolni kivánt elemej számát adja meg.
|
|
Stack(IEnumerable<T> collection)
|
|
Paraméteres konstruktor.
|
|
A verem elemei aparaméterklnt megadott IEnumerable felület implementáló osztály elemei lesznek.
|
|
|
|
T Peek()
|
|
Visszaadja a verem tetején lévő elemet anélkül, hogy azt kivenne a verembol.
|
|
|
|
T Pop()
|
|
Visszaadja a verem tetején lévő elemet és az elemet eltávolítja a veremből.
|
|
|
|
void Push()
|
|
A paraméterkéntmegadott elemet beilleszti a verem tetejére.
|
|
|
|
T[] ToArray()
|
|
A verem elemeit visszaadja egy tömbben.
|
|
|
|
void TrimExcess()
|
|
Átméretezi a veremet úgy, hogy csak annyi elemnek foglaljon helyet, mint amennyi ténylegesen használva van.
|
|
|
|
Verem osztály
|
|
Generikus (a generikus fogalom a programozásban olyan technikára utal,
|
|
amely lehetővé teszi, hogy osztályok, metódusok, vagy adatszerkezetek különböző típusoknál ..)
|
|
|
|
program osztály:
|
|
létrehoz egy Verem<int> példányt 5 kapacitással.
|
|
Bemutatja az összes műveletet, beleértve a verem túlcsordulásának és kiürítésének kezelését.
|
|
|
|
Fák
|
|
#TODO egészítsd ki ezt a részt
|
|
|
|
Hash függvény, Hash tábla, Hasító tábla
|
|
A hash egy rögzített hosszúságú érték, amelyet egy matematikai képlet segítségével állítanak elő.
|
|
A hash értékeket adattömörítésben, kriptológiában stb. használják.
|
|
Az adatindexelésében a hash értéket használjuk, mert rögzített hosszúságúak, függetlenül a generálásukhoz használt értékektől.
|
|
Lehetővé teszi, hogy a ......
|
|
|
|
A hah fgvény egy matematikai algoritmust alkalmaz a kulcs hash-é alakítására.
|
|
Az ütközés akkor következik be, ha egy hash függvény ugyanazt a hash értéket állítja elő több kulcshoz.
|
|
|
|
Hash tábla
|
|
A hash tábla egy olyan adatstruktúra, amely kulcs értékpár használatával értéket tárol.
|
|
Minden értékhez egydi kulcs van hozzárendelve, amelyet egy hash függvény segítségével állítanak elő.
|
|
|
|
A kulcs neve a hozzá tartozó érték elérésére szolgál.
|
|
Ez nagyon felgyorsítja az értlkek keresését a hash táblában,
|
|
függetlenül a hash táblában lévő elemek számától.
|
|
|
|
Hash funkciók
|
|
Például, ha az alkalmazottak nyilvántartásait szeretnénk tárolni,
|
|
és minden alkalmazott egyedileg azonosítható egy alkalmazotti szám segítségével.
|
|
Kulcsként használhatjuk az alkalmazotti számot,
|
|
értékként a munkavállalói adatokat rendelhetjük hozzá.
|
|
|
|
A hash függvény megoldja a fenti problémát azáltal, hogy lekéri az alkalmazottu számot,
|
|
és ennek segítségével generál egy hash egész értéket,
|
|
rögzített számjegyeket, és optimalizálja a tárhelyet.
|
|
A hash függvény célja egy kulcs létrehozása, amely a tárolni kívánt értékre hivatkozik.
|
|
A függvény elfogadja a mentendő értéket, majd egy algoritmus segítségével kiszámítja a kulcs értékét.
|
|
|
|
Dictionary
|
|
|
|
A szótár elempárok tárolására szolgál,
|
|
melyek közül egyik a kulcs, amely azonosítja az elempárt,
|
|
másik az érték, minden kulcs egyedi.
|
|
|
|
Gyakorlatilag a szótár úgy viselkedik,
|
|
mint egy lista, de az elemek indexe itt tetszőleges típusú lehet pl.: szöveg.
|
|
|
|
Konstruktora generikus, paraméter nélküli:
|
|
Dictionary<TKey, TValue>(): létrehoz egy szótárt, ahol Tkey a kulcs TValue az érték típusa.
|
|
|
|
A szótár elemei a [] operátorral érhetőek el.
|
|
A szótárat foreach ciklussal lehet végig olvasni, amellyel a szótárból KeyValuePair<Tkey, Tvalue>
|
|
típusú elemeket kapunk. Ezek Key value mezői adják a megfelelő kulcs és érték párokat.
|
|
|
|
Fő metódusai:
|
|
Add(TKey, TValue)
|
|
Bool ContainsKey(TKey)
|
|
Bool ContainsValue(TValue)
|
|
bool Remove(Tkey) // ha sikeres a művelet akkor true
|
|
int Count()
|
|
void Clear() szótár ürítése
|
|
|
|
Elemek összehasonlítása
|
|
Sok esetben lehet szükségünk az elemek sorba rendezésére,
|
|
ehhez a Syste.Collections.Generic.IComparer<T>
|
|
interfész megvalósító osztályra van szükségünk.
|
|
A T azon adattípus, amelyen majd összehasonlítást végzünk.
|
|
|
|
Ennek fő metódusai
|
|
int Compare (T x, T y):
|
|
összehasonlít két azonos típusú elemet, visszatérési értéke:
|
|
negatív ha x kisebb mint y
|
|
nulla, ha x egyenlő y
|
|
pozitív ha x nagyobb mint y
|
|
|
|
Hashmaps alapjai
|
|
A Hashmaps hatékony megoldást kínál az adatok hatékony tárolására ás visszakeresésére.
|
|
A C# ban a HashMaps-t a Dictionary <Tkey, Tvalue> osztály képviseli,
|
|
amely alapvető eszközként szolgál a kulcs értél párok kezeléséhez.
|
|
Lényegében a HashMap kulcs-érték asszociációk gyűjteményeként működik,
|
|
lehetővé téve azb egyedi kulcsokon alapuló értékekhez valóü gyors hozzáférést.
|
|
|
|
A Hashmaps azon elven működik, hogy egyedi kulcsokat rendel hozzá a megfelelő értékekhez.
|
|
Ezek a kulcsok a társított értékek azonosítóként működnek, gyors hozzáférést biztosítva az adatokhoz
|
|
anélkül, hogy a teljes gyűjteményt végig kellene ismételni.
|
|
|
|
A C# nyelvben a HashMaps a Dictionary<Tkey, Tvalue>
|
|
osztály használatával példányosodik, amely a Tkey a kulcsok típusát,
|
|
a TValue pedig az értékek típusát jelöli.
|
|
|
|
Dictionary<string, int> ageMap = new Dictionary<string, int>();
|
|
|
|
HashMap műveletek
|
|
Elemek hozzáadása és visszakeresésére
|
|
A HashMap-hez elemek hozzáadása magában foglalja az Add() metódust,
|
|
amely értéket rendel egy adott kulcshoz.
|
|
|
|
Az értékek lekérése a HashMap-ról úgy érhető el, hogy az értéket a megfelelő kulcsokkal érik el.
|
|
int ageOfAlice = ageMap["Alice"];
|
|
|
|
Létezés ellenőrzése
|
|
.ContainsKey(TKey)
|
|
|
|
Elemek eltávolítása
|
|
.Remove(TKey)
|
|
|
|
Iterálás a HashMap-bemenet
|
|
használj foreach ciklust
|
|
minden elem kulcs érték párt add vissza
|
|
|
|
HashMap teljesítménye
|
|
A HashMap CSharpban kiváló teljesítményjellemzőket kínál a visszakeresési beillesztési és törlési műveletekhez.
|
|
Az alapul szolgáló megvalósítás hash kódokat használ az elemek hatékony lokalizálására és kezelésére, ami állandó idejű O1
|
|
bonyulultságot eredményet a legtöbb műveletnél.
|
|
|
|
Fontos azonban megjegyezni, hogy a tényleges teljesítmény olyan tényezőktől függően változhat, mint az elemek száma, a hash kód ütközései és a terhalési tényező.
|
|
|
|
|
|
Bináris Keresés
|
|
|
|
A bináris - más néven logaritmikus vagy felező módszer -
|
|
mivel n elem esetén log(n) futási idővel rendelkezik.
|
|
|
|
A bináris keresés egy erősen optimalizált keresési eljárás,
|
|
amely csak rendezett adatsoron alkalmazható.
|
|
Pélédául, amikor egy nyomtatott szótárban keresünk egy szót vagy jelenléti íven keressük a nevünket.
|
|
A keresett értéket egy mintaadattal összehasonlítjuk és az eredménytől függően - amennyire lehet -
|
|
egy nagy részt kizárunk a tartományból.
|
|
|
|
A módszer külön implementációt nem igényel, mivel az Array és a list osztály os tartalmaz bináris keresésre implementációt.
|
|
|
|
int index = Array.BinarySearch(Array array, object value);
|
|
|
|
Buborékrendezés
|
|
|
|
A rendezés során a tömb fennmaradó részén végighaladva az egymás utáni szomszédos elemeket összehasonlítjuk, és ha szükséges, megcseréljük őket, hogy közölük mindig a nagyobb helyezkedjen el feljebb.
|
|
|
|
Pszeudokód:
|
|
Ciklus i = 6 -tól 1-ig
|
|
Ciklus j = 0-tól i-1-ig
|
|
Ha a[j] > a [j+1] AKKOR
|
|
csere a[j] a[j+1]
|
|
|
|
Az algoritmusnak csak akkor van értelme tovább futnia, ha a belső ciklusban volt csere.
|
|
|
|
A javított algoritmus futási ideje legjobb esetben lineáris lesz,
|
|
legrosszabb esetben pedig négyzetes.
|
|
|
|
Beszúrásos rendezés
|
|
|
|
A beszúró rendezés nevét onnan kapta, hogy a működése leginkább a kártyalapok egyenként való kézbevételéhez s a helyükre igazításához hasonlítható.
|
|
|
|
Vesszük a soron következő elemet, és megkeressük a helyét a tőle balra lévő,
|
|
már rendezett részben, majd a kereséssel párhuzamosan a nagyobb elemeket rendre eggyel jobbra mozgatjuk.
|
|
Az aktuális elemet egy segédváltozóban tároljuk,
|
|
mert a mozgatások során értéke felülíródhat egy nagyobb elemmel.
|
|
|
|
Ezen algoritmus használata akkor igazán előnyös,
|
|
ha az adatsorunk már részben rendezett.
|
|
Továbbá akkor igen hatékony, ha egy rendezett sorozatot bővítünk és a bővítés után is szeretnénk,
|
|
hogy a sorozat rendezett maradjon.
|
|
|
|
Az algoritmus futási ideje legjobb esetben konstans, legrosszabb esetben négyzetes. |