图解LeetCode:79递归实现单词搜索

网格 (board):

  1. 单词搜索
    中等
    给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
    单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

    在这里插入图片描述
[['A', 'B', 'C', 'E'],['S', 'F', 'C', 'S'],['A', 'D', 'E', 'E']
]- 单词 (word): "ABCCED"

代码拆解:exist 函数

bool exist(vector<vector<char>> &board, string word) {if (board.empty() || word.empty())return false;for (int i = 0; i < board.size(); i++) {for (int j = 0; j < board[0].size(); j++) {if (board[i][j] == word[0]) {  // 找到首字母匹配的单元格if (backtrack(board, word, i, j, 0)) {return true;}}}}return false;
}
执行步骤
遍历网格:外层循环 i 遍历行,内层循环 j 遍历列。
找到首字母匹配:当 board[i][j] == word[0] 时,调用 backtrack 函数开始回溯搜索。
示例中,找到两个匹配位置:(0,0) 和 (2,0)。
我们先从 (0,0) 开始分析。
bool backtrack(vector<vector<char>> &board, string &word, int i, int j, int index) {// 边界检查和字符匹配检查if (i < 0 || i >= board.size() || j < 0 || j >= board[0].size() ||board[i][j] != word[index]) {return false;}// 所有字符都已匹配成功if (index == word.length() - 1) {return true;}// 标记当前单元格为已访问char temp = board[i][j];board[i][j] = '#';// 递归检查四个方向bool found = backtrack(board, word, i + 1, j, index + 1) || // 下backtrack(board, word, i - 1, j, index + 1) || // 上backtrack(board, word, i, j + 1, index + 1) || // 右backtrack(board, word, i, j - 1, index + 1);   // 左// 恢复当前单元格board[i][j] = temp;return found;
}

详细执行过程陈述:从 (0,0) 开始

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

backtrack(0,0,0)  →  A↓ 右
backtrack(0,1,1)  →  B↓ 右
backtrack(0,2,2)  →  C↓ 下
backtrack(1,2,3)  →  C↓ 下
backtrack(2,2,4)  →  E↓ 左
backtrack(2,1,5)  →  D ✅

文本描述补充

> 边界检查:i=0, j=0 在网格内,且 board[0][0] == 'A' == word[0],通过检查。
是否匹配最后一个字符:index=0,word.length()-1=5,不满足,继续。
标记当前单元格:board[0][0] 被标记为 '#'。
递归检查四个方向:
下:i+1=1, j=0,board[1][0]='S' != word[1]='B',返回 false。
上:i-1=-1,越界,返回 false。
右:i=0, j+1=1,board[0][1]='B' == word[1],递归调用 backtrack(0,1,1)。
左:j-1=-1,越界,返回 false。递归调用 backtrack (0,1,1)
边界检查:i=0, j=1 在网格内,且 board[0][1] == 'B' == word[1],通过检查。
是否匹配最后一个字符:index=1,不满足,继续。
标记当前单元格:board[0][1] 被标记为 '#'。
递归检查四个方向:
下:i+1=1, j=1,board[1][1]='F' != word[2]='C',返回 false。
上:i-1=-1,越界,返回 false。
右:i=0, j+1=2,board[0][2]='C' == word[2],递归调用 backtrack(0,2,2)。
左:board[0][0]='#' != word[2]='C',返回 false。
递归调用 backtrack (0,2,2)边界检查:i=0, j=2 在网格内,且 board[0][2] == 'C' == word[2],通过检查。
是否匹配最后一个字符:index=2,不满足,继续。
标记当前单元格:board[0][2] 被标记为 '#'。
递归检查四个方向:
下:i+1=1, j=2,board[1][2]='C' == word[3],递归调用 backtrack(1,2,3)。
上:i-1=-1,越界,返回 false。
右:i=0, j+1=3,board[0][3]='E' != word[3]='C',返回 false。
左:board[0][1]='#' != word[3]='C',返回 false。递归调用 backtrack (1,2,3)
边界检查:i=1, j=2 在网格内,且 board[1][2] == 'C' == word[3],通过检查。
是否匹配最后一个字符:index=3,不满足,继续。
标记当前单元格:board[1][2] 被标记为 '#'。
递归检查四个方向:
下:i+1=2, j=2,board[2][2]='E' == word[4],递归调用 backtrack(2,2,4)。
上:board[0][2]='#' != word[4]='E',返回 false。
右:i=1, j+1=3,board[1][3]='S' != word[4]='E',返回 false。
左:i=1, j-1=1,board[1][1]='F' != word[4]='E',返回 false。递归调用 backtrack (2,2,4)
边界检查:i=2, j=2 在网格内,且 board[2][2] == 'E' == word[4],通过检查。
是否匹配最后一个字符:index=4,不满足,继续。
标记当前单元格:board[2][2] 被标记为 '#'。
递归检查四个方向:
下:i+1=3,越界,返回 false。
上:board[1][2]='#' != word[5]='D',返回 false。
右:i=2, j+1=3,board[2][3]='E' != word[5]='D',返回 false。
左:i=2, j-1=1,board[2][1]='D' == word[5],递归调用 backtrack(2,1,5)。递归调用 backtrack (2,1,5)
边界检查:i=2, j=1 在网格内,且 board[2][1] == 'D' == word[5],通过检查。
是否匹配最后一个字符:index=5 == word.length()-1,满足!返回 true。
回溯恢复过程当 backtrack(2,1,5) 返回 true 后,递归调用逐层返回:backtrack(2,2,4) 恢复 board[2][2]'E',返回 truebacktrack(1,2,3) 恢复 board[1][2]'C',返回 truebacktrack(0,2,2) 恢复 board[0][2]'C',返回 truebacktrack(0,1,1) 恢复 board[0][1]'B',返回 truebacktrack(0,0,0) 恢复 board[0][0]'A',返回 true。
最终结果
从 (0,0) 出发,成功找到路径 A → B → C → C → E → D。
exist 函数返回 true

网格状态变化

1. 初始状态:[['A', 'B', 'C', 'E'],['S', 'F', 'C', 'S'],['A', 'D', 'E', 'E']]2. 标记 (0,0):[['#', 'B', 'C', 'E'],['S', 'F', 'C', 'S'],['A', 'D', 'E', 'E']]3. 标记 (0,1):[['#', '#', 'C', 'E'],['S', 'F', 'C', 'S'],['A', 'D', 'E', 'E']]4. 标记 (0,2):[['#', '#', '#', 'E'],['S', 'F', 'C', 'S'],['A', 'D', 'E', 'E']]5. 标记 (1,2):[['#', '#', '#', 'E'],['S', 'F', '#', 'S'],['A', 'D', 'E', 'E']]6. 标记 (2,2):[['#', '#', '#', 'E'],['S', 'F', '#', 'S'],['A', 'D', '#', 'E']]7. 标记 (2,1):[['#', '#', '#', 'E'],['S', 'F', '#', 'S'],['A', '#', '#', 'E']] ✅

总结

递归回溯:通过递归尝试所有可能的路径,遇到不匹配的情况时回溯。
标记与恢复:临时标记已访问的单元格,防止重复使用,递归返回前恢复现场。
剪枝优化:一旦找到完整路径,立即终止搜索,避免不必要的计算。

这个过程展示了回溯算法如何高效地在二维网格中搜索目标单词

补充实现动画演示的HTML代码(AI生成)

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>单词搜索回溯过程可视化</title><script src="https://cdn.tailwindcss.com"></script><link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"><script>tailwind.config = {theme: {extend: {colors: {primary: '#6C5CE7',secondary: '#00CEFF',accent: '#FC5C65',danger: '#E55039',dark: '#2D3436',light: '#F7F1E3'},fontFamily: {inter: ['Inter', 'sans-serif'],},}}}</script><style type="text/tailwindcss">@layer utilities {.content-auto {content-visibility: auto;}.grid-cell {@apply w-14 h-14 flex items-center justify-center border-2 rounded-xl font-bold text-xl transition-all duration-300 shadow-md;}.grid-cell-start {@apply bg-primary/20 border-primary text-primary animate-pulse;}.grid-cell-current {@apply bg-accent/30 border-accent text-accent transform scale-110 z-10 animate-bounce;}.grid-cell-visited {@apply bg-secondary/20 border-secondary text-secondary;}.grid-cell-failed {@apply bg-danger/20 border-danger text-danger line-through;}.step-card {@apply p-4 rounded-xl shadow-lg bg-white transition-all duration-300 hover:shadow-xl;}.step-number {@apply inline-flex items-center justify-center w-10 h-10 rounded-full bg-primary text-white font-bold mr-3 text-lg;}.direction-arrow {@apply text-xl font-bold transition-all duration-300;}.direction-arrow-active {@apply text-accent scale-125;}.recursive-call {@apply pl-6 border-l-4 border-primary/30 ml-4 my-2;}.stack-item {@apply px-3 py-1 rounded-lg bg-primary/10 text-primary my-1 inline-block;}}</style>
</head>
<body class="bg-gradient-to-br from-purple-50 to-indigo-100 min-h-screen font-inter"><div class="container mx-auto px-4 py-8 max-w-7xl"><header class="text-center mb-10"><h1 class="text-[clamp(2rem,5vw,3rem)] font-bold text-transparent bg-clip-text bg-gradient-to-r from-primary to-indigo-600 mb-4">单词搜索回溯过程可视化</h1><p class="text-gray-600 max-w-2xl mx-auto text-lg">通过动画演示回溯算法如何在二维网格中搜索目标单词 "ABCCED"</p></header><div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-10"><!-- 左侧:步骤说明 --><div class="lg:col-span-1 space-y-6"><div class="step-card bg-primary/10"><div class="step-number">1</div><p class="text-dark">初始调用 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(0,0,0)</code></p><p class="mt-2 text-gray-600">检查 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[0][0]='A'</code> 是否匹配 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">word[0]='A'</code></p></div><div class="step-card bg-gray-100" id="step-2"><div class="step-number">2</div><p class="text-dark">匹配成功!标记当前单元格为已访问</p><p class="mt-2 text-gray-600">临时将 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[0][0]</code> 替换为 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">'#'</code> 防止重复访问</p></div><div class="step-card bg-gray-100" id="step-3"><div class="step-number">3</div><p class="text-dark">递归检查四个方向:</p><ul class="ml-6 mt-2 space-y-2"><li class="flex items-start"><i class="fa fa-arrow-down text-gray-400 mr-2 mt-1" id="arrow-down"></i><span><span class="font-semibold">下:</span><code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[1][0]='S'</code>,不匹配 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">word[1]='B'</code>,返回 <code class="bg-danger/20 px-1 py-0.5 rounded text-danger">false</code></span></li><li class="flex items-start"><i class="fa fa-arrow-up text-gray-400 mr-2 mt-1" id="arrow-up"></i><span><span class="font-semibold">上:</span>坐标 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">(i-1,j) = (-1,0)</code> 越界,返回 <code class="bg-danger/20 px-1 py-0.5 rounded text-danger">false</code></span></li><li class="flex items-start"><i class="fa fa-arrow-right text-gray-400 mr-2 mt-1" id="arrow-right"></i><span><span class="font-semibold">右:</span><code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[0][1]='B'</code>,匹配 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">word[1]='B'</code>,递归调用 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(0,1,1)</code></span></li><li class="flex items-start"><i class="fa fa-arrow-left text-gray-400 mr-2 mt-1" id="arrow-left"></i><span><span class="font-semibold">左:</span>坐标 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">(i,j-1) = (0,-1)</code> 越界,返回 <code class="bg-danger/20 px-1 py-0.5 rounded text-danger">false</code></span></li></ul></div><div class="step-card bg-gray-100 hidden" id="step-4"><div class="step-number">4</div><p class="text-dark">递归调用 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(0,1,1)</code></p><div class="recursive-call"><p class="mb-2"><span class="font-semibold">边界检查:</span><code class="bg-primary/20 px-1 py-0.5 rounded text-primary">i=0, j=1</code> 在网格内,且 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[0][1] == 'B' == word[1]</code></p><p class="mb-2"><span class="font-semibold">字符匹配:</span><code class="bg-primary/20 px-1 py-0.5 rounded text-primary">index=1</code>,未达到最后一个字符,继续递归</p><p><span class="font-semibold">标记单元格:</span><code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[0][1]</code> 标记为 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">'#'</code></p></div></div><div class="step-card bg-gray-100 hidden" id="step-5"><div class="step-number">5</div><p class="text-dark"><code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(0,1,1)</code> 中检查四个方向:</p><ul class="ml-6 mt-2 space-y-2"><li class="flex items-start"><i class="fa fa-arrow-down text-gray-400 mr-2 mt-1" id="arrow-down-2"></i><span><span class="font-semibold">下:</span><code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[1][1]='F'</code>,不匹配 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">word[2]='C'</code>,返回 <code class="bg-danger/20 px-1 py-0.5 rounded text-danger">false</code></span></li><li class="flex items-start"><i class="fa fa-arrow-up text-gray-400 mr-2 mt-1" id="arrow-up-2"></i><span><span class="font-semibold">上:</span>坐标越界,返回 <code class="bg-danger/20 px-1 py-0.5 rounded text-danger">false</code></span></li><li class="flex items-start"><i class="fa fa-arrow-right text-gray-400 mr-2 mt-1" id="arrow-right-2"></i><span><span class="font-semibold">右:</span><code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[0][2]='C'</code>,匹配 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">word[2]='C'</code>,递归调用 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(0,2,2)</code></span></li><li class="flex items-start"><i class="fa fa-arrow-left text-gray-400 mr-2 mt-1" id="arrow-left-2"></i><span><span class="font-semibold">左:</span><code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[0][0]='#'</code> 已访问,返回 <code class="bg-danger/20 px-1 py-0.5 rounded text-danger">false</code></span></li></ul></div><!-- 更多步骤可以继续添加 --></div><!-- 右侧:网格可视化 --><div class="lg:col-span-2 bg-white rounded-2xl shadow-xl p-6 transform transition-all duration-500 hover:shadow-2xl"><div class="flex justify-between items-center mb-6"><h2 class="text-2xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-primary to-indigo-600">网格状态</h2><div class="flex space-x-4"><button id="restart-btn" class="px-5 py-2.5 bg-gray-200 hover:bg-gray-300 rounded-xl transition-colors flex items-center"><i class="fa fa-refresh mr-2"></i> 重新开始</button><button id="prev-btn" class="px-5 py-2.5 bg-gray-200 hover:bg-gray-300 rounded-xl transition-colors disabled:opacity-50 flex items-center"><i class="fa fa-step-backward mr-2"></i> 上一步</button><button id="next-btn" class="px-5 py-2.5 bg-gradient-to-r from-primary to-indigo-600 hover:opacity-90 text-white rounded-xl transition-all shadow-md hover:shadow-lg flex items-center">下一步 <i class="fa fa-step-forward ml-2"></i></button></div></div><div class="flex justify-center mb-8"><div class="text-xl font-semibold text-dark" id="current-step">步骤 1/10</div></div><!-- 网格 --><div class="grid grid-cols-4 gap-4 justify-center mb-10 relative"><div class="grid-cell grid-cell-start" id="cell-0-0">A</div><div class="grid-cell" id="cell-0-1">B</div><div class="grid-cell" id="cell-0-2">C</div><div class="grid-cell" id="cell-0-3">E</div><div class="grid-cell" id="cell-1-0">S</div><div class="grid-cell" id="cell-1-1">F</div><div class="grid-cell" id="cell-1-2">C</div><div class="grid-cell" id="cell-1-3">S</div><div class="grid-cell" id="cell-2-0">A</div><div class="grid-cell" id="cell-2-1">D</div><div class="grid-cell" id="cell-2-2">E</div><div class="grid-cell" id="cell-2-3">E</div><!-- 方向箭头 --><div class="absolute invisible" id="direction-arrow" style="width: 30px; height: 30px; background-color: #FC5C65; clip-path: polygon(50% 0%, 0% 100%, 100% 100%); transform: rotate(90deg); z-index: 20;"></div></div><!-- 调用栈可视化 --><div class="bg-gray-50 rounded-xl p-4 mb-6"><h3 class="font-bold mb-3 text-dark flex items-center"><i class="fa fa-code mr-2 text-primary"></i> 递归调用栈</h3><div id="call-stack" class="flex flex-wrap gap-2"><div class="stack-item">backtrack(0,0,0)</div></div></div><!-- 当前状态 --><div class="bg-gradient-to-r from-primary/5 to-indigo-50 rounded-xl p-5"><h3 class="font-bold mb-3 text-dark text-lg">当前状态</h3><p id="status-text" class="text-gray-700 text-lg">正在检查 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[0][0]</code> 是否匹配 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">word[0]</code>...</p></div></div></div><!-- 单词匹配路径 --><div class="mt-10 bg-white rounded-2xl shadow-xl p-6"><h2 class="text-2xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-primary to-indigo-600 mb-6">单词匹配路径</h2><div class="flex flex-wrap justify-center items-center space-x-4 md:space-x-8"><div class="w-16 h-16 rounded-full bg-primary flex items-center justify-center text-white font-bold text-2xl shadow-lg" id="path-0">A</div><div class="direction-arrow text-gray-400 text-2xl" id="path-arrow-1"></div><div class="w-16 h-16 rounded-full bg-gray-200 flex items-center justify-center text-gray-700 font-bold text-2xl shadow-md" id="path-1">B</div><div class="direction-arrow text-gray-400 text-2xl"></div><div class="w-16 h-16 rounded-full bg-gray-200 flex items-center justify-center text-gray-700 font-bold text-2xl shadow-md" id="path-2">C</div><div class="direction-arrow text-gray-400 text-2xl"></div><div class="w-16 h-16 rounded-full bg-gray-200 flex items-center justify-center text-gray-700 font-bold text-2xl shadow-md" id="path-3">C</div><div class="direction-arrow text-gray-400 text-2xl"></div><div class="w-16 h-16 rounded-full bg-gray-200 flex items-center justify-center text-gray-700 font-bold text-2xl shadow-md" id="path-4">E</div><div class="direction-arrow text-gray-400 text-2xl"></div><div class="w-16 h-16 rounded-full bg-gray-200 flex items-center justify-center text-gray-700 font-bold text-2xl shadow-md" id="path-5">D</div></div></div><!-- 回溯算法说明 --><div class="mt-10 bg-white rounded-2xl shadow-xl p-6"><h2 class="text-2xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-primary to-indigo-600 mb-4">回溯算法原理</h2><div class="grid grid-cols-1 md:grid-cols-2 gap-6"><div><h3 class="font-bold text-lg mb-3 text-primary">什么是回溯算法?</h3><p class="text-gray-700 mb-4">回溯算法是一种通过尝试所有可能的路径来寻找问题解的算法。当它发现当前路径不可能得到解时,它会"回溯"到上一步,尝试其他路径。</p><h3 class="font-bold text-lg mb-3 text-primary">在此问题中的应用</h3><p class="text-gray-700">在单词搜索问题中,回溯算法从网格的每个单元格开始,递归地检查四个方向:上、下、左、右。如果当前单元格匹配单词的当前字符,则继续递归检查下一个字符,直到找到完整的单词或确定无法找到。</p></div><div><h3 class="font-bold text-lg mb-3 text-primary">关键步骤</h3><ol class="list-decimal list-inside text-gray-700 space-y-2"><li><span class="font-semibold">边界检查:</span>确保当前位置在网格内</li><li><span class="font-semibold">字符匹配:</span>检查当前单元格是否匹配单词的当前字符</li><li><span class="font-semibold">标记访问:</span>临时标记当前单元格为已访问,防止重复使用</li><li><span class="font-semibold">递归搜索:</span>尝试四个方向的相邻单元格</li><li><span class="font-semibold">恢复状态:</span>递归返回后恢复单元格的原始值</li></ol></div></div></div></div><footer class="mt-16 py-8 bg-gradient-to-r from-primary/10 to-indigo-100"><div class="container mx-auto px-4 text-center text-gray-600"><p>单词搜索回溯过程可视化 &copy; 2023</p><p class="mt-2 text-sm">使用 Tailwind CSS 和 Font Awesome 构建</p></div></footer><script>// 当前步骤let currentStep = 1;// 总步骤数const totalSteps = 10;// 单元格状态const cells = {};for (let i = 0; i < 3; i++) {for (let j = 0; j < 4; j++) {cells[`${i}-${j}`] = document.getElementById(`cell-${i}-${j}`);}}// 路径节点const pathNodes = {};for (let i = 0; i < 6; i++) {pathNodes[i] = document.getElementById(`path-${i}`);}// 方向箭头const directionArrow = document.getElementById('direction-arrow');// 更新步骤显示function updateStepDisplay() {document.getElementById('current-step').textContent = `步骤 ${currentStep}/${totalSteps}`;// 重置所有步骤卡片样式document.querySelectorAll('.step-card').forEach(card => {card.classList.remove('bg-primary/10');card.classList.add('bg-gray-100');card.classList.add('hidden');});// 显示当前步骤及之前的步骤for (let i = 1; i <= Math.min(currentStep, 5); i++) {const stepCard = document.getElementById(`step-${i}`);if (stepCard) {stepCard.classList.remove('hidden');if (i === currentStep) {stepCard.classList.remove('bg-gray-100');stepCard.classList.add('bg-primary/10');}}}// 更新按钮状态document.getElementById('prev-btn').disabled = currentStep === 1;document.getElementById('next-btn').disabled = currentStep === totalSteps;// 更新路径节点状态for (let i = 0; i < 6; i++) {if (i < currentStep - 1) {pathNodes[i].classList.remove('bg-gray-200');pathNodes[i].classList.add('bg-secondary');} else if (i === currentStep - 1) {pathNodes[i].classList.remove('bg-gray-200');pathNodes[i].classList.add('bg-accent');pathNodes[i].classList.add('animate-pulse');} else {pathNodes[i].classList.remove('bg-secondary', 'bg-accent', 'animate-pulse');pathNodes[i].classList.add('bg-gray-200');}}// 更新方向箭头document.querySelectorAll('.direction-arrow').forEach(arrow => {arrow.classList.remove('direction-arrow-active');});// 更新调用栈updateCallStack();// 更新状态文本和网格状态updateStatusText();}// 更新调用栈function updateCallStack() {const callStack = document.getElementById('call-stack');callStack.innerHTML = '';// 根据当前步骤构建调用栈let stackItems = [];switch(currentStep) {case 1:case 2:case 3:stackItems = ['backtrack(0,0,0)'];break;case 4:case 5:stackItems = ['backtrack(0,0,0)', 'backtrack(0,1,1)'];break;case 6:case 7:stackItems = ['backtrack(0,0,0)', 'backtrack(0,1,1)', 'backtrack(0,2,2)'];break;case 8:stackItems = ['backtrack(0,0,0)', 'backtrack(0,1,1)', 'backtrack(0,2,2)', 'backtrack(1,2,3)'];break;case 9:stackItems = ['backtrack(0,0,0)', 'backtrack(0,1,1)', 'backtrack(0,2,2)', 'backtrack(1,2,3)', 'backtrack(2,2,4)'];break;case 10:stackItems = ['backtrack(0,0,0)', 'backtrack(0,1,1)', 'backtrack(0,2,2)', 'backtrack(1,2,3)', 'backtrack(2,2,4)', 'backtrack(2,1,5)'];break;}// 添加到DOMstackItems.forEach((item, index) => {const stackItem = document.createElement('div');stackItem.className = 'stack-item';stackItem.textContent = item;// 高亮当前调用if (index === stackItems.length - 1) {stackItem.classList.add('bg-accent/20');stackItem.classList.add('text-accent');stackItem.classList.add('font-bold');}callStack.appendChild(stackItem);});}// 更新状态文本function updateStatusText() {const statusText = document.getElementById('status-text');directionArrow.classList.add('invisible');// 重置所有单元格样式for (let i = 0; i < 3; i++) {for (let j = 0; j < 4; j++) {const cellId = `${i}-${j}`;cells[cellId].classList.remove('grid-cell-current', 'grid-cell-visited', 'grid-cell-failed');// 恢复原始值if (i === 0 && j === 0) cells[cellId].textContent = 'A';if (i === 0 && j === 1) cells[cellId].textContent = 'B';if (i === 0 && j === 2) cells[cellId].textContent = 'C';if (i === 1 && j === 2) cells[cellId].textContent = 'C';if (i === 2 && j === 2) cells[cellId].textContent = 'E';if (i === 2 && j === 1) cells[cellId].textContent = 'D';}}switch(currentStep) {case 1:statusText.innerHTML = `正在检查 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[0][0]</code> 是否匹配 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">word[0]='A'</code>...`;cells['0-0'].classList.add('grid-cell-current');cells['0-0'].classList.remove('grid-cell-start');break;case 2:statusText.innerHTML = `匹配成功!标记 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[0][0]</code> 为已访问`;cells['0-0'].classList.remove('grid-cell-current');cells['0-0'].classList.add('grid-cell-visited');cells['0-0'].textContent = '#';break;case 3:statusText.innerHTML = `正在检查 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">board[0][0]</code> 的四个方向:`;cells['0-0'].classList.add('grid-cell-current');cells['0-0'].classList.remove('grid-cell-visited');cells['0-0'].textContent = 'A';// 激活右侧箭头document.getElementById('arrow-right').classList.add('direction-arrow-active');document.getElementById('path-arrow-1').classList.add('direction-arrow-active');// 显示方向箭头positionDirectionArrow(0, 0, 'right');break;case 4:statusText.innerHTML = `递归调用 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(0,1,1)</code>`;cells['0-1'].classList.add('grid-cell-current');cells['0-0'].classList.add('grid-cell-visited');cells['0-0'].textContent = '#';break;case 5:statusText.innerHTML = `在 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(0,1,1)</code> 中检查四个方向:`;cells['0-1'].classList.add('grid-cell-current');cells['0-0'].classList.add('grid-cell-visited');cells['0-0'].textContent = '#';// 激活右侧箭头document.getElementById('arrow-right-2').classList.add('direction-arrow-active');// 显示方向箭头positionDirectionArrow(0, 1, 'right');break;case 6:statusText.innerHTML = `递归调用 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(0,2,2)</code>`;cells['0-2'].classList.add('grid-cell-current');cells['0-1'].classList.add('grid-cell-visited');cells['0-1'].textContent = '#';break;case 7:statusText.innerHTML = `在 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(0,2,2)</code> 中检查四个方向:`;cells['0-2'].classList.add('grid-cell-current');cells['0-1'].classList.add('grid-cell-visited');cells['0-1'].textContent = '#';// 激活下侧箭头document.getElementById('arrow-down').classList.add('direction-arrow-active');// 显示方向箭头positionDirectionArrow(0, 2, 'down');break;case 8:statusText.innerHTML = `递归调用 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(1,2,3)</code>`;cells['1-2'].classList.add('grid-cell-current');cells['0-2'].classList.add('grid-cell-visited');cells['0-2'].textContent = '#';break;case 9:statusText.innerHTML = `递归调用 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(2,2,4)</code>`;cells['2-2'].classList.add('grid-cell-current');cells['1-2'].classList.add('grid-cell-visited');cells['1-2'].textContent = '#';// 显示方向箭头positionDirectionArrow(1, 2, 'down');break;case 10:statusText.innerHTML = `递归调用 <code class="bg-primary/20 px-1 py-0.5 rounded text-primary">backtrack(2,1,5)</code> - <span class="text-accent font-bold">找到最后一个字符 'D'!</span>`;cells['2-1'].classList.add('grid-cell-current');cells['2-2'].classList.add('grid-cell-visited');cells['2-2'].textContent = '#';// 显示方向箭头positionDirectionArrow(2, 2, 'left');// 激活所有路径箭头document.querySelectorAll('.direction-arrow').forEach(arrow => {arrow.classList.add('direction-arrow-active');});// 高亮所有路径节点for (let i = 0; i < 6; i++) {pathNodes[i].classList.remove('bg-gray-200', 'bg-primary');pathNodes[i].classList.add('bg-accent');pathNodes[i].classList.add('animate-pulse');}break;}}// 定位方向箭头function positionDirectionArrow(i, j, direction) {const cell = cells[`${i}-${j}`];const cellRect = cell.getBoundingClientRect();const container = document.querySelector('.grid');const containerRect = container.getBoundingClientRect();const cellSize = cellRect.width;const arrowSize = 30;let top, left, rotate;switch(direction) {case 'right':top = cellRect.top - containerRect.top + cellSize/2 - arrowSize/2;left = cellRect.right - containerRect.left;rotate = 0;break;case 'down':top = cellRect.bottom - containerRect.top;left = cellRect.left - containerRect.left + cellSize/2 - arrowSize/2;rotate = 90;break;case 'left':top = cellRect.top - containerRect.top + cellSize/2 - arrowSize/2;left = cellRect.left - containerRect.left - arrowSize;rotate = 180;break;case 'up':top = cellRect.top - containerRect.top - arrowSize;left = cellRect.left - containerRect.left + cellSize/2 - arrowSize/2;rotate = 270;break;}// 设置箭头位置和旋转directionArrow.style.top = `${top}px`;directionArrow.style.left = `${left}px`;directionArrow.style.transform = `rotate(${rotate}deg)`;directionArrow.classList.remove('invisible');}// 重新开始function restartVisualization() {// 重置所有状态currentStep = 1;// 重置路径节点for (let i = 0; i < 6; i++) {pathNodes[i].classList.remove('bg-secondary', 'bg-accent', 'animate-pulse');pathNodes[i].classList.add('bg-gray-200');}// 重置方向箭头document.querySelectorAll('.direction-arrow').forEach(arrow => {arrow.classList.remove('direction-arrow-active');});// 更新显示updateStepDisplay();}// 初始化页面document.addEventListener('DOMContentLoaded', function() {// 初始化显示updateStepDisplay();// 设置按钮事件document.getElementById('prev-btn').addEventListener('click', function() {if (currentStep > 1) {currentStep--;updateStepDisplay();}});document.getElementById('next-btn').addEventListener('click', function() {if (currentStep < totalSteps) {currentStep++;updateStepDisplay();}});// 添加重新开始按钮事件document.getElementById('restart-btn').addEventListener('click', restartVisualization);// 添加键盘导航document.addEventListener('keydown', function(e) {if (e.key === 'ArrowLeft' && currentStep > 1) {currentStep--;updateStepDisplay();} else if (e.key === 'ArrowRight' && currentStep < totalSteps) {currentStep++;updateStepDisplay();} else if (e.key === 'r' || e.key === 'R') {restartVisualization();}});});</script>
</body>
</html>    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/bicheng/89195.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025 R3CTF

文章目录EvalgelistSilent Profit&#xff08;复现&#xff09;Evalgelist <?phpif (isset($_GET[input])) {echo <div class"output">;$filtered str_replace([$, (, ), , ", "", "", ":", "/", "!&…

WebView JSBridge 无响应问题排查实录 全流程定位桥接调用失效

在混合开发项目中&#xff0c;Web 页面与 Native 的通信桥梁——JSBridge&#xff0c;承担着极为关键的角色。它不仅让网页能调起原生功能&#xff08;分享、登录、拍照等&#xff09;&#xff0c;也支持原生传值、事件回调。 然而&#xff0c;当 JSBridge 调用“没有响应”、c…

前端构建工具 Webpack 5 的优化策略与高级配置

前端构建工具 Webpack 5 的优化策略与高级配置 当你的项目启动需要一分钟&#xff0c;或者每次热更新都像在“编译整个宇宙”时&#xff0c;你可能已经意识到了一个问题&#xff1a;前端构建性能&#xff0c;正成为开发效率的瓶颈。Webpack 作为现代前端开发的基石&#xff0c;…

tun2socks原理浅析

tun2socks 的原理是将TUN 设备上的IP 数据包转换为SOCKS 协议数据&#xff0c;然后通过SOCKS 代理服务器发送。简单来说&#xff0c;它利用TUN 设备模拟一个虚拟网络接口&#xff0c;将所有流经该接口的网络流量重定向到SOCKS 代理&#xff0c;从而实现流量的代理转发&#xff…

Go从入门到精通(22) - 一个简单web项目-统一日志输出

Go从入门到精通(21) - 一个简单web项目-统一日志输出 统一日志输出 文章目录Go从入门到精通(21) - 一个简单web项目-统一日志输出前言日志库横向对比zap 使用安装依赖创建日志配置修改主程序的日志在处理函数中使用日志日志示例控制台输出文件输出&#xff08;json&#xff09…

UI前端大数据处理新挑战:如何高效处理实时数据流?

hello宝子们...我们是艾斯视觉擅长ui设计和前端数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩!一、引言&#xff1a;从 “批处理” 到 “流处理” 的前端革命当股票 APP 因每秒接收 10 万条行情数据…

【接口测试】08 Postman使用教程(带案例)

目录 一. Postman安装 二. Postman使用 1. 创建项目 2. 创建集合 3. 设置变量 4. 创建测试用例 5. 数据驱动测试 6. 接口关联 7. 断言和封装 8. 批量执行 9. 导出用例 10. 生成测试报告 一. Postman安装 PostMan——安装教程&#xff08;图文详解&#xff09;_postman安装教程-…

从springcloud-gateway了解同步和异步,webflux webMvc、共享变量

webMVC和webFlux 这是spring framework提供的两种不同的Web编程模型应用场景&#xff1a;用 WebMvc&#xff1a; 项目依赖 Servlet 生态、需要简单同步代码&#xff0c;或使用阻塞式数据库&#xff08;如 MySQL JDBC&#xff09;。用 WebFlux&#xff1a; 需要高并发&#xff…

如何在 Pytest 中调用其他用例返回的接口参数?

回答重点在 Pytest 中&#xff0c;我们可以通过使用共享夹具&#xff08;fixtures&#xff09;来调用和复用其他用例返回的接口参数。在 Pytest 中&#xff0c;fixtures 提供了一种灵活且有组织的方式来共享测试数据或对象。具体步骤如下&#xff1a;1&#xff09;首先&#xf…

倒计时熔断机制的出价逻辑

一、业务背景传统竞价机制中&#xff0c;“倒计时结束”是系统决定成交者的关键逻辑&#xff0c;但在实际中&#xff0c;最后3秒突然被抢价的情况极为常见&#xff0c;出现以下问题&#xff1a;用户投诉平台机制不公平&#xff1b;用户出价但未成交&#xff0c;产生争议订单&am…

未来手机会自动充电吗

未来手机实现‌全自动充电&#xff08;无需人为干预&#xff09;‌是技术发展的明确趋势&#xff0c;目前已有部分技术落地&#xff0c;但要达到“随时随地无感补电”&#xff0c;仍需突破以下关键领域&#xff1a;一、已实现的技术&#xff08;当下可用的“半自动”充电&#…

MySQL高级篇(二):深入理解数据库事务与MySQL锁机制

引言在现代数据库系统中&#xff0c;事务和锁机制是确保数据一致性和完整性的两大核心技术。无论是金融交易系统、电商平台还是企业级应用&#xff0c;都离不开这些基础功能的支持。本文将全面剖析数据库事务的四大特性&#xff0c;深入探讨MySQL中的各种锁机制&#xff0c;帮助…

XML 指南

XML 指南 引言 XML(可扩展标记语言)是一种用于存储和传输数据的标记语言,它具有高度的可扩展性和灵活性。在互联网和软件开发领域,XML被广泛应用于数据交换、配置文件、文档存储等场景。本文将为您详细介绍XML的基本概念、语法规则、应用场景以及开发技巧,帮助您全面了解…

Flink Watermark原理与实战

一、引言Flink 作为一款强大的流处理框架&#xff0c;在其中扮演着关键角色。今天&#xff0c;咱们来聊聊 Flink 中一个极为重要的概念 —— Watermark&#xff08;水位线&#xff09;&#xff0c;它是处理乱序数据和准确计算的关键。接下来我们直入主题&#xff0c;首先来看看…

Rust Web 全栈开发(五):使用 sqlx 连接 MySQL 数据库

Rust Web 全栈开发&#xff08;五&#xff09;&#xff1a;使用 sqlx 连接 MySQL 数据库Rust Web 全栈开发&#xff08;五&#xff09;&#xff1a;使用 sqlx 连接 MySQL 数据库项目创建数据库准备连接请求功能实现Rust Web 全栈开发&#xff08;五&#xff09;&#xff1a;使用…

【zynq7020】PS的“Hello World”

目录 基本过程 新建Vivado工程 ZYNQ IP核设置 使用SDK进行软件开发 基于Vivado2017 Vivado工程建立 SDK调试 固化程序 注&#xff1a;Vivado 2019.1 及之前&#xff1a;默认使用 SDK Vivado 2019.2-2020.1&#xff1a;逐步过渡&#xff0c;支持 SDK 与 Vitis 并存 Vi…

希尔排序和选择排序及计数排序的简单介绍

希尔排序法又称缩小增量法。希尔排序法的基本思想是&#xff1a;先选定一个整数gap&#xff0c;把待排序文件中所有数据分成几个组&#xff0c;所有距离为gap的数据分在同一组内&#xff0c;并对每一组内的数据进行排序。然后gap减减&#xff0c;重复上述分组和排序的工作。当到…

Solid Edge多项目并行,浮动许可如何高效调度?

在制造企业的数字化设计体系中&#xff0c;Solid Edge 作为主流 CAD 工具&#xff0c;因其灵活的建模能力、同步技术和强大的装配设计功能&#xff0c;广泛应用于机械设备、零部件制造等行业的研发场景。随着企业设计任务复杂化&#xff0c;多项目并行成为常态&#xff0c;Soli…

Flink cdc 使用总结

Flink 与 Flink CDC 版本兼容对照表Flink 版本支持的 Flink CDC 版本关键说明Flink 1.11.xFlink CDC 1.2.x早期版本&#xff0c;需注意 Flink 1.11.0 的 Bug&#xff08;如 Upsert 写入问题&#xff09;&#xff0c;建议使用 1.11.1 及以上。Flink 1.12.xFlink CDC 2.0.x&#…

企业培训笔记:axios 发送 ajax 请求

文章目录axios 简介一&#xff0c;Vue工程中安装axios二&#xff0c;编写app.vue三&#xff0c;编写HomeView.vue四&#xff0c;Idea打开后台项目五&#xff0c;创建HelloController六&#xff0c;配置web访问端口七&#xff0c;运行项目&#xff0c;查看效果&#xff08;一&am…