尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

freeCodeCamp 每日编码挑战解析:Connect 3 三连棋检测算法(Challenge 322)

freeCodeCamp 每日编码挑战解析:Connect 3 三连棋检测算法(Challenge 322) freeCodeCamp 每日编码挑战解析Connect 3 三连棋检测算法Challenge 322【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp本篇技术指南以 freeCodeCamp 开源仓库中的每日编码挑战Daily Coding ChallengesJavaScript 板块第 322 题 Connect 3 为对象完整还原题目规则、输入输出格式、全部测试用例与官方参考解答并结合仓库源码讲解其运行机制与算法原理。读完本文你将掌握矩阵方向枚举、边界检查、坐标规范化等二维数组遍历的核心技巧并了解该挑战在 freeCodeCamp 课程体系与后端 API 中的实际位置。挑战背景freeCodeCamp 的每日编码挑战体系本挑战文档位于curriculum/challenges/english/blocks/daily-coding-challenges-javascript/6a19b1062d1b153d8ac76d70.md归属于 JavaScript 每日编码挑战块block。从 块结构配置 可以看到整个块共包含 365 道挑战按天数编号Challenge 1 至 Challenge 365这正是每日一题的设计初衷——一年 365 天每天一道编码练习题。从仓库结构看该功能在项目中是完整闭环的课程内容挑战文档以 Markdown 形式存放在curriculum/challenges/english/blocks/daily-coding-challenges-javascript/目录下每份文档包含题目描述--description--、测试用例--hints--、初始代码--seed--与参考解答--solutions--四部分数据落地种子脚本 seed-daily-challenges.ts 从 GraphQL 拉取挑战数据并写入 MongoDB 的DailyCodingChallenges集合起始日期被固定为2025-08-11T00:00:00.000Z此后每个挑战顺延一天脚本还内置了数量校验期望 365 道与起始日期校验防止发布后误改后端服务daily-coding-challenge.ts 提供了按日期、按天、按月份、全部以及最新等 6 个只读 GET 接口供前端按日拉取题目信息前端展示客户端通过/learn/daily-coding-challenge/路由渲染当日挑战见 show-daily-coding-challenge.tsx并用 daily-coding-challenge-validator.ts 中的 Joi schema 校验后端返回的数据结构。Connect 3Challenge 322正是这 365 道题中的一道二维数组遍历类题目位于该块的后期部分。问题陈述精读输入函数connectThree(matrix)接收一个二维数组矩阵作为输入矩阵中的每个单元格是以下三种值之一取值含义R红色玩家的棋子Y黄色玩家的棋子空字符串表示该格为空判定规则三连three in a row的定义是水平、垂直或对角线上存在三个连续的、类型相同的非空单元格。也就是说三个R或三个Y必须紧挨着排成一条直线。返回值函数需要返回两种结果之一有赢家时返回一个扁平数组格式为[R, [0,2], [1,3], [2,4]]即依次是赢家标识R或Y和三个获胜格子的坐标。坐标以[行, 列]形式给出且必须按从上到下、从左到右的顺序排列top-to-bottom, then left-to-right无赢家时返回空数组[]。测试用例逐条剖析原文档共给出 5 组测试用例覆盖了水平、垂直、正对角线、反对角线和无赢家五种典型场景是理解题意的关键素材。用例 1水平三连connectThree([[, , , ], [, , , ], [, Y, , ], [Y, R, R, R]]) // 应返回 [R, [3, 1], [3, 2], [3, 3]]棋盘第 4 行索引 3从左到右是[Y, R, R, R]其中R在列索引 1、2、3 上连续出现三个构成水平三连。赢家为R坐标按从左到右返回。用例 2垂直三连connectThree([[, , , ], [, Y, Y, ], [, Y, R, R], [, Y, R, R]]) // 应返回 [Y, [1, 1], [2, 1], [3, 1]]观察列索引 1行 1、2、3 的取值分别是Y、Y、Y构成垂直三连。赢家为Y坐标按从上到下返回。用例 3正对角线三连connectThree([[, , Y, R], [, Y, R, Y], [, R, Y, R], [, R, Y, R]]) // 应返回 [R, [0, 3], [1, 2], [2, 1]](0,3)、(1,2)、(2,1)三个格子的值依次是R、R、R沿右上到左下的对角线方向即行递增、列递减连成一线。用例 4反对角线三连connectThree([[, Y, , ], [, Y, Y, ], [, R, R, Y], [R, R, Y, R]]) // 应返回 [Y, [0, 1], [1, 2], [2, 3]](0,1)、(1,2)、(2,3)三个格子均为Y沿左上到右下方向行、列同时递增连成一线。用例 5无赢家connectThree([[Y, R, R, Y], [R, Y, Y, R], [Y, R, R, Y], [R, Y, Y, R]]) // 应返回 []该棋盘是一个交替排列的Y/R棋盘任何方向上都找不到三个连续同类棋子因此返回空数组。初始代码与求解思路题目给出的种子代码--seed--部分非常简单function connectThree(matrix) { return matrix; }它原样返回矩阵显然不满足任何测试用例需要你补全算法。整体求解思路分三步第一步枚举四个方向三连只可能沿四个方向出现因此只需检查四种方向向量方向行增量 (dr)列增量 (dc)说明水平向右01检查同一行垂直向下10检查同一列右下对角线11主对角线方向左下对角线1-1副对角线方向由于水平向左是水平向右的反方向检查方向向量的正方向即可覆盖所有情况无需重复枚举。第二步以每个非空格为起点延伸遍历矩阵中的每一个格子跳过空字符串。对每个非空格沿四个方向依次检查第 1、2 个后续格共 3 格一旦遇到越界或类型不一致就中断该方向的检查。第三步命中后规范化坐标如果某个方向连续收集到 3 个相同棋子就对这 3 个坐标排序使结果满足从上到下、从左到右的输出要求再组装返回数组。官方参考解答逐行解析原文档--solutions--部分给出了完整的参考实现逐段解析如下function connectThree(matrix) { const rows matrix.length; const cols matrix[0].length; const directions [[0, 1], [1, 0], [1, 1], [1, -1]]; for (let r 0; r rows; r) { for (let c 0; c cols; c) { const piece matrix[r][c]; if (!piece) continue; for (const [dr, dc] of directions) { const cells [[r, c]]; for (let i 1; i 3; i) { const nr r dr * i; const nc c dc * i; if (nr 0 || nr rows || nc 0 || nc cols) break; if (matrix[nr][nc] ! piece) break; cells.push([nr, nc]); } if (cells.length 3) { cells.sort(([r1, c1], [r2, c2]) r1 ! r2 ? r1 - r2 : c1 - c2); return [piece, ...cells]; } } } } return []; }第 1-2 行读取矩阵的行数rows与列数cols用于后续边界判断第 4 行定义四个方向向量。这里选择向右、向下、右下、左下四个方向恰好覆盖水平、垂直、主对角线、副对角线第 6-7 行双重循环遍历所有格子第 8-9 行取出当前格子的棋子若为空字符串!piece为真注意是假值则跳过。这一行是性能关键空单元格既不可能成为任何三连的起点也无需作为终点检查直接剪枝第 11-21 行对每个方向从当前格开始沿方向延伸。cells数组初始只含起点循环i从 1 到 2检查第 2、3 个格子是否越界第 14 行或与起点棋子不同第 15 行任一条件不满足就break放弃该方向第 23-26 行cells.length 3意味着成功收集到三连。此时先对坐标排序优先按行号升序从上到下行号相同时按列号升序从左到右最后用展开语法组装成[piece, ...cells]扁平数组并立即返回第 30 行全部遍历结束仍未找到三连返回[]。注意排序回调中的解构写法cells.sort(([r1, c1], [r2, c2]) r1 ! r2 ? r1 - r2 : c1 - c2)它直接对坐标数组对[行, 列]解构比较规则完全对应题目要求的输出顺序。算法复杂度与正确性分析时间复杂度最坏情况下需要遍历全部rows × cols个格子每个格子最多检查 4 个方向、每个方向最多延伸 2 步因此总复杂度为O(rows × cols × 4 × 2)即O(rows × cols)的线性级别。对于 4×4 这样的小棋盘性能开销可以忽略空间复杂度除cells数组外没有额外的大型数据结构为O(1)不计入返回值正确性论证任意一条长度为 3 的直线段其最靠上行号最小、同行时最靠左列号最小的端点一定会在遍历时先被访问且该方向向量会被枚举到因此所有潜在三连都会被检测不会遗漏反之break条件保证了只有严格同类型且连续的格子才会被计入不会产生误报。实战验证在本地运行测试若想在本地验证你的实现可以按如下步骤在 freeCodeCamp 仓库环境中操作确保已按 README.md 安装好 pnpm 依赖将上述参考解答放入 Node.js 环境或直接在浏览器控制台执行依次运行文档中的 5 组assert.deepEqual断言assert.deepEqual(connectThree([[, , , ], [, , , ], [, Y, , ], [Y, R, R, R]]), [R, [3, 1], [3, 2], [3, 3]]); assert.deepEqual(connectThree([[, , , ], [, Y, Y, ], [, Y, R, R], [, Y, R, R]]), [Y, [1, 1], [2, 1], [3, 1]]); assert.deepEqual(connectThree([[, , Y, R], [, Y, R, Y], [, R, Y, R], [, R, Y, R]]), [R, [0, 3], [1, 2], [2, 1]]); assert.deepEqual(connectThree([[, Y, , ], [, Y, Y, ], [, R, R, Y], [R, R, Y, R]]), [Y, [0, 1], [1, 2], [2, 3]]); assert.deepEqual(connectThree([[Y, R, R, Y], [R, Y, Y, R], [Y, R, R, Y], [R, Y, Y, R]]), []);全部通过即代表实现正确。该挑战对应的块配置位于 daily-coding-challenges-javascript.json其中challengeType: 28标记了这类题目的类型而挑战文档本身存放在 challenge 文件 中。拓展思考从 Connect 3 到 Tic-Tac-Toe 与四子棋Connect 3 的解法框架具有极强的可扩展性可以推广到一系列同类博弈判定问题推广连子数将内层循环的i 3改为i n即可检测任意长度的连子例如五子棋的五连推广到 Tic-Tac-Toe 完整判定本块中的 Challenge 153: Tic-Tac-Toe 就是同族题目需要同时处理胜负判定与平局判定方向向量表驱动把directions数组抽象为可配置参数能让同一套遍历逻辑复用于国际象棋、四子棋等不同棋类的攻击范围检查例如本块中的 Challenge 243: Rook Attack 与 Challenge 217: Captured Chess Pieces 都涉及沿方向向量的棋盘扫描。掌握方向向量 边界剪枝 坐标规范化这一组合拳你就拥有了解决一大类棋盘/网格类算法题的通用的底层能力。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表