Stromy Karel Richta a kol. Katedra počítačů Fakulta elektrotechnická České vysoké učení technické v Praze Karel Richta a kol., 2018, B6B36DSA 01/2018, Lekce 9 https://cw.fel.cvut.cz/wiki/courses/b6b36dsa/start B6B36DSA, 2018, Lekce 9, 1/62
Připomenutí: QUICKSORT Ukázka průběhu 8 5 12 6 10 11 2 4 1 3 7 9 7 5 3 6 1 4 2 11 10 12 8 9 pivot = = první v úseku 2 5 3 6 1 4 7 9 10 8 12 11 1 5 3 6 2 4 8 10 9 11 12 4 3 2 6 5 9 10 2 3 4 5 6 2 3 B6B36DSA, 2018, Lekce 9, 2/62
Motivace: QUICKSORT 8 5 12 6 10 11 2 4 1 3 7 9 7 5 3 6 1 4 2 11 10 12 8 9 2 5 3 6 1 4 7 9 10 8 11 12 1 5 3 6 2 4 8 9 10 4 3 2 2 3 4 5 6 Schéma rekurzivního volání Quick sortu má podobu binárního kořenového stromu. B6B36DSA, 2018, Lekce 9, 3/62
Strom uzel, vrchol node, vertex strom tree hrana edge vnitřní uzel internal node list leaf B6B36DSA, 2018, Lekce 9, 4/62
Orientované a neorientované grafy B6B36DSA, 2018, Lekce 9, 5/62
Definice stromu (volného stromu) B6B36DSA, 2018, Lekce 9, 6/62
Vlastnosti volných stromů B6B36DSA, 2018, Lekce 9, 7/62
Kořenové stromy kořenový strom rooted tree k l kořen root R h g i j m a b R n f c d e a m j i k l h g n b c e d f předchůdce, rodič predecessor, parent j i následník, potomek successor, child B6B36DSA, 2018, Lekce 9, 8/62
Definice kořenového stromu B6B36DSA, 2018, Lekce 9, 9/62
Hloubka kořenového stromu R 0 a b c 1 m j i n d 2 k l h e f 3 g 4 4 hloubka uzlu node depth hloubka stromu tree depth B6B36DSA, 2018, Lekce 9, 10/62
Binární kořenové stromy binární (kořenový!!) strom 0, 1 nebo 2 následníci binary (rooted!!) tree 0, 1 or 2 successors x podstrom uzlu x... levý subtree of node x... left... pravý right B6B36DSA, 2018, Lekce 9, 11/62
Pravidelné a vyvážené stromy pravidelný binární strom 0 nebo 2 následníci regular binary tree 0 or 2 successors vyvážený strom balanced tree Hloubky všech listů jsou (víceméně) stejné. All leaf depths are (more or less) equal. B6B36DSA, 2018, Lekce 9, 12/62
Hloubka vyváženého stromu hloubka 0 1 2 3 4 uzlů 1 2 4 8 16 (2 (hloubka stromu)+1 1) ~ uzlů hloubka stromu ~ log 2 (uzlů+1) - 1 k 2 k 2 k+1-1 vyvážený strom: hloubka ~ log 2 (uzlů) B6B36DSA, 2018, Lekce 9, 13/62
Hloubka nevyváženého stromu hloubka 0 1 2 3 k-1 k uzlů 1 2 2 2 2 2 2k+1 2(hloubka)+1 ~ uzlů hloubka ~ (uzlů 1)/2 extrémně nevyvážený pravidelný strom: hloubka ~ (uzlů 1)/2 B6B36DSA, 2018, Lekce 9, 14/62
Souhrn velikosti hloubka pravidelného stromu = (počet uzlů) obvykle hloubka pravidelného stromu = (log 2 (počet uzlů)) B6B36DSA, 2018, Lekce 9, 15/62
Jednoduchý příklad rekurze pravítko 1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 void ruler(int val) { if (val < 1) return; } ruler(val-1); print(val); ruler(val-1); rysky pravítka délky rysek kód vypisující délky rysek pravítka Call: ruler(4); B6B36DSA, 2018, Lekce 9, 16/62
Jednoduchý příklad rekurze (pokr.) ruler(2); print(3); ruler(2); ruler(3); print(4); ruler(3); 4 if (val < 1) return; ruler(val-1); print(val); ruler(val-1); Start 3 3 2 2 2 2 1 1 1 1 1 1 1 1 ruler(1); print(2); ruler(1); ruler(0); print(1); ruler(0); return; 1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 B6B36DSA, 2018, Lekce 9, 17/62
Jednoduchý příklad rekurze (pokr.) 4 if (val < 1) return; ruler(val-1); print(val); ruler(val-1); 3 3 2 2 2 2 1 1 1 1 1 1 1 1 1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 B6B36DSA, 2018, Lekce 9, 18/62
Implementace binárního stromu C key left right typedef struct node { int key; struct node *left; struct node *right; } NODE; B6B36DSA, 2018, Lekce 9, 19/62
Implementace binárního stromu Java public class Node { public Node left; public Node right; public int key; public Node(int k) { key = k; left = null; right = null; } } public class Tree { public Node root; public Tree() { root = null; } } B6B36DSA, 2018, Lekce 9, 20/62
Vybudování náhodného binárního stromu v C NODE *randtree(int depth) { NODE *pnode; if ((depth <= 0) (random(10) > 7)) return (NULL); //stop recursion pnode = (NODE *) malloc(sizeof(node)); // create node if (pnode == NULL) { printf("%s", "No memory."); return NULL; } pnode->left = randtree(depth-1); // make left subtree pnode->key = random(100); // some value pnode->right = randtree(depth-1); // make right subtree return pnode; // all done } NODE *root; root = randtree(4); B6B36DSA, 2018, Lekce 9, 21/62
Vybudování náhodného binárního stromu v Javě public Node randtree(int depth) { Node node; if ((depth <= 0) ((int) Math.random()*10 > 7) return null; // create node with a key value node = new Node((int)(Math.random()*100)); } node.left = randtree(depth-1); // make left subtree node.right = randtree(depth-1); // make right subtree return node; // all done Node root; root = randtree(4); B6B36DSA, 2018, Lekce 9, 22/62
Náhodný binární strom strom 13 39 83 84 58 28 98 71 5 reprezentace stromu 39 13 83 84 58 28 98 71 5 B6B36DSA, 2018, Lekce 9, 23/62
Průchod stromem v pořadí Inorder 13 Strom 84 39 58 83 28 98 71 5 Průchod stromem v pořadí INORDER void listinorder( NODE *ptr) { if (ptr == NULL) return; listinorder(ptr->left); printf("%d ", ptr->key); listinorder(ptr->right); } Výstup 98 84 71 39 13 58 5 83 28 B6B36DSA, 2018, Lekce 9, 24/62
Rekurzivní pohyb v průchodu Inorder A listinorder(ptr->left); printf("%d ", ptr->key); listinorder(ptr->right); B C D E F G H I J K L M N O H D I B J E K A L F M C N G O B6B36DSA, 2018, Lekce 9, 25/62
Průchod stromem v pořadí Preorder 13 Strom 84 39 58 83 28 98 71 5 Průchod stromem v pořadí PREORDER void listpreorder( NODE *ptr) { if (ptr == NULL) return; printf("%d ", ptr->key); listpreorder(ptr->left); listpreorder(ptr->right); } Výstup 13 39 84 98 71 83 58 5 28 B6B36DSA, 2018, Lekce 9, 26/62
Rekurzivní pohyb v průchodu Preorder printf("%d ", ptr->key); listpreorder(ptr->left); listpreorder(ptr->right); A B C D E F G H I J K L M N O A B D H I E J K C F L M G N O B6B36DSA, 2018, Lekce 9, 27/62
Průchod stromem v pořadí Postorder 13 Strom 84 39 58 83 28 98 71 5 Průchod stromem v pořadí POSTORDER void listpostorder( NODE *ptr) { if (ptr == NULL) return; listpostorder(ptr->left); listpostorder(ptr->right); printf("%d ", ptr->key); } Výstup 98 71 84 39 5 58 28 83 13 B6B36DSA, 2018, Lekce 9, 28/62
Rekurzivní pohyb v průchodu Postorder listpostorder(ptr->left); listpostorder(ptr->right); printf("%d ", ptr->key); A B C D E F G H I J K L M N O H I D J K E B L M F N O G C A B6B36DSA, 2018, Lekce 9, 29/62
Velikost stromu rekurzivně 9 13 4 4 3 39 2 83 1 m nodes n nodes 1 84 1 98 71 58 1 5 28 m+n+1 nodes int count(node *ptr) { if (ptr == NULL) return (0); return (count(ptr->left) + count(ptr->right)+1); } B6B36DSA, 2018, Lekce 9, 30/62
Hloubka stromu rekurzivně 0 1 84 0 98 71 39 3 13 2 2 1 58 0 5 83 0 28 m n n+1 = max(m,n)+1 int depth(node *ptr) { if (ptr == NULL) return (-1); return ( max(depth(ptr->left), depth(ptr->right) )+1 ); } B6B36DSA, 2018, Lekce 9, 31/62
Zásobník implementuje rekurzi Uzel A navštíven 1x Uzel A navštíven 0x 1 A 1 2 0 Pravítko bez rekurze 0 4 3 3 2 2 2 2 3 1 1 1 1 1 1 1 1 2 1 2 1 1 2 2 2 Uzel A navštíven 2x B6B36DSA, 2018, Lekce 9, 32/62
Zásobník implementuje rekurzi zásobník dno 0 1 A 1 2 2 3 A 0/1/2 hodnota počet návštěv 1 2 2 1 1 2 2 vrchol B6B36DSA, 2018, Lekce 9, 33/62
Zásobník implementuje rekurzi Standardní strategie Při používání zásobníku: Je-li to možné, zpracovávej jen data ze zásobníku. Standardní postup Ulož první uzel (první zpracovávaný prvek) do zásobníku. Každý další uzel (zpracovávaný prvek) ulož také na zásobník. Zpracovávej vždy pouze uzel na vrcholu zásobníku. Když jsi s uzlem (prvkem) hotov, ze zásobníku ho odstraň. Skonči, když je zásobník prázdný. B6B36DSA, 2018, Lekce 9, 34/62
Zásobník implementuje rekurzi Každý záběr v následující sekvenci předvádí situaci PŘED zpracováním uzlu. Start: Vstup do kořene 4 zásobník hodnota návštěv 4 0 push(4,0) 3 2 2 1 1 1 1 Výstup B6B36DSA, 2018, Lekce 9, 35/62
Zásobník implementuje rekurzi zásobník Opouštíme 4 a blížíme se k 3 4 hodnota návštěv 4 1 push(3,0) 3 3 0 2 2 1 1 1 1 Výstup B6B36DSA, 2018, Lekce 9, 36/62
Zásobník implementuje rekurzi hodnota zásobník návštěv Opouštíme 3 a blížíme se k 2 4 4 1 3 2 2 3 1 2 0 push(2,0) 1 1 1 1 Výstup B6B36DSA, 2018, Lekce 9, 37/62
Zásobník implementuje rekurzi Opouštíme 2 a blížíme 3 se k 1 2 2 1 1 1 1 4 zásobník hodnota návštěv 4 1 3 1 2 1 1 0 push(1,0) Výstup B6B36DSA, 2018, Lekce 9, 38/62
Zásobník implementuje rekurzi Opouštíme 1 a blížíme 3 se k 0 2 2 1 1 1 1 4 zásobník hodnota návštěv 4 1 3 1 2 1 1 1 0 0 push(0,0) Výstup B6B36DSA, 2018, Lekce 9, 39/62
Zásobník implementuje rekurzi Opouštíme 0 a blížíme 3 se k 1 2 2 1 1 1 1 4 zásobník hodnota návštěv 4 1 3 1 2 1 1 1 0 0 pop() Výstup B6B36DSA, 2018, Lekce 9, 40/62
Zásobník implementuje rekurzi Opouštíme 1 a blížíme 3 se k 0 2 2 1 1 1 1 4 zásobník hodnota návštěv 4 1 3 1 2 1 1 2 0 0 push(0,0) 1 Výstup B6B36DSA, 2018, Lekce 9, 41/62
Zásobník implementuje rekurzi Opouštíme 0 a blížíme 3 se k 1 2 2 1 1 1 1 4 zásobník hodnota návštěv 4 1 3 1 2 1 1 2 0 0 pop() 1 Výstup B6B36DSA, 2018, Lekce 9, 42/62
Zásobník implementuje rekurzi Opouštíme 1 a blížíme 3 se k 2 2 2 1 1 1 1 4 zásobník hodnota návštěv 4 1 3 1 2 1 1 2 pop() 1 Výstup B6B36DSA, 2018, Lekce 9, 43/62
Zásobník implementuje rekurzi 4 zásobník hodnota návštěv 4 1 Opouštíme 2 a blížíme 3 se k 1 2 2 3 2 1 2 push(1,0) 1 1 1 1 1 0 atd 1 2 Výstup B6B36DSA, 2018, Lekce 9, 44/62
po chvíli Zásobník implementuje rekurzi zásobník hodnota návštěv Opouštíme 2 a blížíme se k 3 3 4 4 1 3 1 2 2 2 2 pop() 1 1 1 1 1 2 1 Výstup B6B36DSA, 2018, Lekce 9, 45/62
Zásobník implementuje rekurzi hodnota zásobník návštěv Opouštíme 3 a blížíme se k 2 3 4 4 1 3 2 push(2,0) 2 2 2 0 1 1 1 1 1 2 1 3 Výstup atd B6B36DSA, 2018, Lekce 9, 46/62
a je hotovo Zásobník implementuje rekurzi Konec 4 zásobník hodnota návštěv 4 2 (empty == true) pop() 3 2 1 1 1 1 2 1 3 1 2 1 4 1 2 1 3 1 2 1 Výstup B6B36DSA, 2018, Lekce 9, 47/62
Zásobník implementuje rekurzi // Rekurzivní pravítko bez rekurzivníhop volání // Pseudokód (skoro kód :-)) while (stack.empty() == false) { if (stack.top.hodnota == 0) stack.pop(); if (stack.top.navstev == 0) { stack.top.navstev++; stack.push(stack.top.hodnota-1,0); } if (stack.top.navstev == 1) { print(stack.top.hodnota); stack.top.navstev++; stack.push(stack.top.hodnota-1,0); } if (stack.top.navstev==2) stack.pop(); } B6B36DSA, 2018, Lekce 9, 48/62
Zásobník implementuje rekurzi int zashodn[10]; int zasnavst[10]; int SP; // SP = Stack Pointer void ruler2() { while (SP >= 0) { if (zashodn[sp] == 0) SP--; // pop if (zasnavst[sp] == 0) { // první návštěva zasnavst[sp]++; SP++; zashodn[sp] = zashodn[sp-1]-1; // jdi doleva zasnavst[sp] = 0; } if (zasnavst[sp] == 1) { // druhá návštěva printf("%d%s", zashodn[sp], " ");// zpracuj uzel zasnavst[sp]++; SP++; zashodn[sp] = zashodn[sp-1]-1; // jdi dopreva zasnavst[sp] = 0; } if (zasnavst[sp] == 2) SP --; // pop: uzel hotov } } B6B36DSA, 2018, Lekce 9, 49/62
B6B36DSA, 2018, Lekce 9, 50/62
B6B36DSA, 2018, Lekce 9, 51/62
B6B36DSA, 2018, Lekce 9, 52/62
Rekurzivně definovaný terén B6B36DSA, 2018, Lekce 9, 53/62
Rekurzivně definovaný terén B6B36DSA, 2018, Lekce 9, 54/62
Karel Richta a kol. (ČVUT FEL) B6B36DSA, 2018, Lekce 9, 55/62 Stromy 0 4 7 5 2 6 1 3 1 2 3 4 5 6 7 0 6 5 4 3 2 1 0 7 5 2 6 3 0 7 1 4 x y y 1 2 3 4 5 6 7 0 x 6 5 4 3 2 1 0 7 Problém osmi dam na šachovnici Snadné prohledávání s návratem (backtrack)
Snadné prohledávání s návratem (backtrack) B6B36DSA, 2018, Lekce 9, 56/62
Snadné prohledávání s návratem (backtrack) B6B36DSA, 2018, Lekce 9, 57/62
Snadné prohledávání s návratem (backtrack) Stop! B6B36DSA, 2018, Lekce 9, 58/62
Snadné prohledávání s návratem (backtrack) N Počet řešení Počet testovaných pozic dámy Hrubá síla (N N ) Backtrack 4 2 256 240 5 10 3 125 1 100 6 4 46 656 5 364 7 40 823 543 25 088 8 92 16 777 216 125 760 9 352 387 420 489 651 402 10 724 10 000 000 000 3 481 500 11 2 680 285 311 670 611 19 873 766 12 14 200 8 916 100 448 256 121 246 416 Zrychlení 1.07 2.84 8.70 32.83 133.41 594.75 2 872.33 14 356.20 73 537.00 Tab.: Rychlosti řešení problému osmi dam B6B36DSA, 2018, Lekce 9, 59/62
Snadné prohledávání s návratem (backtrack) bool posok( int x, int y) { int i; for (i = 0; i < x; i++) if ((xposarr[i] == y) // stejná řada (abs(x-i) == abs(xposarr[i]-y) )) // nebo diagonála return false; return true; } void tryputcolumn(int y) { int x; if (y >= N ) print_yposarr(); else for (x = 0; x < N; x++) if (posok(y, x) == true) { xposarr[y] = x; tryputcolumn(y + 1); } } Call: tryputcolumn(0); // řešení // testuj sloupce // když je volno, // dej tam dámu // a volej rekurzi B6B36DSA, 2018, Lekce 9, 60/62
Salvador Dalí Tvář války (1940-1941) B6B36DSA, 2018, Lekce 9, 61/62
The End B6B36DSA, 2018, Lekce 9, 62/62