遞歸是電腦科學中的強大技術,通常用於樹遍歷、深度優先搜尋和回溯演算法等任務。然而,由於函數呼叫和維護呼叫堆疊的開銷,遞歸在時間和空間方面的效率可能較低。在某些情況下,使用顯式堆疊來模擬遞歸調用,將遞歸轉換為迭代方法是有益的。本文提供了在 JavaScript 中使用堆疊將遞歸演算法轉換為迭代演算法的逐步指南。
為什麼將遞歸轉換為迭代?
您可能想要將遞歸轉換為迭代的原因有幾個:
- 堆疊溢位:深度遞歸呼叫可能會耗盡呼叫堆疊,導致堆疊溢位。使用顯式堆疊可以避免這個問題。
- 效率:迭代解決方案通常更節省內存,因為它們不需要維護呼叫堆疊的開銷。
- 更好的控制:使用顯式堆疊可以讓您更好地控制演算法的執行,特別是在涉及回溯時。
使用堆疊將遞歸轉換為迭代的模板
當使用堆疊將遞歸函數轉換為迭代函數時,不同類型的演算法(例如樹遍歷、回溯問題或圖遍歷)的一般方法保持相似。以下是一個靈活的模板,可以適應各種場景。
通用模板
1. 遞歸函數(範例)
function recursiveFunction(args) { // Base case if (baseCondition) { // Handle the base case return; } // Recursive calls for (let i = 0; i <h4> 2. <strong>使用堆疊的迭代函數</strong> </h4> <p>要將上述遞歸函數轉換為迭代函數,我們按照以下步驟操作:<br> </p> <pre class="brush:php;toolbar:false">function iterativeFunction(args) { // Initialize the stack let stack = [initialState]; // Loop until the stack is empty while (stack.length > 0) { // Pop the current state from the stack let currentState = stack.pop(); // Handle the base case (optional, since we can check on each iteration) if (baseCondition) { continue; // Skip or handle the base case } // Process the current state processState(currentState); // Push next states onto the stack for (let i = 0; i <hr> <h3> 模板分解 </h3> <ol> <li><p><strong>初始化堆疊</strong>:<br><br> 堆疊應使用起始狀態進行初始化,起始狀態可以是初始參數或遍歷中的第一個節點。 </p></li> <li><p><strong>循環堆疊</strong>:<br><br> 只要堆疊有項目,循環就會繼續,這表示在原始函數中進行的遞歸呼叫。 </p></li> <li><p><strong>基本條件處理</strong>:<br><br> 在遞歸中,基本條件檢查是否需要進一步遞歸。在迭代方法中,您可以在循環內執行相同的檢查。當滿足基本條件時,您可以使用繼續跳過進一步的處理。 </p></li> <li><p><strong>處理目前狀態</strong>:<br><br> 處理當前迭代的狀態(相當於當前遞歸呼叫時發生的處理)。 </p></li> <li><p><strong>推送下一個狀態</strong>:<br><br> 就像遞歸函數呼叫新的遞歸函數一樣,在這裡將下一個狀態(即要處理的函數參數或節點)推送到堆疊上。 </p></li> </ol> <hr> <h3> 轉換範例:有序樹遍歷 </h3> <h4> 遞迴版本: </h4> <pre class="brush:php;toolbar:false">function recursiveFunction(args) { // Base case if (baseCondition) { // Handle the base case return; } // Recursive calls for (let i = 0; i <h4> 使用堆疊的迭代版本: </h4> <pre class="brush:php;toolbar:false">function iterativeFunction(args) { // Initialize the stack let stack = [initialState]; // Loop until the stack is empty while (stack.length > 0) { // Pop the current state from the stack let currentState = stack.pop(); // Handle the base case (optional, since we can check on each iteration) if (baseCondition) { continue; // Skip or handle the base case } // Process the current state processState(currentState); // Push next states onto the stack for (let i = 0; i <hr> <h3> 將遞歸轉換為迭代的範例 </h3> <h4> 範例 1:圖上的深度優先搜尋 (DFS) </h4> <p>深度優先搜尋(DFS)通常使用遞歸來實現。這是遞歸 DFS 演算法:<br> </p> <pre class="brush:php;toolbar:false">function inorderTraversal(root) { if (root === null) return; inorderTraversal(root.left); console.log(root.value); inorderTraversal(root.right); }
使用堆疊的迭代版本:
function inorderTraversalIterative(root) { let stack = []; let current = root; while (stack.length > 0 || current !== null) { // Reach the leftmost node while (current !== null) { stack.push(current); current = current.left; } // Visit the node current = stack.pop(); console.log(current.value); // Move to the right node current = current.right; } }
在這個例子中,堆疊明確地保存了要存取的節點,我們使用循環來模擬遞歸呼叫。
範例2:中序樹遍歷(迭代)
二元樹的中序遍歷通常是遞歸完成的。這是遞迴版本:
function dfs(graph, node, visited = new Set()) { if (visited.has(node)) return; console.log(node); visited.add(node); for (let neighbor of graph[node]) { dfs(graph, neighbor, visited); } }
使用堆疊的迭代版本:
function dfsIterative(graph, startNode) { let stack = [startNode]; let visited = new Set(); while (stack.length > 0) { let node = stack.pop(); if (visited.has(node)) continue; console.log(node); visited.add(node); // Add neighbors to the stack in reverse order to maintain DFS order for (let neighbor of graph[node].reverse()) { if (!visited.has(neighbor)) { stack.push(neighbor); } } } }
在這種情況下,堆疊幫助追蹤要存取的節點,內循環向下遍歷樹的左側,直到到達最左邊的節點。
範例 3:產生子集(回溯)
用於產生集合子集的回溯方法可以像這樣遞歸地實現:
function inorderTraversal(root) { if (root === null) return; inorderTraversal(root.left); console.log(root.value); inorderTraversal(root.right); }
使用堆疊的迭代版本:
function inorderTraversalIterative(root) { let stack = []; let current = root; while (stack.length > 0 || current !== null) { // Reach the leftmost node while (current !== null) { stack.push(current); current = current.left; } // Visit the node current = stack.pop(); console.log(current.value); // Move to the right node current = current.right; } }
迭代版本使用堆疊來模擬遞歸函數呼叫。 currentSubset 被就地修改,堆疊透過將新狀態推送到其上來處理回溯。
範例 4:生成排列
要產生集合的所有排列,通常使用遞歸:
function subsets(nums) { let result = []; function backtrack(start, currentSubset) { result.push([...currentSubset]); for (let i = start; i <p><strong>使用堆疊的迭代版本</strong>:<br> </p> <pre class="brush:php;toolbar:false">function subsetsIterative(nums) { let stack = [{start: 0, currentSubset: []}]; let result = []; while (stack.length > 0) { let { start, currentSubset } = stack.pop(); result.push([...currentSubset]); // Explore subsets by including elements from `start` onwards for (let i = start; i <p>這個迭代版本使用堆疊來儲存排列的目前狀態。回溯是透過從堆疊中壓入和彈出狀態來處理的。 </p> <hr> <h4> 例 5:N 皇后問題(回溯) </h4> <p>N 皇后問題通常使用遞歸回溯來解決:<br> </p> <pre class="brush:php;toolbar:false">function permute(nums) { let result = []; function backtrack(start) { if (start === nums.length) { result.push([...nums]); return; } for (let i = start; i <p><strong>使用堆疊的迭代版本</strong>:<br> </p><pre class="brush:php;toolbar:false">function recursiveFunction(args) { // Base case if (baseCondition) { // Handle the base case return; } // Recursive calls for (let i = 0; i <hr> <h3> 結論 </h3> <p>使用堆疊將遞歸轉換為迭代對於許多演算法來說是一項很有價值的技術,特別是那些涉及回溯或樹/圖遍歷的演算法。透過使用顯式堆疊,我們可以避免深度遞歸,手動管理函數狀態,並確保我們更好地控制演算法的執行。這些範例應該作為指南來幫助您解決自己程式碼中的類似問題。 </p>
以上是使用堆疊將遞歸轉換為迭代:實用指南的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript字符串替換方法詳解及常見問題解答 本文將探討兩種在JavaScript中替換字符串字符的方法:在JavaScript代碼內部替換和在網頁HTML內部替換。 在JavaScript代碼內部替換字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 該方法僅替換第一個匹配項。要替換所有匹配項,需使用正則表達式並添加全局標誌g: str = str.replace(/fi

本教程向您展示瞭如何將自定義的Google搜索API集成到您的博客或網站中,提供了比標準WordPress主題搜索功能更精緻的搜索體驗。 令人驚訝的是簡單!您將能夠將搜索限制為Y

因此,在這裡,您準備好了解所有稱為Ajax的東西。但是,到底是什麼? AJAX一詞是指用於創建動態,交互式Web內容的一系列寬鬆的技術。 Ajax一詞,最初由Jesse J創造

本文系列在2017年中期進行了最新信息和新示例。 在此JSON示例中,我們將研究如何使用JSON格式將簡單值存儲在文件中。 使用鍵值對符號,我們可以存儲任何類型的

利用輕鬆的網頁佈局:8 ESTISSEL插件jQuery大大簡化了網頁佈局。 本文重點介紹了簡化該過程的八個功能強大的JQuery插件,對於手動網站創建特別有用

核心要點 JavaScript 中的 this 通常指代“擁有”該方法的對象,但具體取決於函數的調用方式。 沒有當前對象時,this 指代全局對象。在 Web 瀏覽器中,它由 window 表示。 調用函數時,this 保持全局對象;但調用對象構造函數或其任何方法時,this 指代對象的實例。 可以使用 call()、apply() 和 bind() 等方法更改 this 的上下文。這些方法使用給定的 this 值和參數調用函數。 JavaScript 是一門優秀的編程語言。幾年前,這句話可

jQuery是一個很棒的JavaScript框架。但是,與任何圖書館一樣,有時有必要在引擎蓋下發現發生了什麼。也許是因為您正在追踪一個錯誤,或者只是對jQuery如何實現特定UI感到好奇

該帖子編寫了有用的作弊表,參考指南,快速食譜以及用於Android,BlackBerry和iPhone應用程序開發的代碼片段。 沒有開發人員應該沒有他們! 觸摸手勢參考指南(PDF)是Desig的寶貴資源


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3 Linux新版
SublimeText3 Linux最新版