200 likes | 510 Views
Algoritmai ir duomenų struktūros ( AD S). 3 paskaita Saulius Ragaišis , VU MIF saulius.ragaisis@mif.vu.lt 20 1 0- 0 2-1 9. Duomenų struktūra Prioritetinė eilė.
E N D
Algoritmai ir duomenų struktūros(ADS) 3 paskaita Saulius Ragaišis, VU MIF saulius.ragaisis@mif.vu.lt 2010-02-19
Duomenų struktūra Prioritetinė eilė Gyvenime ne kartą esame susidūrę su situacija, kai yra eilė, bet atsiranda žmonių, kurie aptarnaujami be eilės. Jei tokių atsiranda daug, tai susidaro eilė “be eilės”... Apibendrinę turime tokią situaciją: - gali būti daugiau nei viena eilė; - kiekvienos eilės elementai turi vienodus prioritetus; - pirmiausia aptarnaujami elementai iš eilės su didžiausiu prioritetu; - elementai su vienodu prioritetu aptarnaujami eilės tvarka. Duomenų struktūra, užtikrinanti tokį elementų aptarnavimo eiliškumą, vadinama prioritetine eile (angl. Priority Queue). Kiekvienas elementas ne tik saugo tam tikrus duomenis, bet ir turi prioritetą, kuris ir apibrėžia elemento aptarnavimo tvarką (žinoma, svarbi ir elementų “pasirodymo” tvarka).
Prioritetinės eilės operacijos • Sukurti tuščią prioritetinę eilę • Patikrinti, ar prioritetinė eilė tuščia • Patikrinti, ar prioritetinė eilė pilna • Įdėti naują elementą į prioritetinę eilę • Išimti elementą iš prioritetinės eilės • Sunaikinti prioritetinę eilę Toliau aptarsime prioritetinės eilės realizacijas jau žinomomis duomenų struktūromis. Svarbu atkreipti dėmesį, kad realizacijų aptarime vaizduojami tik elementų prioritetai, o pačios elementų reikšmės nevaizduojamos.
Prioritetinės eilės realizacija masyvu Realizacijoje masyvu elementai dažniausiai išdėstomi prioritetų didėjimo tvarka. Tai užtikrina, kad elementas su didžiausia prioriteto reikšme bus masyvo gale. Išėmimas: išimamas paskutinis elementas. Įdėjimas: randama teisinga pozicija – elementas turi būti įterptas pagal savo prioritetą o tarp turinčių tokį prioritetą kaip pirmasis – o po to perstumiami visi elementai, kad atsirastų vietos naujam elementui.
Prioritetinės eilės realizacija tiesiniu sąrašu Realizacijoje tiesiniu sąrašu elementai dažniausiai išdėstomi prioritetų mažėjimo tvarka, t.y. elementas su didžiausia prioriteto reikšme yra sąrašo pradžioje. Išėmimas: išimamas pirmas elementas. Įdėjimas: einama sąrašu tol, kol randama elementui tinkama vieta – naujas elementas turi būti įterptas prieš pirmą elementą, turintį mažesnį prioritetą už naują elementą arba sąrašo gale.
7 9 4 6 1 Prioritetinės eilės realizacija dvejetainiu paieškos medžiu Elementai dėstomi kaip tradiciniame dvejetainiame paieškos medyje tik ne pagal raktus, bet pagal prioritetus. Tokios realizacijos ypatumas yra tame, kad elementas su didžiausia prioriteto reikšme bus pačioje dešinėje viršūnėje. Taigi reikia sekti dešiniuosius vaikus, kol atsiras viršūnė be dešiniojo vaiko. Būtina atkreipti dėmesį, kad viršūnėje saugomas ne 1 elementas, bet eilė elementų, turinčių viršūnėje nurodytą prioritetą.
Duomenų struktūra Piramidė Piramidė (angl. Heap) yra duomenų struktūra, panaši į dvejetainį paieškos medį, bet skiriasi nuo pastarojo dviem esminiais aspektais. Visų pirma paieškos medžiuose duomenys yra surikiuoti, o piramidėje – ne. Tačiau tai, kaip duomenys yra organizuoti piramidėje, leidžia efektyviai realizuoti prioritetinės eilės operacijas, tokias kaip: sukurti; patikrinti, ar eilė yra tuščia; įterpti ir išmesti. Kitas esminis skirtumas yra tas, kad dvejetainiai paieškos medžiai gali būti įvairūs (pvz., AVL medis, Raudonai-juodas medis). Piramidė visada yra užbaigtas (angl. complete) dvejetainis medis, todėl jei jo maksimalus dydis yra žinomas iš anksto, tai realizacija masyvu yra labai efektyvi.
Duomenų struktūra Piramidė (2) Apibrėžimas. Duomenų struktūra Piramidė (angl. Heap) - užbaigtas dvejetainis medis, kurio šaknies prioriteto reikšmė yra didesnė arba lygi kiekvieno jos vaiko prioriteto reikšmei ir abu šaknies pomedžiai yra duomenų struktūros piramidės. Kitaip nei dvejetainiame paieškos medyje, nėra jokio sąryšio tarp vaikų reikšmių, t.y. nežinoma, kurio iš vaikų reikšmė didesnė. Operacijos: • elemento išmetimas; • elemento įdėjimas. Būtina atkreipti dėmesį, kad jei piramidė naudojama prioritetinės eilės realizacijai, tai viršūnėje saugomas ne 1 elementas, bet eilė elementų, turinčių viršūnėje nurodytą prioritetą.
Duomenų struktūra Vektorius Java klasė Vector: dinaminis masyvas, į kurį galima dėti bet kokius objektus... Pagrindinės operacijos: • Sukurti: Vector() • Patikrinti, ar tuščias: boolean isEmpty() • Išvalyti: void removeAllElements() • Išimti elementą:void removeElementAt(int index) • Įterpti elementą nurodytoje vietoje: void insertElementAt(Object obj, int index) • Pridėti elementą gale: void addElement(Object obj) • Pakeisti elemento reikšmę: void setElementAt(Object obj, int index) • Gauti elemento reikšmę: Object elementAt(int index) • Sužinoti dydį: int size() Klasė turi ir daug kitų naudingų operacijų.
Hanojaus bokštų uždavinys Duoti trys stulpai ir N diskų. Ant vieno (pradinio) stulpo sumauti diskai didėjimo tvarka, einant iš viršaus į apačią. Reikia visus diskus perkelti nuo pradinio stulpo ant laisvo (tikslo) stulpo, pasinaudojant atsarginiu stulpu. Apribojimai: 1. Per vieną ėjimą galima nuimti tik vieną diską ir jį būtina iš karto uždėti ant kito stulpo. 2. Didesnio disko negalima dėti ant mažesnio.
Rekursinis Hanojaus bokštų uždavinio sprendimo algoritmas Jei diskas tik vienas (N=1), uždavinys elementarus – tiesiog perkeliame šį 1 diską nuo pradinio stulpo ant tikslo stulpo. Tarkime, kad mes mokame išspręsti uždavinį su N-1 disku. Tada norėdami perkelti N diskų elgiamės taip: • N-1 diską nuo pradinio stulpo perkeliame ant atsarginio stulpo (pagal prielaidą tai mes jau mokame padaryti); • tada ant pradinio stulpo likusį 1 diską (didžiausią) tiesiog perkeliame ant tikslo stulpo; • N-1 diską nuo atsarginio stulpo perkeliame ant tikslo stulpo (pagal prielaidą tai mes jau mokame padaryti).
Rekursinės procedūros pseudokodas arba pažymėjimai:count - diskų skaičius, source - pradinis stulpas, dest - tikslo stulpas, spare - tarpinis stulpas
Žingsnių skaičiaus įvertinimas Pažymėkime žingsniai(N) žingsnių skaičių, reikalingą perkelti N diskų. Kai N = 1, tai žingsniai(1) = 1. Kai N > 1, procedūra Towers iškviečiama 3 kartus: perkelti N-1 diską, perkelti 1 diską ir vėl perkelti N-1 diską. Taigi galime apskaičiuoti žingsniai(N) pagal tokią rekurentinę formulę: žingsniai(N) = žingsniai(N-1) + žingsniai(1) + žingsniai(N-1) = 2 * žingsniai(N-1) + 1 Žingsnių skaičių galima apskaičiuoti pagal tokią formulę: žingsniai(N) = 2N - 1 (N1) Šios formulės teisingumą įrodysime matematinės indukcijos metodu. Patikriname jos teisingumą, kai N=1. žingsniai(1) = 21 - 1 = 2 - 1= 1 Tarkime, kad ji teisinga su N-1 (t.y. žingsniai(N-1) = 2N-1 – 1), tada: žingsniai(N) = 2 * žingsniai(N-1) + 1 = 2 * (2N-1 – 1) + 1 = 2N – 2 + 1 = 2N - 1 Įrodymas baigtas.
Perrinkimas Gyvenime tenka susidurti su užduotimis, kuriose reikia rasti sprendinį iš daug galimų variantų. Vienas iš sprendimo būdų yra perrinkti (patikrinti) galimus variantus, bet variantų dažnai būna tiek daug, kad perrinkimas visų galimų variantų yra praktiškai neįmanomas. Reikia kažkaip apriboti nagrinėjamus variantus, tačiau apribojant negalima prarasti egzistuojančio sprendinio. Dažnai sprendinio radimui pakanka perrinkti tik tam tikrus variantus. Šios problemos sprendimo būdą detaliau panagrinėsime su tokiu uždaviniu: šachmatų lentoje sustatyti 8 karalienes taip, kad nei viena iš jų nekirstų kitos.
8 karalienių uždavinys Viena iš strategijų yra perrinkti visus galimus variantus ir iš jų išrinkti tinkamą. Viso variantų yra 4 426 165 368. Galima pastebėti, kad yra ypatybė, kuri žymiai sumažina perrinkimų skaičių: vienoje eilutėje ir viename stulpelyje negali būti pastatytos dvi karalienės. Kitaip sakant, kiekvienas stulpelis ir kiekviena eilė gali turėti tik vieną karalienę. Perrinkimų skaičius sumažėja iki 8!=40 320. Prieš tolimesnį nagrinėjimą reikėtų susipažinti su tokia strategija kaip valdymas su grįžimais (angl. backtracking).
Valdymas su grįžimais Apibrėžimas. Valdymas su grįžimais (angl. Backtracking) – tai strategija, kuri leidžia grįžti atgal po žingsnį ir pasirinkti kitą sprendimo vystymo kryptį, jei atsirado atvejis, kai sprendimo nėra ar jis netinkamas.
8 karalienių uždavinio sprendimas (1) Padarom prielaidą, kad pirma karalienė stovės pirmo stulpelio pirmame kvadrate. Tada antra karalienė stovės antro stulpelio trečiame kvadrate, nes pirmame ir antrame kvadratuose ją kirstų pirma karalienė. Bendra taisyklė: sekančią karalienę statome į pirmą neužimtą sekančio stulpelio kvadratą. Reikia pastebėti, kad taip sustatytos penkios karalienės kerta visus šešto stulpelio kvadratus.
8 karalienių uždavinio sprendimas (2) Sugrįžtame atgal į penktą stulpelį ir perstatome karalienę į kitą kvadratą (backtracking strategija), pavyzdžiui, į paskutinį. Tačiau tai dar neišsprendžia problemos. Penkios karalienės vis tiek kirs šeštąją kiekviename šešto stulpelio langelyje.
8 karalienių uždavinio sprendimas (3) Reikia grįžti prie ketvirto stulpelio ir perstatyti ketvirtą karalienę, pavyzdžiui, į septintą kvadratą, o penktą karalienę - pavyzdžiui, į penkto stulpelio antrą kvadratą. Dabar galima pastatyti šeštą karalienę į šešto stulpelio ketvirtą kvadratą. Tuo pačiu būdu galima sustatyti ir likusias karalienes.