# Vyhledávání

- Vyhledávání je velmi často prováděná činnost
- **Zjišťujeme**, **zda je prvek určité hodnoty** (též prvek s hodnotou klíče) **přítomen v poli**
	- Někdy stačí informace, zda je či není (```true```/```false```)
	- Většinou je potřeba zjistit index, na kterém se hledaný prvek nachází
- Typ vyhledávání
	- **Neúplné**
		- Nalezneme **první výskyt** prvku
	- **Úplné**
		- Nalezneme **všechny výskyty** prvku
- Výsledek neúplného vyhledávání
	- Typicky první index od začátku pole, na kterém se prvek nachází (prvek může být v poli obsažen vícekrát), nebo záporná hodnota, pokud se prvek v poli nenachází (typické použití, protože index prvku nemůže být záporný)
	- Pokud je důležité pouze, zda prvek v poli je či není, ale není důležité kde je, je výsledek vyhledávání pouze ```true``` (prvek je obsažen) nebo ```false``` (prvek není obsažen)
- Výsledek úplného vyhledávání
	- Pole (případně výpis) všech indexů, na kterých se hledaný prvek nachází, nebo prázdné pole, pokud prvek není v poli obsažen
	- Pokud není důležité, na kterých indexech se prvek nachází, ale zajímá nás, kolikrát je v poli obsažen, výsledkem je počet výskytů prvku (0 pokud prvek není obsažen)

### Vyhledávání v neseřazené posloupnosti (poli) 

- Pokud není pole, ve kterém prvek hledáme, seřazené, je jediná možnost sekvenční vyhledávání 

#### Neúplné sekvenční vyhledávání v poli základních datových typů

- Neúplné sekvenční vyhledávání má **složitost Ο(n)**
	- V **nejhorším případě je nutné projít celé pole**, tedy všech n prvků 
- Princip vyhledávání
	- Procházíme pole od začátku do konce a porovnáváme hodnoty prvků pole s hodnotou hledaného prvku
	- Když prvek nalezneme, ukončíme procházení pole a vrátíme index, na kterém jsme prvek nalezli
	- Pokud dojdeme až do konce pole a prvek nenajdeme, vrátíme zápornou hodnotu (typicky ```-1```)

#### Neúplné sekvenční vyhledávání v poli objektů

- Vyhledávání v poli objektů je velice podobné, jako vyhledávání v poli základních datových typů 
	- Pro porovnání ale **nemůžeme použít operátor** „```==```“, protože pro objekty vrací true pouze v případě, že se jedná o stejnou instanci
		- Můžeme **porovnávat přímo jeden či více atributů instance**
		- Můžeme **využít metodu** ```equals()```, **pokud je** v dané třídě **správně implementovaná**

#### Úplné sekvenční vyhledávání

- Úplné sekvenční vyhledávání má **složitost Ο(n)**
	- V každém případě **je nutné projít celé pole**, tedy všech n prvků
- Princip vyhledávání
	- **Princip** je **stejný jako u neúplného sekvenčního vyhledávání**, pouze **neukončíme procházení pole při nalezení prvního výskytu** prvku, ale projdeme pole **vždy až do konce**
	- Protože indexů s pozicemi prvků je více, nestačí vrátit jeden index  místo jednoho indexu **vrátíme pole s jednotlivými indexy**
		- Délka pole indexů může být maximálně stejná, jako je počet prvků prohledávaného pole a minimálně může být 0, pokud hledaný prvek nebyl v poli nalezen
		- Počet výskytů hledaného prvku v poli (a tedy délku pole indexů) na začátku neznáme
			- Délku pole indexů tedy volíme jako délku prohledávaného pole
			- Délku můžeme po skončení algoritmu zkrátit vytvořením kratší kopie pole na skutečný počet indexů 

### Vyhledávání v seřazené posloupnosti (poli)

- **Pokud je posloupnost seřazená** (předpokládáme vzestupně, ale mohla by být i sestupně), je možné **použít vyhledávání půlením intervalů** (též binární vyhledávání)
	- Sekvenční vyhledávání je možné použít také, stejně jako na neseřazenou posloupnost, ale je podstatně pomalejší, takže není důvod ho používat, pokud je posloupnost seřazená 
	- Pokud provádíme vyhledávání opakovaně a pořadí prvků v prohledávaném poli není důležité, vyplatí se pole jednou seřadit a následně opakovaně používat vyhledávání půlením intervalů

#### Neúplné vyhledávání půlením intervalů v poli základních datových typů

- Neúplné vyhledávání půlením intervalů (binární vyhledávání) má **složitost Ο(log₂n)**
	- Čas vyhledávání tedy roste pouze s logaritmem počtu prvků prohledávaného pole  pro **velký počet prvků** prohledávaného pole je **podstatně rychlejší než sekvenční vyhledávání**
- Princip vyhledávání
	- V každém kroku rozdělíme prohledávaný interval na dvě poloviny a následně hledáme jen v jedné z polovin
		- Zjistíme hodnotu prvku ležícího na prostředním indexu
		- Pokud je rovna hledané hodnotě, algoritmus končí
		- Pokud je větší než hledaná hodnota, hledáme v levé polovině
		- Pokud je menší než hledaná hodnota, hledáme v pravé polovině

#### Úplné vyhledávání půlením intervalů

- Pokud je hledaný prvek v poli obsažen vícekrát, vyhledávání půlením intervalů najde jeden z výskytů, ale není jasné, který výskyt to je
	- Nalevo i napravo od nalezeného indexu se mohou vyskytovat prvky se stejnou hodnotou
	- Pro úplné vyhledávání je potřeba sekvenčně prohledat pravé i levé okolí nalezeného indexu, dokud se nenarazí na jiný prvek nebo konec či začátek pole
	- Protože stejné prvky jsou v seřazeném poli vždy u sebe, není třeba vracet pole všech indexů, na kterých se hledaný prvek nachází, stačí vrátit první a poslední index

#### Metody pro vyhledávání půlením intervalů z Java Core API

- Třída ```Arrays``` obsahuje metody pro vyhledávání půlením intervalů
	- Metoda ```Arrays.binarySearch(pole, klíč)```
		- Prohledává celé pole
	- Metoda ```Arrays.binarySearch(pole, od, do, klíč)```
		- Prohledává pouze část pole udanou indexy ```od``` (včetně) a ```do``` (vyjma)
	- Metody jsou překryté pro pole všech základních datových typů a pro pole objektů
	- Prohledávané pole musí být seřazené, typicky vzestupně (typicky knihovní metodou ```Arrays.sort()```)
	- Metoda vrací index nalezeného prvku, nebo zápornou hodnotu, pokud prvek nebyl nalezen
		- Tato hodnota obecně není ```-1```  nutno testovat hodnotu, zda je menší než ```0```, nikoliv rovna ```-1```
		- Absolutní hodnota záporného čísla udává index, na kterém by prvek byl, kdyby v poli byl 

#### Vyhledávání půlením intervalů v poli objektů

- Pokud bychom chtěli použít binární vyhledávání v poli objektů, je nutné určit, podle čeho se mají objekty porovnávat (podle jakého atributu)
	- Je nutné seřadit pole podle tohoto vybraného porovnání a stejné porovnání použít i v algoritmu binárního vyhledávání
	- Nejrozumnější je použít metodu ```compareTo()```, kterou jsme použili při ručním i knihovním řazení
- Pro správné použití knihovní metody ```Arrays.binarySearch()``` je nezbytné, aby třída, jejíž instance jsou v poli, implementovala rozhraní ```Comparable<Třída>```
	- Hlavička třídy, jejíž instance jsou v prohledávaném poli, musí být ```public Třída implements Comparable<Třída>```
	- Musí obsahovat metodu ```public int compareTo(Třída třída)```
	- Podrobnosti viz předměty KIV/PPA2 a KIV/OOP

