{"id":1563,"date":"2026-04-30T11:28:45","date_gmt":"2026-04-30T09:28:45","guid":{"rendered":"https:\/\/trzykody.pl\/?p=1563"},"modified":"2026-04-30T11:28:46","modified_gmt":"2026-04-30T09:28:46","slug":"rekurencja","status":"publish","type":"post","link":"https:\/\/trzykody.pl\/index.php\/2026\/04\/30\/rekurencja\/","title":{"rendered":"Rekurencja"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Rekurencyjne podej\u015bcie do rozwi\u0105zywania problem\u00f3w pojawia si\u0119 naturalnie tam, gdzie struktura danych lub samego zadania ma charakter samopodobny, czyli mo\u017cna je rozbi\u0107 na mniejsze instancje tego samego problemu. W praktyce oznacza to, \u017ce funkcja wywo\u0142uje sam\u0105 siebie z innymi argumentami a\u017c do osi\u0105gni\u0119cia warunku ko\u0144cowego. To narz\u0119dzie jest jednocze\u015bnie bardzo eleganckie i bardzo niebezpieczne, bo \u0142atwo doprowadzi\u0107 do przepe\u0142nienia stosu lub dramatycznego spadku wydajno\u015bci, je\u015bli nie kontroluje si\u0119 liczby wywo\u0142a\u0144, dlatego rozumienie mechanizmu jakim jest Rekurencja ma bezpo\u015brednie prze\u0142o\u017cenie na jako\u015b\u0107 kodu i stabilno\u015b\u0107 programu.<\/p>\n\n\n\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>Spis Tre\u015bci<\/h2><nav><ol><li><a href=\"#rekurencja-jako-mechanizm-dekompozycji-problemu-na-mniejsze-instancje-i-warunek-zakonczenia\">Rekurencja jako mechanizm dekompozycji problemu na mniejsze instancje i warunek zako\u0144czenia<\/a><\/li><li><a href=\"#rekurencja-a-stos-wywolan-funkcji-i-rzeczywisty-koszt-pamieciowy-operacji\">Rekurencja a stos wywo\u0142a\u0144 funkcji i rzeczywisty koszt pami\u0119ciowy operacji<\/a><\/li><li><a href=\"#rekurencja-ogonowa-jako-optymalizacja-ograniczajaca-zuzycie-stosu-i-jej-brak-w-wielu-kompilatorach\">Rekurencja ogonowa jako optymalizacja ograniczaj\u0105ca zu\u017cycie stosu i jej brak w wielu kompilatorach<\/a><\/li><li><a href=\"#rekurencja-w-strukturach-danych-drzewa-grafy-i-przetwarzanie-hierarchii\">Struktury danych: drzewa, grafy i przetwarzanie hierarchii<\/a><ol><li><a href=\"#rekurencja-vs-iteracja-kiedy-jedno-podejscie-prowadzi-do-bledow-a-drugie-upraszcza-kod\">Rekurencja vs iteracja: kiedy jedno podej\u015bcie prowadzi do b\u0142\u0119d\u00f3w a drugie upraszcza kod<\/a><\/li><\/ol><\/li><li><a href=\"#memoizacja-i-dynamiczne-programowanie-jako-sposob-ograniczenia-eksplozji-wywolan-rekurencyjnych\">Memoizacja i dynamiczne programowanie jako spos\u00f3b ograniczenia eksplozji wywo\u0142a\u0144 rekurencyjnych<\/a><\/li><li><a href=\"#pulapki-praktyczne-i-typowe-bledy-przy-stosowaniu-rekurencji-w-realnych-projektach\">Pu\u0142apki praktyczne i typowe b\u0142\u0119dy przy stosowaniu rekurencji w realnych projektach<\/a><\/li><li><a href=\"#faq\">FAQ<\/a><\/li><\/ol><\/nav><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"rekurencja-jako-mechanizm-dekompozycji-problemu-na-mniejsze-instancje-i-warunek-zakonczenia\">Rekurencja jako mechanizm dekompozycji problemu na mniejsze instancje i warunek zako\u0144czenia<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Rekurencja polega na definiowaniu rozwi\u0105zania problemu poprzez odniesienie do jego prostszej wersji. Ka\u017cda poprawna funkcja rekurencyjna sk\u0142ada si\u0119 z dw\u00f3ch element\u00f3w: warunku bazowego (base case), kt\u00f3ry zatrzymuje dalsze wywo\u0142ania oraz kroku rekurencyjnego (recursive case), kt\u00f3ry redukuje problem. Bez warunku bazowego program nie zako\u0144czy dzia\u0142ania i to najcz\u0119stszy b\u0142\u0105d pocz\u0105tkuj\u0105cych. Klasyczny przyk\u0142ad to silnia, gdzie n! = n \u00d7 (n-1)! dla n &gt; 0 oraz 0! = 1.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>J\u0119zyk<\/th><th>Kod<\/th><\/tr><\/thead><tbody><tr><td>C<\/td><td><code>c int silnia(int n) { if (n == 0) return 1; return n * silnia(n - 1); }<\/code><\/td><\/tr><tr><td>C++<\/td><td><code>cpp int silnia(int n) { if (n == 0) return 1; return n * silnia(n - 1); }<\/code><\/td><\/tr><tr><td>Python<\/td><td><code>python def silnia(n): if n == 0: return 1 return n * silnia(n - 1)<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Ka\u017cde wywo\u0142anie funkcji tworzy now\u0105 ramk\u0119 stosu (stack frame), czyli zapisuje parametry, adres powrotu oraz zmienne lokalne. Dla n = 5 stos rozwija si\u0119 liniowo: silnia(5) \u2192 silnia(4) \u2192 silnia(3) \u2192 silnia(2) \u2192 silnia(1) \u2192 silnia(0), a nast\u0119pnie wyniki s\u0105 zwijane w odwrotnej kolejno\u015bci.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"rekurencja-a-stos-wywolan-funkcji-i-rzeczywisty-koszt-pamieciowy-operacji\">Rekurencja a stos wywo\u0142a\u0144 funkcji i rzeczywisty koszt pami\u0119ciowy operacji<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ka\u017cde wywo\u0142anie funkcji rekurencyjnej powoduje zaj\u0119cie pami\u0119ci stosu, co oznacza, \u017ce z\u0142o\u017cono\u015b\u0107 pami\u0119ciowa wynosi O(n), gdzie n to g\u0142\u0119boko\u015b\u0107 rekurencji. W praktyce prowadzi to do problem\u00f3w takich jak przepe\u0142nienie stosu (stack overflow) oraz spadek wydajno\u015bci wynikaj\u0105cy z kosztu wywo\u0142a\u0144 funkcji. Dobrym przyk\u0142adem jest naiwny algorytm Fibonacciego, kt\u00f3ry generuje ogromn\u0105 liczb\u0119 powt\u00f3rze\u0144 oblicze\u0144.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>J\u0119zyk<\/th><th>Kod<\/th><\/tr><\/thead><tbody><tr><td>C<\/td><td><code>c int fib(int n) { if (n &lt;= 1) return n; return fib(n-1) + fib(n-2); }<\/code><\/td><\/tr><tr><td>C++<\/td><td><code>cpp int fib(int n) { if (n &lt;= 1) return n; return fib(n-1) + fib(n-2); }<\/code><\/td><\/tr><tr><td>Python<\/td><td><code>python def fib(n): if n &lt;= 1: return n return fib(n-1) + fib(n-2)<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Z\u0142o\u017cono\u015b\u0107 czasowa wynosi O(2\u207f), co oznacza, \u017ce dla n = 40 liczba wywo\u0142a\u0144 idzie w miliony, co w realnym systemie mo\u017ce prowadzi\u0107 do zauwa\u017calnych op\u00f3\u017anie\u0144 lub blokady procesu.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"rekurencja-ogonowa-jako-optymalizacja-ograniczajaca-zuzycie-stosu-i-jej-brak-w-wielu-kompilatorach\">Rekurencja ogonowa jako optymalizacja ograniczaj\u0105ca zu\u017cycie stosu i jej brak w wielu kompilatorach<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Rekurencja ogonowa to szczeg\u00f3lny przypadek, w kt\u00f3rym ostatni\u0105 operacj\u0105 funkcji jest jej w\u0142asne wywo\u0142anie. Dzi\u0119ki temu kompilator mo\u017ce teoretycznie zamieni\u0107 rekurencj\u0119 na iteracj\u0119 i ograniczy\u0107 zu\u017cycie pami\u0119ci. W praktyce jednak wiele j\u0119zyk\u00f3w nie implementuje tej optymalizacji.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>J\u0119zyk<\/th><th>Kod<\/th><\/tr><\/thead><tbody><tr><td>C<\/td><td><code>c int silnia_tail(int n, int acc) { if (n == 0) return acc; return silnia_tail(n - 1, n * acc); }<\/code><\/td><\/tr><tr><td>C++<\/td><td><code>cpp int silnia_tail(int n, int acc) { if (n == 0) return acc; return silnia_tail(n - 1, n * acc); }<\/code><\/td><\/tr><tr><td>Python<\/td><td><code>python def silnia_tail(n, acc): if n == 0: return acc return silnia_tail(n - 1, n * acc)<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">W praktyce oznacza to, \u017ce mimo poprawnej konstrukcji nadal istnieje ryzyko przepe\u0142nienia stosu, szczeg\u00f3lnie w Pythonie lub PHP.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"rekurencja-w-strukturach-danych-drzewa-grafy-i-przetwarzanie-hierarchii\">Struktury danych: drzewa, grafy i przetwarzanie hierarchii<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Rekurencja bardzo dobrze sprawdza si\u0119 w pracy ze strukturami hierarchicznymi, takimi jak drzewa binarne, systemy plik\u00f3w czy dane JSON. Wynika to z faktu, \u017ce ka\u017cdy element mo\u017ce zawiera\u0107 kolejne elementy tego samego typu. Przyk\u0142adem jest przej\u015bcie drzewa binarnego metod\u0105 inorder.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>J\u0119zyk<\/th><th>Kod<\/th><\/tr><\/thead><tbody><tr><td>C<\/td><td><code>c void inorder(Node* root) { if (root == NULL) return; inorder(root-&gt;left); printf(\"%d \", root-&gt;value); inorder(root-&gt;right); }<\/code><\/td><\/tr><tr><td>C++<\/td><td><code>cpp void inorder(Node* root) { if (!root) return; inorder(root-&gt;left); cout &lt;&lt; root-&gt;value &lt;&lt; \" \"; inorder(root-&gt;right); }<\/code><\/td><\/tr><tr><td>Python<\/td><td><code>python def inorder(root): if root is None: return inorder(root.left) print(root.value) inorder(root.right)<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Zalet\u0105 tego podej\u015bcia jest prostota i czytelno\u015b\u0107, szczeg\u00f3lnie w por\u00f3wnaniu do iteracyjnych implementacji wymagaj\u0105cych dodatkowych struktur danych.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"rekurencja-vs-iteracja-kiedy-jedno-podejscie-prowadzi-do-bledow-a-drugie-upraszcza-kod\">Rekurencja vs iteracja: kiedy jedno podej\u015bcie prowadzi do b\u0142\u0119d\u00f3w a drugie upraszcza kod<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Rekurencja upraszcza kod w problemach typu dziel i zwyci\u0119\u017caj, ale zwi\u0119ksza zu\u017cycie pami\u0119ci i ryzyko b\u0142\u0119d\u00f3w zwi\u0105zanych ze stosem. Iteracja daje wi\u0119ksz\u0105 kontrol\u0119 nad zasobami i jest wydajniejsza, ale mo\u017ce by\u0107 trudniejsza w implementacji. Przyk\u0142adem jest iteracyjna wersja silni.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>J\u0119zyk<\/th><th>Kod<\/th><\/tr><\/thead><tbody><tr><td>C<\/td><td><code>c int silnia_iter(int n) { int wynik = 1; for(int i=1;i&lt;=n;i++) wynik *= i; return wynik; }<\/code><\/td><\/tr><tr><td>C++<\/td><td><code>cpp int silnia_iter(int n) { int wynik = 1; for(int i=1;i&lt;=n;i++) wynik *= i; return wynik; }<\/code><\/td><\/tr><tr><td>Python<\/td><td><code>python def silnia_iter(n): wynik = 1 for i in range(1, n+1): wynik *= i return wynik<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">W praktyce wyb\u00f3r zale\u017cy od charakteru problemu oraz ogranicze\u0144 \u015brodowiska.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"memoizacja-i-dynamiczne-programowanie-jako-sposob-ograniczenia-eksplozji-wywolan-rekurencyjnych\">Memoizacja i dynamiczne programowanie jako spos\u00f3b ograniczenia eksplozji wywo\u0142a\u0144 rekurencyjnych<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Memoizacja polega na zapami\u0119tywaniu wynik\u00f3w funkcji dla wcze\u015bniej obliczonych argument\u00f3w. Dzi\u0119ki temu unika si\u0119 wielokrotnego wykonywania tych samych operacji. Jest to kluczowa technika przy optymalizacji rekurencji.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>J\u0119zyk<\/th><th>Kod<\/th><\/tr><\/thead><tbody><tr><td>C<\/td><td><code>c int memo[1000]; int fib(int n) { if (n &lt;= 1) return n; if (memo[n] != -1) return memo[n]; memo[n] = fib(n-1) + fib(n-2); return memo[n]; }<\/code><\/td><\/tr><tr><td>C++<\/td><td><code>cpp vector&lt;int&gt; memo(1000, -1); int fib(int n) { if (n &lt;= 1) return n; if (memo[n] != -1) return memo[n]; return memo[n] = fib(n-1) + fib(n-2); }<\/code><\/td><\/tr><tr><td>Python<\/td><td><code>python memo = {} def fib(n): if n &lt;= 1: return n if n in memo: return memo[n] memo[n] = fib(n-1) + fib(n-2) return memo[n]<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Z\u0142o\u017cono\u015b\u0107 czasowa spada do O(n), co ma ogromne znaczenie przy wi\u0119kszych danych.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"pulapki-praktyczne-i-typowe-bledy-przy-stosowaniu-rekurencji-w-realnych-projektach\">Pu\u0142apki praktyczne i typowe b\u0142\u0119dy przy stosowaniu rekurencji w realnych projektach<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Najcz\u0119stsze problemy to brak warunku bazowego, zbyt du\u017ca g\u0142\u0119boko\u015b\u0107 wywo\u0142a\u0144, niekontrolowane powielanie oblicze\u0144 oraz trudno\u015bci w debugowaniu. W realnych projektach cz\u0119sto pojawiaj\u0105 si\u0119 b\u0142\u0119dy przy przetwarzaniu du\u017cych struktur danych lub graf\u00f3w zawieraj\u0105cych cykle, gdzie brak mechanizmu oznaczania odwiedzonych element\u00f3w prowadzi do niesko\u0144czonej rekurencji.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"faq\">FAQ<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Czy rekurencja jest zawsze wolniejsza od iteracji?<\/strong> Nie zawsze, ale narzut wywo\u0142a\u0144 funkcji cz\u0119sto powoduje spadek wydajno\u015bci.<br><strong>Kiedy rekurencja ma sens?<\/strong> Gdy problem ma struktur\u0119 hierarchiczn\u0105 lub dzieli si\u0119 na podobne.<br>Czy ka\u017cd\u0105 rekurencj\u0119 mo\u017cna zamieni\u0107 na iteracj\u0119? Tak, cho\u0107 czasem kosztem czytelno\u015bci kodu.<br><strong>Dlaczego Fibonacci w wersji rekurencyjnej jest wolny?<\/strong> Bo powtarza te same obliczenia wielokrotnie.<br>Czy Python nadaje si\u0119 do rekurencji? Tak, ale ma ograniczenie g\u0142\u0119boko\u015bci stosu.<br><strong>Czym jest rekurencja ogonowa?<\/strong> To forma, gdzie ostatnia operacja to wywo\u0142anie funkcji, co umo\u017cliwia optymalizacj\u0119.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rekurencja jest narz\u0119dziem, kt\u00f3re upraszcza zapis problem\u00f3w o strukturze samopodobnej, ale wymaga \u015bwiadomego zarz\u0105dzania pami\u0119ci\u0105 i kontrolowania liczby wywo\u0142a\u0144, bo w przeciwnym razie bardzo szybko prowadzi do problem\u00f3w wydajno\u015bciowych lub b\u0142\u0119d\u00f3w wykonania.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>\u0179r\u00f3d\u0142o Foto: Freepik<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Rekurencyjne podej\u015bcie do rozwi\u0105zywania problem\u00f3w pojawia si\u0119 naturalnie tam, gdzie struktura danych lub samego zadania ma charakter samopodobny, czyli mo\u017cna je rozbi\u0107 na mniejsze instancje tego samego problemu. W praktyce oznacza to, \u017ce funkcja wywo\u0142uje sam\u0105 siebie z innymi argumentami a\u017c do osi\u0105gni\u0119cia warunku ko\u0144cowego. To narz\u0119dzie jest jednocze\u015bnie bardzo eleganckie i bardzo niebezpieczne, bo \u0142atwo doprowadzi\u0107 do przepe\u0142nienia stosu lub dramatycznego spadku wydajno\u015bci, je\u015bli nie kontroluje si\u0119 liczby wywo\u0142a\u0144, dlatego rozumienie mechanizmu jakim jest Rekurencja ma bezpo\u015brednie prze\u0142o\u017cenie na jako\u015b\u0107 kodu i stabilno\u015b\u0107 programu. Rekurencja jako mechanizm dekompozycji problemu na mniejsze instancje i warunek zako\u0144czenia Rekurencja polega na definiowaniu rozwi\u0105zania problemu poprzez odniesienie do jego prostszej wersji. Ka\u017cda poprawna funkcja rekurencyjna sk\u0142ada si\u0119 z dw\u00f3ch element\u00f3w: warunku bazowego (base case), kt\u00f3ry zatrzymuje dalsze wywo\u0142ania oraz kroku rekurencyjnego (recursive case), kt\u00f3ry redukuje problem. Bez warunku bazowego program nie zako\u0144czy dzia\u0142ania i to najcz\u0119stszy b\u0142\u0105d pocz\u0105tkuj\u0105cych. Klasyczny przyk\u0142ad to silnia, gdzie n! = n \u00d7 (n-1)! dla n &gt; 0 oraz 0! = 1. J\u0119zyk Kod C c int silnia(int n) { if (n == 0) return 1; return n * silnia(n &#8211; 1); } C++ cpp int silnia(int n) { if (n == 0) return 1; return n * silnia(n &#8211; 1); } Python python def silnia(n): if n == 0: return 1 return n * silnia(n &#8211; 1) Ka\u017cde wywo\u0142anie funkcji tworzy now\u0105 ramk\u0119 stosu (stack frame), czyli zapisuje parametry, adres powrotu oraz zmienne lokalne. Dla n = 5 stos rozwija si\u0119 liniowo: silnia(5) \u2192 silnia(4) \u2192 silnia(3) \u2192 silnia(2) \u2192 silnia(1) \u2192 silnia(0), a nast\u0119pnie wyniki s\u0105 zwijane w odwrotnej kolejno\u015bci. Rekurencja a stos wywo\u0142a\u0144 funkcji i rzeczywisty koszt pami\u0119ciowy operacji Ka\u017cde wywo\u0142anie funkcji rekurencyjnej powoduje zaj\u0119cie pami\u0119ci stosu, co oznacza, \u017ce z\u0142o\u017cono\u015b\u0107 pami\u0119ciowa wynosi O(n), gdzie n to g\u0142\u0119boko\u015b\u0107 rekurencji. W praktyce prowadzi to do problem\u00f3w takich jak przepe\u0142nienie stosu (stack overflow) oraz spadek wydajno\u015bci wynikaj\u0105cy z kosztu wywo\u0142a\u0144 funkcji. Dobrym przyk\u0142adem jest naiwny algorytm Fibonacciego, kt\u00f3ry generuje ogromn\u0105 liczb\u0119 powt\u00f3rze\u0144 oblicze\u0144. J\u0119zyk Kod C c int fib(int n) { if (n &lt;= 1) return n; return fib(n-1) + fib(n-2); } C++ cpp int fib(int n) { if (n &lt;= 1) return n; return fib(n-1) + fib(n-2); } Python python def fib(n): if n &lt;= 1: return n return fib(n-1) + fib(n-2) Z\u0142o\u017cono\u015b\u0107 czasowa wynosi O(2\u207f), co oznacza, \u017ce dla n = 40 liczba wywo\u0142a\u0144 idzie w miliony, co w realnym systemie mo\u017ce prowadzi\u0107 do zauwa\u017calnych op\u00f3\u017anie\u0144 lub blokady procesu. Rekurencja ogonowa jako optymalizacja ograniczaj\u0105ca zu\u017cycie stosu i jej brak w wielu kompilatorach Rekurencja ogonowa to szczeg\u00f3lny przypadek, w kt\u00f3rym ostatni\u0105 operacj\u0105 funkcji jest jej w\u0142asne wywo\u0142anie. Dzi\u0119ki temu kompilator mo\u017ce teoretycznie zamieni\u0107 rekurencj\u0119 na iteracj\u0119 i ograniczy\u0107 zu\u017cycie pami\u0119ci. W praktyce jednak wiele j\u0119zyk\u00f3w nie implementuje tej optymalizacji. J\u0119zyk Kod C c int silnia_tail(int n, int acc) { if (n == 0) return acc; return silnia_tail(n &#8211; 1, n * acc); } C++ cpp int silnia_tail(int n, int acc) { if (n == 0) return acc; return silnia_tail(n &#8211; 1, n * acc); } Python python def silnia_tail(n, acc): if n == 0: return acc return silnia_tail(n &#8211; 1, n * acc) W praktyce oznacza to, \u017ce mimo poprawnej konstrukcji nadal istnieje ryzyko przepe\u0142nienia stosu, szczeg\u00f3lnie w Pythonie lub PHP. Struktury danych: drzewa, grafy i przetwarzanie hierarchii Rekurencja bardzo dobrze sprawdza si\u0119 w pracy ze strukturami hierarchicznymi, takimi jak drzewa binarne, systemy plik\u00f3w czy dane JSON. Wynika to z faktu, \u017ce ka\u017cdy element mo\u017ce zawiera\u0107 kolejne elementy tego samego typu. Przyk\u0142adem jest przej\u015bcie drzewa binarnego metod\u0105 inorder. J\u0119zyk Kod C c void inorder(Node* root) { if (root == NULL) return; inorder(root-&gt;left); printf(&#8222;%d &#8222;, root-&gt;value); inorder(root-&gt;right); } C++ cpp void inorder(Node* root) { if (!root) return; inorder(root-&gt;left); cout &lt;&lt; root-&gt;value &lt;&lt; &#8221; &#8222;; inorder(root-&gt;right); } Python python def inorder(root): if root is None: return inorder(root.left) print(root.value) inorder(root.right) Zalet\u0105 tego podej\u015bcia jest prostota i czytelno\u015b\u0107, szczeg\u00f3lnie w por\u00f3wnaniu do iteracyjnych implementacji wymagaj\u0105cych dodatkowych struktur danych. Rekurencja vs iteracja: kiedy jedno podej\u015bcie prowadzi do b\u0142\u0119d\u00f3w a drugie upraszcza kod Rekurencja upraszcza kod w problemach typu dziel i zwyci\u0119\u017caj, ale zwi\u0119ksza zu\u017cycie pami\u0119ci i ryzyko b\u0142\u0119d\u00f3w zwi\u0105zanych ze stosem. Iteracja daje wi\u0119ksz\u0105 kontrol\u0119 nad zasobami i jest wydajniejsza, ale mo\u017ce by\u0107 trudniejsza w implementacji. Przyk\u0142adem jest iteracyjna wersja silni. J\u0119zyk Kod C c int silnia_iter(int n) { int wynik = 1; for(int i=1;i&lt;=n;i++) wynik *= i; return wynik; } C++ cpp int silnia_iter(int n) { int wynik = 1; for(int i=1;i&lt;=n;i++) wynik *= i; return wynik; } Python python def silnia_iter(n): wynik = 1 for i in range(1, n+1): wynik *= i return wynik W praktyce wyb\u00f3r zale\u017cy od charakteru problemu oraz ogranicze\u0144 \u015brodowiska. Memoizacja i dynamiczne programowanie jako spos\u00f3b ograniczenia eksplozji wywo\u0142a\u0144 rekurencyjnych Memoizacja polega na zapami\u0119tywaniu wynik\u00f3w funkcji dla wcze\u015bniej obliczonych argument\u00f3w. Dzi\u0119ki temu unika si\u0119 wielokrotnego wykonywania tych samych operacji. Jest to kluczowa technika przy optymalizacji rekurencji. J\u0119zyk Kod C c int memo[1000]; int fib(int n) { if (n &lt;= 1) return n; if (memo[n] != -1) return memo[n]; memo[n] = fib(n-1) + fib(n-2); return memo[n]; } C++ cpp vector&lt;int&gt; memo(1000, -1); int fib(int n) { if (n &lt;= 1) return n; if (memo[n] != -1) return memo[n]; return memo[n] = fib(n-1) + fib(n-2); } Python python memo = {} def fib(n): if n &lt;= 1: return n if n in memo: return memo[n] memo[n] = fib(n-1) + fib(n-2) return memo[n] Z\u0142o\u017cono\u015b\u0107 czasowa spada do O(n), co ma ogromne znaczenie przy wi\u0119kszych danych. Pu\u0142apki praktyczne i typowe b\u0142\u0119dy przy stosowaniu rekurencji w realnych projektach Najcz\u0119stsze problemy to brak warunku bazowego, zbyt du\u017ca g\u0142\u0119boko\u015b\u0107 wywo\u0142a\u0144, niekontrolowane powielanie oblicze\u0144 oraz trudno\u015bci w debugowaniu. W realnych projektach cz\u0119sto pojawiaj\u0105 si\u0119 b\u0142\u0119dy przy przetwarzaniu du\u017cych struktur danych lub graf\u00f3w zawieraj\u0105cych cykle, gdzie brak mechanizmu oznaczania odwiedzonych element\u00f3w prowadzi do niesko\u0144czonej rekurencji. FAQ Czy rekurencja jest zawsze wolniejsza od iteracji? Nie zawsze, ale narzut wywo\u0142a\u0144 funkcji cz\u0119sto powoduje spadek wydajno\u015bci.Kiedy rekurencja ma sens? Gdy problem ma struktur\u0119 hierarchiczn\u0105 lub dzieli si\u0119 na podobne.Czy ka\u017cd\u0105 rekurencj\u0119 mo\u017cna zamieni\u0107 na iteracj\u0119? Tak, cho\u0107 czasem kosztem czytelno\u015bci kodu.Dlaczego Fibonacci w wersji rekurencyjnej jest wolny? Bo powtarza te same obliczenia wielokrotnie.Czy Python nadaje si\u0119 do rekurencji? Tak, ale ma ograniczenie g\u0142\u0119boko\u015bci stosu.Czym jest rekurencja ogonowa? To forma, gdzie ostatnia operacja to wywo\u0142anie funkcji, co umo\u017cliwia optymalizacj\u0119. Rekurencja jest narz\u0119dziem, kt\u00f3re upraszcza zapis problem\u00f3w o strukturze samopodobnej, ale wymaga \u015bwiadomego zarz\u0105dzania pami\u0119ci\u0105 i kontrolowania liczby wywo\u0142a\u0144, bo w przeciwnym razie bardzo szybko prowadzi do problem\u00f3w wydajno\u015bciowych lub b\u0142\u0119d\u00f3w wykonania. \u0179r\u00f3d\u0142o Foto: Freepik<\/p>\n","protected":false},"author":1,"featured_media":1564,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,24],"tags":[],"class_list":["post-1563","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-poradnik","category-kodowanie"],"_links":{"self":[{"href":"https:\/\/trzykody.pl\/index.php\/wp-json\/wp\/v2\/posts\/1563","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/trzykody.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/trzykody.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/trzykody.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/trzykody.pl\/index.php\/wp-json\/wp\/v2\/comments?post=1563"}],"version-history":[{"count":1,"href":"https:\/\/trzykody.pl\/index.php\/wp-json\/wp\/v2\/posts\/1563\/revisions"}],"predecessor-version":[{"id":1565,"href":"https:\/\/trzykody.pl\/index.php\/wp-json\/wp\/v2\/posts\/1563\/revisions\/1565"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/trzykody.pl\/index.php\/wp-json\/wp\/v2\/media\/1564"}],"wp:attachment":[{"href":"https:\/\/trzykody.pl\/index.php\/wp-json\/wp\/v2\/media?parent=1563"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/trzykody.pl\/index.php\/wp-json\/wp\/v2\/categories?post=1563"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/trzykody.pl\/index.php\/wp-json\/wp\/v2\/tags?post=1563"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}