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ó.
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.
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ő.