/// <summary> /// Postupně vybudovává podstrom v rozsahovém stromu. Pro seřazení prvků využívá v listu metodu sort. /// </summary> /// <param name="seznamPrvku">Poskytne seznam prvků, které vstupují so stromu.</param> /// <param name="predek">Prvek rozsahového stromu.</param> /// <param name="predchoziZListu">Předcházející hodnota ze seznamu prvků, které se vkládají postupně do stromu.</param> /// <param name="dimenzeX">Parametr, aby bylo zřejmé, jestli se bude budovat od dimenze X nebo Y.</param> /// <returns>Vrací "pomocny", což je pomocný abstraktní uzel, který drží prvek rozsahového stromu, /// ať už jde o navigační vrchol nebo plnohodnotný prvek</returns> private PrvekRozsahovehoStromu VybudujPodstrom(List <T> seznamPrvku, PrvekRozsahovehoStromu predek, ref PrvekRozsahovehoStromu predchoziZListu, bool dimenzeX) { PrvekRozsahovehoStromu pomocny; if (seznamPrvku.Count >= 2) { // nastaveni intervalu pro navigacni prvek (ten ktery rika jake prvky jsou pod nim) if (dimenzeX == true) { // sort vraci void nikoli serazenou kolekci, tzn ze zmeni zdrojova data seznamPrvku.Sort(porovnejPodleX); // utridit seznam vrcholu, vzit prvni vrchol a jeho x ovou souradnici // pouzit jako (zacatek intervalu) navigacniho vrcholu a jeho dalsi souradnici // pouzit jako (konec intervalu) navigacniho vrcholu, tim vytvorit navigacni vrchol int zacatekIntervalu = seznamPrvku[0].vratX(); int konecIntervalu = seznamPrvku[seznamPrvku.Count - 1].vratX(); // vytvorit instanci prvku pomocneho rozsahoveho stromu - navigacniho vrcholu pomocny = new PrvekRozsahovehoStromu(zacatekIntervalu, konecIntervalu); } else { seznamPrvku.Sort(porovnejPodleY); int zacatekIntervalu = seznamPrvku[0].vratY(); int konecIntervalu = seznamPrvku[seznamPrvku.Count - 1].vratY(); pomocny = new PrvekRozsahovehoStromu(zacatekIntervalu, konecIntervalu); } //vytvoreni seznamů prvků pro levý a pravý podstrom, do nichž se prvky rozdělí //diky teto skvele metode dost pravdepodobne pri lichem poctu bude v pravem podstromu o jeden prvek vice int x = seznamPrvku.Count; //pocet prvku v seznamu int y = x / 2; //deleni int odsekava desetiny List <T> prvkyLevehoPodstromu = new List <T>(seznamPrvku.GetRange(0, y)); //getRange chce start index a pocetPrvku List <T> prvkyPravyhoPodstromu = new List <T>(seznamPrvku.GetRange(y, (x - y))); // vybudovani podstromů z rozdělených prvků, rekurze pomocny.levyPotomek = VybudujPodstrom(prvkyLevehoPodstromu, pomocny, ref predchoziZListu, dimenzeX); pomocny.pravyPotomek = VybudujPodstrom(prvkyPravyhoPodstromu, pomocny, ref predchoziZListu, dimenzeX); } // byl-li pro budování předán jediný prvek, stane se plnohodnotným prvkem else { // pokud je pocet prvku je jedna >?< - neni v Diplomce reseno pomocny = new PrvekRozsahovehoStromu(seznamPrvku[0]); // TODO: oveřít podmínku u if - pomocny != null? // zřetězení prvků na úrovni listů (plnohodnotné prvky) if (predchoziZListu != null) { predchoziZListu.dalsiPrvekRozsahovehoStromu = pomocny; pomocny.predchoziPrvekzRozsahovehoStromu = predchoziZListu; } predchoziZListu = pomocny; } // pro navig. vrchol ve stromu první dimenze je vybudován strom druhé dimenze if (dimenzeX == true && pomocny.platny == false) { pomocny.druhaDimenze = VybudujStrom(seznamPrvku, pomocny, false); } pomocny.otec = predek; return(pomocny); }
/// <summary> /// Pro vyhledání prvků v obdélníkovém segmentu. Hledání opět začíná od kořene stromu směrem k listům. Při /// dosažení plnohodnotného prvku(listu stromu) se zkontrolují jeho souřadnice, a pokud /// spadají do hledaného segmentu, vykoná se na aktuálním prvku zadaná akce.Při /// traverzování přes navigační vrcholy se rozhoduje, jakým způsobem v hledání pokračovat. /// Uplatňují se přitom daná pravidla. /// </summary> /// <param name="levyHorniBod"></param> /// <param name="pravyDolniBod"></param> /// <param name="vrchol"></param> /// <param name="dimenzeX"></param> /// <returns></returns> private void NajdiInterval(ISouradnice levyHorniRohIntervalu, ISouradnice pravyDolniRohIntervalu, PrvekRozsahovehoStromu vrchol, bool dimenzeX) { if (vrchol != null) { // při dosažení plnohodnotného prvku se zkontrolují jeho souřadnice if (vrchol.platny == true) { if (vrchol.nositelDat.vratX() >= levyHorniRohIntervalu.vratX() && vrchol.nositelDat.vratX() <= pravyDolniRohIntervalu.vratX() && vrchol.nositelDat.vratY() >= levyHorniRohIntervalu.vratY() && vrchol.nositelDat.vratY() <= pravyDolniRohIntervalu.vratY()) // TODO: k uvaze { vysledekIntervalovehoHledani.Add(vrchol.nositelDat); } } else { if (dimenzeX == true) { // patří-li celý interval do hledaného rozsahu, hledá se ve druhé dimenzi if (levyHorniRohIntervalu.vratX() <= vrchol.zacatekIntervalu && pravyDolniRohIntervalu.vratX() >= vrchol.konecIntervalu) { NajdiInterval(levyHorniRohIntervalu, pravyDolniRohIntervalu, vrchol.druhaDimenze, !dimenzeX); } // patří-li část intervalu do hledaného rozsahu, pokračuje se oběma syny else if (levyHorniRohIntervalu.vratX() >= vrchol.zacatekIntervalu || pravyDolniRohIntervalu.vratX() <= vrchol.konecIntervalu) { NajdiInterval(levyHorniRohIntervalu, pravyDolniRohIntervalu, vrchol.levyPotomek, dimenzeX); NajdiInterval(levyHorniRohIntervalu, pravyDolniRohIntervalu, vrchol.pravyPotomek, dimenzeX); } } else // patří-li celý interval do hledaného rozsahu, provede se prohlídka podstromu { if (vrchol.zacatekIntervalu >= levyHorniRohIntervalu.vratY() && vrchol.konecIntervalu <= pravyDolniRohIntervalu.vratY()) { // TODO funkce prohlidka (vsechny listy podstromu a ulozi je do listu) Prohlidka(levyHorniRohIntervalu, pravyDolniRohIntervalu, vrchol, dimenzeX); } else if (vrchol.zacatekIntervalu <= levyHorniRohIntervalu.vratY() || vrchol.konecIntervalu >= pravyDolniRohIntervalu.vratY()) { NajdiInterval(levyHorniRohIntervalu, pravyDolniRohIntervalu, vrchol.levyPotomek, dimenzeX); NajdiInterval(levyHorniRohIntervalu, pravyDolniRohIntervalu, vrchol.pravyPotomek, dimenzeX); } } } } }
/// <summary> /// Pomocná metoda pro postupné budování rozsahového stromu. /// </summary> /// <param name="seznamPrvku">Poskytne seznam prvků, které vstupují so stromu.</param> /// <param name="prvekDruheDimenze">prvekDruheDimenze, aby se odkázal Ypsylonovy interval do Xoveho intervalu.</param> /// <param name="dimenzeX">Parametr, aby bylo zřejmé, jestli se bude budovat od dimenze X nebo Y.</param> /// <returns>Vrací "pomocny", což je pomocný abstraktní uzel, který drží prvek rozsahového stromu, /// ať už jde o navigační vrchol nebo plnohodnotný prvek</returns> private PrvekRozsahovehoStromu VybudujStrom(List <T> seznamPrvku, PrvekRozsahovehoStromu prvekDruheDimenze, bool dimenzeX) { PrvekRozsahovehoStromu predchozi = null; PrvekRozsahovehoStromu pomocny = VybudujPodstrom(seznamPrvku, null, ref predchozi, dimenzeX); //případné spojení vrcholu podstromu s prvkem opačné dimenze if (prvekDruheDimenze != null) { pomocny.druhaDimenze = prvekDruheDimenze; } return(pomocny); }
/// <summary> /// Postupně buduje rozsahovy strom. Využívá k tomu metodu Vybuduj Strom a metodu Vybuduj podstrom. /// Pokud je předáné více než jeden prvek (seznamPrvků) vzniká tzv. navigační vrchol hlavního stromu. /// Za první dimenzi je v realizaci zvolena souřadnice x, do navigačního se předá nejmenší možný interval se všema prvkama. /// Ty se následně seřadí podle první dimenze a rozdělí se na poloviny.První polovina bude tvořit /// levý podstrom navigačního vrcholu, druhá polovina pravý podstrom. Toto se opakuje dokud nezbyde jeden poslední prvek, /// ten se pak stane liste, kde se zřetězí s dalšíma prvkama. /// </summary> /// <param name="seznamPrvku"></param> public void Vybuduj(List <T> seznamPrvku) { if (seznamPrvku == null || seznamPrvku.Count == 0) { throw new Exception("Strukturu Rozsahový strom nešlo vybudovat, list vrcholů je prázdný nebo neexistuje."); } else { pocetPrvkuVeStrukture = seznamPrvku.Count; //vybudovani stromu prvni dimenze koren = VybudujStrom(seznamPrvku, null, true); } }
// prohlidka(PrvekRange < T > vrchol, AkceCallback akce, bool dimenzeX) – metoda // provádějící prohlídku, dotraverzuje k nejlevějšímu listu stromu, jehož kořen odpovídá // parametru vrchol, a následně projde zřetězený seznam se všemi plnohodnotnými prvky, // nad kterými vykoná zvolenou akci(během fáze ladění navíc kontroluje korektnost // umístění prvků ve struktuře), private void Prohlidka(ISouradnice levyHorniRohIntervalu, ISouradnice pravyDolniRohIntervalu, PrvekRozsahovehoStromu vrchol, bool dimenzeX) { // dotraverzovat k platnemu vrcholu if (vrchol != null) { if (vrchol.platny == true) { if (vrchol.nositelDat.vratX() >= levyHorniRohIntervalu.vratX() && vrchol.nositelDat.vratX() <= pravyDolniRohIntervalu.vratX() && vrchol.nositelDat.vratY() >= levyHorniRohIntervalu.vratY() && vrchol.nositelDat.vratY() <= pravyDolniRohIntervalu.vratY()) // TODO: k uvaze { vysledekIntervalovehoHledani.Add(vrchol.nositelDat); if (vrchol.dalsiPrvekRozsahovehoStromu != null) { Prohlidka(levyHorniRohIntervalu, pravyDolniRohIntervalu, vrchol.dalsiPrvekRozsahovehoStromu, dimenzeX); } } } else { if (vrchol.levyPotomek != null) { Prohlidka(levyHorniRohIntervalu, pravyDolniRohIntervalu, vrchol.levyPotomek, dimenzeX); } } } }
/// <summary> /// Hledání prvku se zadanými souřadnicemi probíhá pouze ve stromu první dimenze, který je /// organizován podle zeměpisné šířky.Prohledávání začíná od kořene stromu směrem /// k listům.V každém navigačním vrcholu se testuje, zda interval vrcholu obsahuje hledanou /// hodnotu zeměpisné šířky. V případě záp**né odpovědi hledání skončí, protože lze /// jednoznačně určit, že hledaný prvek se v range stromu nenachází. V opačném případě se /// pokračuje do obou podstromů aktuálního vrcholu. Při dosažení plnohodnotného prvku /// (listu stromu) se porovnají jeho souřadnice se souřadnicemi hledanými. --text z literatury-- /// </summary> /// <param name="obeSouradnice">Proměnná uchovávající obě souřadnice.</param> /// <returns>Prvek Rozsahového stromu </returns> public T Najdi(ISouradnice obeSouradnice) { // je-li kořen platným vrcholem, porovnají se souřadnice a hledání skončí if (koren != null && koren.platny == true) { if (koren.nositelDat.vratX() == obeSouradnice.vratX() && koren.nositelDat.vratY() == obeSouradnice.vratY()) { return(koren.nositelDat); } else { return(default(T)); } } else { // v opačném případě je strom prohledáván traverzováním směrem k listům PrvekRozsahovehoStromu pomocny = koren; while (true) { if (pomocny == null) { return(default(T)); } // je-li levý syn platným vrcholem, porovnají se jeho souřadnice if (pomocny.levyPotomek != null && pomocny.levyPotomek.platny == true && pomocny.levyPotomek.nositelDat.vratX() == obeSouradnice.vratX() && pomocny.levyPotomek.nositelDat.vratY() == obeSouradnice.vratY()) { return(pomocny.levyPotomek.nositelDat); } // je-li pravý syn platným vrcholem, porovnají se jeho souřadnice if (pomocny.pravyPotomek != null && pomocny.pravyPotomek.platny == true && pomocny.pravyPotomek.nositelDat.vratX() == obeSouradnice.vratX() && pomocny.pravyPotomek.nositelDat.vratY() == obeSouradnice.vratY()) { return(pomocny.pravyPotomek.nositelDat); } // jinak se rozhodne, kterým směrem pokračovat // TODO: projit vetsi mensi jestli je spravne nakodovano if (pomocny.levyPotomek != null && pomocny.levyPotomek.platny == false && pomocny.levyPotomek.zacatekIntervalu <= obeSouradnice.vratX() && pomocny.levyPotomek.konecIntervalu >= obeSouradnice.vratX()) { pomocny = pomocny.levyPotomek; } else if (pomocny.pravyPotomek != null && pomocny.pravyPotomek.platny == false && pomocny.pravyPotomek.zacatekIntervalu <= obeSouradnice.vratX() && pomocny.pravyPotomek.konecIntervalu >= obeSouradnice.vratX()) { pomocny = pomocny.pravyPotomek; } else { return(default(T)); } } } }