pak celou úlohu. ani Jako obvykle je static int M, N; [] key, L, R; NIL = -1; cost; roota, rootb; throws IOExceptio // tree roots on { static void

Podobné dokumenty
ALGORITMIZACE 2010/03 STROMY, BINÁRNÍ STROMY VZTAH STROMŮ A REKURZE ZÁSOBNÍK IMPLEMENTUJE REKURZI PROHLEDÁVÁNÍ S NÁVRATEM (BACKTRACK)

ALGORITMIZACE 2010/03 STROMY, BINÁRNÍ STROMY VZTAH STROMŮ A REKURZE ZÁSOBNÍK IMPLEMENTUJE REKURZI PROHLEDÁVÁNÍ S NÁVRATEM (BACKTRACK)

Stromy. Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta a kol.

BINARY SEARCH TREE

BINARY SEARCH TREE

1. Téma 12 - Textové soubory a výjimky

R zné algoritmy mají r znou složitost

Dynamické programování. Optimální binární vyhledávací strom

5 Rekurze a zásobník. Rekurzivní volání metody

Dynamické datové struktury III.

Stromy. Příklady. Rekurzivní datové struktury. Základní pojmy

Dynamic programming. Optimal binary search tree

Pokud zadání nerozumíte nebo se vám zdá nejednoznačné, zeptejte se. Pište čitelně, nečitelná řešení nebudeme uznávat.

IB108 Sada 1, Příklad 1 Vypracovali: Tomáš Krajča (255676), Martin Milata (256615)

Pokud zadání nerozumíte nebo se vám zdá nejednoznačné, zeptejte se. Pište čitelně, nečitelná řešení nebudeme uznávat.

Stromy. Strom: souvislý graf bez kružnic využití: počítačová grafika seznam objektů efektivní vyhledávání výpočetní stromy rozhodovací stromy

Stromy, haldy, prioritní fronty

Radek Mařík

Spojové struktury. Spojová struktura (linked structure):

Datové struktury obsah přednášky 1. Úvod 2. Třídy Type-wrapper (obalový typ) pro primitivní typy automatické převody 3. Automatické převody mezi

Základy algoritmizace c2005, 2007 Michal Krátký, Jiří Dvorský1/39

Volné stromy. Úvod do programování. Kořenové stromy a seřazené stromy. Volné stromy

Programování v C++ 1, 16. cvičení

Dynamické datové struktury IV.

Algoritmy a datové struktury

AVL stromy. pro každý uzel u stromu platí, že rozdíl mezi výškou jeho levého a pravého podstromu je nejvýše 1 stromy jsou samovyvažující

Datové struktury. alg12 1

součet cvičení celkem. známka. Úloha č.: max. bodů: skut. bodů:

Čipové karty Lekařská informatika

Pokud zadání nerozumíte nebo se vám zdá nejednoznačné, zeptejte se. Pište čitelně, nečitelná řešení nebudeme uznávat.

VYSOKÉ UČENÍ TECHNICKÉ V BRNĚ

Seznamy a iterátory. Kolekce obecně. Rozhraní kolekce. Procházení kolekcí

Stromy. Jan Kybic.

Datové struktury obsah přednášky 1. Úvod 2. Třídy Type-wrapper (obalový typ) pro primitivní typy automatické převody 3. Automatické převody mezi

Typický prvek kolekce pro české řazení

Abstraktní datové typy

Základní datové struktury III: Stromy, haldy

Reprezentace aritmetického výrazu - binární strom reprezentující aritmetický výraz

prohled av an ı graf u Karel Hor ak, Petr Ryˇsav y 16. bˇrezna 2016 Katedra poˇ c ıtaˇ c u, FEL, ˇ CVUT

Fronta (Queue) Úvod do programování. Fronta implementace. Fronta implementace pomocí pole 1/4. Fronta implementace pomocí pole 3/4

Generické programování

Datové typy v Javě. Tomáš Pitner, upravil Marek Šabo

Hledání k-tého nejmenšího prvku

Stromy. Jan Hnilica Počítačové modelování 14

Rekurzivní algoritmy

Úvod do programovacích jazyků (Java)

Adresní vyhledávání (přímý přístup, zřetězené a otevřené rozptylování, rozptylovací funkce)

Abstraktní datové typy: zásobník

Red Black strom (Red Black Tree) Úvod do programování. Rotace. Red Black strom. Rotace. Rotace

Intervalové stromy. Představme si, že máme posloupnost celých čísel p 0, p 1,... p N 1, se kterou budeme. 1. Změna jednoho čísla v posloupnosti.

Rekurze a zásobník. Jak se vypočítá rekurzivní program? volání metody. vyšší adresy. main(){... fa(); //push ret1... } ret1

A4B33ALG 2010/05 ALG 07. Selection sort (Select sort) Insertion sort (Insert sort) Bubble sort deprecated. Quicksort.

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

Datové struktury. Obsah přednášky: Definice pojmů. Abstraktní datové typy a jejich implementace. Algoritmizace (Y36ALG), Šumperk - 12.

Práce s textem. Třída Character. Třída Character. Třída Character. reprezentuje objekty zapouzdřující hodnotu typu char (boxing / unboxing)

Programování v Javě I. Leden 2008

Tabulka symbolů. Vazba (binding) Vazba - příklad. Deklarace a definice. Miroslav Beneš Dušan Kolář

Více o konstruktorech a destruktorech

Část 1 Spojové struktury (stromy) Dynamické struktury a Abstraktní Datový Typy (ADT) Část 2 Abstraktní datový typ. Část 3 Příklad ADT Prioritní fronta

OOPR_05. Případové studie

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Rozklad problému na podproblémy

Algoritmizace prostorových úloh

PŘETĚŽOVÁNÍ OPERÁTORŮ

Dynamické datové struktury II.

Dynamicky vázané metody. Pozdní vazba, virtuální metody

Vyvažování a rotace v BVS, všude se předpokládá AVL strom

IAJCE Přednáška č. 8. double tprumer = (t1 + t2 + t3 + t4 + t5 + t6 + t7) / 7; Console.Write("\nPrumerna teplota je {0}", tprumer);

Spojová implementace lineárních datových struktur

Dynamické struktury a Abstraktní Datový Typy (ADT)

Programování 3. hodina. RNDr. Jan Lánský, Ph.D. Katedra informatiky a matematiky Fakulta ekonomických studií Vysoká škola finanční a správní 2015

20. Projekt Domácí mediotéka

TGH07 - Chytré stromové datové struktury

Principy objektově orientovaného programování

Programování v Javě I. Únor 2009

Vyhledávání. doc. Mgr. Jiří Dvorský, Ph.D. Katedra informatiky Fakulta elektrotechniky a informatiky VŠB TU Ostrava. Prezentace ke dni 12.

Semin aˇr Java V yjimky Radek Ko ˇc ı Fakulta informaˇcn ıch technologi ı VUT Unor 2008 Radek Koˇc ı Semin aˇr Java V yjimky 1/ 25

Class loader. každá třída (java.lang.class) obsahuje referenci na svůj class loader. Implementace class loaderu

Java Výjimky Java, zimní semestr

ALG 04. Zásobník Fronta Operace Enqueue, Dequeue, Front, Empty... Cyklická implementace fronty. Průchod stromem do šířky

Bubble sort. příklad. Shaker sort

Kolekce, cyklus foreach

Dynamické programování

STACK

bin arn ı vyhled av an ı a bst Karel Hor ak, Petr Ryˇsav y 23. bˇrezna 2016 Katedra poˇ c ıtaˇ c u, FEL, ˇ CVUT

ÚVODNÍ ZNALOSTI. datové struktury. správnost programů. analýza algoritmů

Generování vnitřní reprezentace programu

Algoritmy výpočetní geometrie

PG 9.5 novinky ve vývoji aplikací

Správa paměti. Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta, 2016

ALG 09. Radix sort (přihrádkové řazení) Counting sort. Přehled asymptotických rychlostí jednotlivých řazení. Ilustrační experiment řazení

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

Implementace LL(1) překladů

Algoritmizace Dynamické programování. Jiří Vyskočil, Marko Genyg-Berezovskyj 2010

DobSort. Úvod do programování. DobSort Implementace 1/3. DobSort Implementace 2/3. DobSort - Příklad. DobSort Implementace 3/3

Algoritmizace a programování

Iterator & for cyklus

Struktura programu v době běhu

WORKSHEET 1: LINEAR EQUATION 1

Teoretické minimum z PJV

Transkript:

Úloha nevžaduje žádnou zvláštníí manipulacii se stromy nebo jejich uzly, kroměě jediné neustále opakované operace Insert, proto bude vhodné volitt reprezentaci pokud možno úsporně. Nejprve ukáže me řešení pro redukovanou úlohu, kdybychom měli měřit pouze obyčejný BVS. Přechod k AVL pak bude jednoduchým (byť uvážlivým) rozšířením. Obr. 1. Příklad jednoduché reprezentace BVS.. ALG-BVS a AVL stromy - komentář a řešení Dopředuu víme, kolik bude uzlů, a proto si je očíslujeme 0,1, 0..., M+N 1. Hodnoty klíčů můžeme uložit do jednorozměrného pole key indexovaného právě čísly uzlů. Protože samotné uzly jako takové (jako objekty) vůbec nebudeme potřebovat, může celý uzel představovat p t jediná položka pole key. Reference na n pravéhoo a levého potomka uzlu můžeme pakk opět uložit do dalších dvou 1D polí L a R, které budou indexovány stejně a budou obsahovat indexy levého a pravého potomka každého uzlu. S těmito datovými strukturami jsme již vybaveni pro celou úlohu. Vkládání nového uzluu do stromu provedeme funkcí Insert, jež je rekurzivní, kódd je kratší a riziko přeplnění zásobníku nevelké. Přesun všech uzlů ze stromu B do A zařídí kratičká rovněž rekurzivní funkce, jiným způsobem ji ani nemá cenu psát. S další funkcí pro vybudování stromů opakovaným voláním funkcee Insert a funkcí pro načtení a inicializaci dat a datových struktur jsme prakticky hotovi, uvedený kód snad nepotřebuje další zdlouhavá vysvětlení. Jako obvykle je nejdelší funkce načítající data. M, N; [] key, L, R; NIL = -1; cost; roota, rootb; // node keys Left and Rightt children // null pointer // to be calculated // tree roots public static void main(string[] args) readandinitall(); makebsttrees(); transfertree(rootb); System.out.printf( ("%d\n", cost); throws IOExceptio on { static void makebsttrees(){ L[rootA] = R[rootA] = L[rootB] = R[rootB] = NIL; cost = 2; // cost of the roots for(int inode = 1; inode < M; inode+ ++) Insert(rootA, inode); for(int inode = M+ +1; inode < M+N; inode++) Insert(rootB, inode); static void transfertree(int if (inode == NIL) return ; transfertree(l[inode]); transfertree(r[inode]); Insert(rootA, inode); inode) {

Insert(int iparent, int inode){ int [] nextbranch = (key[inode] < key[iparent] )? L : R; if (nextbranch[iparent]!= NIL) return Insert(nextBranch[iParent], inode) ; nextbranch[iparent] = inode; L[iNode] = R[iNode] = NIL; return inode; static void readandinitall() throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); M = Integer.valueOf(st.nextToken()); N = Integer.valueOf(st.nextToken()); // initialize all structures roota = 0; rootb = M; L = new int [M+N]; R = new int [M+N]; for(int i = 0; i < M+N; i++) L[i] = R[i] = NIL; key = new int [M+N]; key[0] = 519217; for(int i = 1; i < key.length; i++) key[i] = (int) (( 754043 *(long) key[i-1] + 500009) % 1000003); Při přechodu k AVL stromu zachováme strukturu předchozího programu, pouze operaci Insert je nutno vhodně rozšířit o postup zpět z vloženého uzlu ke kořeni a případnou rotaci. U rotace je nutno dát pozor, může proběhnout i v kořeni a pak se změní kořen celého stromu. Rotace v AVL stromu jsou teoreticky čtyři: L, R, LR a RL. Každá ze dvojitých rotací RL a LR se skládá ze dvou jednoduchých rotací, takže dvojité rotace nemusíme explicitně programovat. Rotace R je ale strukturně zcela totožná s rotací L, až na to, že je jejím zrcadlovým obrazem. Kdybychom si nakreslili strom na skleněné dveře, tak by nám stačilo umět provádět jen např. levou rotaci, protože při potřebě pravé rotace bychom jen obešli dveře z druhé strany a z tohoto nového pohledu bychom udělali levou rotaci. V rotacích nehrají roli klíče uzlů, tedy ani pořadí klíčů ve stromu, a proto tento náš malý trik je bude strom upravovat korektně. V implementaci podobného efektu dosáhneme ještě snáze. Stačí soustavně prohodit ukazatele (reference) na levé a pravé potomky uzlů a spolu s nimi ukazatele (reference) na výšky levých a pravých podstromů registrované v AVL. Dejme tomu, že naprogramujeme pouze rotaci L. Abychom ji mohli snadno používat, budeme předpokládat, že postupujeme stromem nahoru stále zprava doleva. Tak zajistíme, že i do rozváženého uzlu příjdeme vždy zprava zdola, a tím bude i dáno, že jediné rotace, které nás mohou čekat, budou vždy jednoduchá L rotace nebo dvojitá RL rotace. I neustálý postup zprava doleva nahoru je snadné zařídit, prostě vždy při přechodu z uzlu do jeho rodiče zjistíme, zda je uzel levým nebo pravým potomkem tohoto rodiče. Pokud je pravým potomkem, postoupíme rovnou vzhůru (zprava doleva), a pokud je levým potomkem, obejdeme pomyslné dveře, resp. prohodíme reference na pravé a levé potomky a opět postoupíme zprava doleva nahoru. Funkce pro rotaci a funkce pro postup vzhůru stromem ovšem musí pracovat s kopiemi (jsou to formální pole Lptr, Rptr, LHptr, RHptr v implementaci) původních ukazatelů (referencí) na skutečná data, protože původní ukazatele (reference) nesmí být ovlivněny, pomocí nich je nutno přistupovat k dané struktuře stromu i v jiných okamžicích než při postupu vzhůru nebo při rotacích.

M, N; [] key; [] L, R, P; [] LH, RH; [] Lptr, Rptr; [] LHptr, RHptr; [] nextbranch; [] tmp; NIL = -1; roota; rootb; currroot; cost; // each node contains a single key // Left & Right children, Parent // Left and Right heights // pointers to L, R in flipped tree // pointers to LH, RH in flipped tree // auxiliary reference, array not allocated // auxiliary reference, array not allocated // determines the current tree // to be calculated public static void main(string[] args) throws IOException { readandinitall(); // perform actions for simple BST tree roota = 0; rootb = M; maketreesbstoravl(false); transfersubtreebstoravl(rootb, false); // false == not AVL System.out.printf("%d", cost); // BST cost // perform actions for AVL tree cost = 0; maketreesbstoravl(true); transfersubtreebstoravl(rootb, true); System.out.printf(" %d\n", cost); // true == is AVL // AVL cost static void maketreesbstoravl(boolean isavl ){ L[rootA] = R[rootA] = L[rootB] = R[rootB]; // init if (isavl) P[rootA] = LH[rootA] = RH[rootA] = P[rootB] = LH[rootB] = RH[rootB] = NIL; cost = 2; // cost of the roots, which are not inserted if (isavl) cost += 2; // insert all other nodes one by one for(int inode = 1; inode < M; inode++) InsertBSTorAVL(rootA, inode, isavl); for(int inode = M+1; inode < M+N; inode++) InsertBSTorAVL(rootB, inode, isavl); static void transfersubtreebstoravl(int inode, boolean isavl) { // standard postorder if (inode == NIL) return ; transfersubtreebstoravl(l[inode], isavl); transfersubtreebstoravl(r[inode], isavl); InsertBSTorAVL(rootA, inode, isavl); static void InsertBSTorAVL(int iroot, int inode, boolean isavl) { inode = InsertBST(iRoot, inode); if (!isavl ) return; // all done for usual BST currroot = iroot; LH[iNode] = RH[iNode] = -1; // AVL rotation might change the root, so remember it

// go up the tree Lptr = L; Rptr = R; LHptr = LH; RHptr = RH; // set ptrs which can flip the tree int iparent = P[iNode]; cost += 1; // cost of the leaf on the way up if (inode == L[iParent]) swaplrptrs(); // make sure inode is right child of parent goupleftinavl(inode, iparent); // to ascend up and to the left InsertBST(int iroot, int inode){ nextbranch = (key[inode] < key[iroot] )? L : R; // count nodes during descent if (nextbranch[iroot]!= NIL) return InsertBST(nextBranch[iRoot], inode) ; nextbranch[iroot] = inode; L[iNode] = R[iNode] = NIL; P[iNode] = iroot; // unnecessary in bare BST :-) // count the leaf too return inode; static void goupleftinavl(int inode, int iparent) { // recursively node by node // count nodes during ascent RHptr[iParent] = 1+ Math.max(LHptr[iNode], RHptr[iNode]); // recalc balance if (LHptr[iParent]+1 >= RHptr[iParent] ) { // no rotation required, go up inode = iparent; iparent = P[iParent]; if (iparent == NIL) return; // root reached if (inode == Lptr[iParent]) swaplrptrs(); // make sure inode is right child of parent goupleftinavl(inode, iparent); // to ascend up and to the left else // else must rotate and stop if (LHptr[iNode] < RHptr[iNode]) Lrot(iParent, inode); else { swaplrptrs(); Lrot(iNode, Rptr[iNode]); // flip tree to get R rotation swaplrptrs(); Lrot(iParent, Rptr[iParent]); // flip it back to get L rotation // and stop ascent static void swaplrptrs(){ // flips the tree effectively: left <-> right tmp = Lptr; Lptr = Rptr; Rptr = tmp; tmp = LHptr; LHptr = RHptr; RHptr = tmp; static void Lrot(int inode, int ilchild) { // right rotation never used int origparent = P[iNode]; //move right subtree of the Child to the oposite side Rptr[iNode] = Lptr[iLchild]; if (Lptr[iLchild]!= NIL) P[Lptr[iLchild]] = inode; // ichild becomes subtree root Lptr[iLchild] = inode; P[iNode] = ilchild;

// link the new subtree root with the parent if (origparent == NIL) { if (currroot == roota) roota = currroot = ilchild; else rootb = currroot = ilchild; else { if (Lptr[origParent] == inode) Lptr[origParent] = ilchild; else Rptr[origParent] = ilchild; P[iLchild] = origparent; // recalc depths; RHptr[iNode] = LHptr[iLchild]; LHptr[iLchild] = 1+Math.max(RHptr[iNode], LHptr[iNode]); static void readandinitall() throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); M = Integer.valueOf(st.nextToken()); N = Integer.valueOf(st.nextToken()); // initialize all structures L = new int [M+N]; R = new int [M+N]; P = new int [M+N]; LH = new int [M+N]; RH = new int [M+N]; for(int i = 0; i < M+N; i++) L[i] = R[i] = P[i] = LH[i] = RH[i] = NIL; key = new int [M+N]; key[0] = 519217; for(int i = 1; i < key.length; i++) key[i] = (int) (( 754043 *(long) key[i-1] + 500009) % 1000003);