尧图精选

type-challenges 中等难度题解:用 FindEles 找出数组中出现且仅出现一次的元素

🕒 发布时间:2026/10/2 2:10:12 📁 来源:尧图网络
示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载本题属于 type-challenges 中等medium难度系列编号 9898题名为「找出目标数组中只出现过一次的元素Appear only once」。其核心任务是在 TypeScript 类型系统层面实现一个工具类型FindElesT输入一个元素类型组成的数组元组输出一个保留原始顺序的新元组其中只包含在原数组中恰好出现一次的元素。例如输入[1, 2, 2, 3, 3, 4, 5, 6, 6, 6]应输出[1, 4, 5]。读完本文你将掌握基于「条件类型 递归元组拆解」实现出现次数统计的通用套路并能将其复用到去重、剔除重复项等同类数组问题中。题目与测试用例分析题目来源与定位该题由 X.Q. ChenGitHub 用户 brenner8023difficulty: medium title: Appear only once author: github: brenner8023 name: X.Q. Chen在 README.zh-CN.md 中题目要求一句话即可概括找出目标数组中只出现过一次的元素示例为输入[1,2,2,3,3,4,5,6,6,6]输出[1,4,5]。此外也可参考英文版 README.md。测试用例逐条解读题目的判题逻辑写在 test-cases.ts 中共有 5 个用例import type { Equal, Expect } from type-challenges/utils type cases [ ExpectEqualFindEles[1, 2, 2, 3, 3, 4, 5, 6, 6, 6], [1, 4, 5], ExpectEqualFindEles[2, 2, 3, 3, 6, 6, 6], [], ExpectEqualFindEles[1, 2, 3], [1, 2, 3], ExpectEqualFindEles[1, 2, number], [1, 2, number], ExpectEqualFindEles[1, 2, number, number], [1, 2], ]逐条含义[1, 2, 2, 3, 3, 4, 5, 6, 6, 6]→[1, 4, 5]2、3出现两次6出现三次均被剔除1、4、5各出现一次保留且顺序不变。[2, 2, 3, 3, 6, 6, 6]→[]所有元素都重复结果为空元组。[1, 2, 3]→[1, 2, 3]元素全部唯一原样输出。[1, 2, number]→[1, 2, number]注意这里的number是类型而非字面量3这样的具体数字[1, 2, number]中number只出现一次所以保留。[1, 2, number, number]→[1, 2]number作为类型出现了两次被剔除。其中EqualX, Y是仓库 utils/index.d.ts 提供的严格类型相等判断工具export type EqualX, Y (T() T extends X ? 1 : 2) extends (T() T extends Y ? 1 : 2) ? true : false它借助函数类型参数的逆变位置推断来区分any、never等边缘情况比朴素的X extends Y ? ... : ...更严格——这也是为什么题目用例 4、5 敢于直接用number类型参与比较。初始模板仓库给出的 template.ts 非常精简只需实现这一个类型type FindElesT extends any[] any模板已经约束了输入必须是数组/元组extends any[]我们只需把any替换成真正的实现。解题思路核心难点与可行方案难点一统计出现次数没有内置工具TypeScript 类型系统并没有类似count、filter的现成内置类型。要实现「只出现一次」必须先实现一个「统计某个元素在元组中出现次数」的能力。这只能靠递归 条件类型手工构造。难点二类型相等 vs 字面量比较在类型层面判断「两个元素相同」不能简单用A extends B。对于number extends 1这样的判断number宽类型extends1窄字面量为false但反过来1 extends number为true会得到不对称的结果。因此统计次数时需要一个对称的相等判断。可以参考仓库中Equal的实现思路或在计数时使用双向包含判断A extends B ? (B extends A ? true : false) : false。测试用例 4、5 中出现的number类型恰恰就是为了验证这一点。难点三结果必须保持原始顺序题目要求输出保留元素在元组中的原始相对顺序[1, 4, 5]而非[4, 1, 5]因此不能使用会把顺序打乱的联合类型展开必须按元组从左到右递归拼接。参考实现基于递归与次数统计的完整解法下面给出一个可直接通过全部 5 个测试用例的实现type EqualA, B (T() T extends A ? 1 : 2) extends (T() T extends B ? 1 : 2) ? true : false // 统计元素 U 在元组 T 中出现的次数 type CountT extends any[], U, C extends any[] [] T extends [infer F, ...infer Rest] ? CountRest, U, EqualF, U extends true ? [...C, 1] : C : C[length] // 取出元组中只出现一次的元素 type FindElesT extends any[], R extends any[] [] T extends [infer F, ...infer Rest] ? CountT, F extends 1 ? FindElesRest, [...R, F] : FindElesRest, R : R逐段拆解1.Equal严格相等判断与仓库 utils/index.d.ts 中的Equal一致通过两个函数类型在推断时的行为差异来判断A、B是否完全相同能正确处理number、any等宽类型与字面量之间的比较。2.Count计数工具CountT, U递归遍历元组T每次取出首元素F和剩余部分Rest[infer F, ...infer Rest]用EqualF, U判断是否命中命中就往结果元组C里追加一个1最后用C[length]得到总次数。这个「用元组长度当计数器」的手法在类型挑战中非常常见——例如 07544-medium-construct-tuple、04425-medium-greater-than 等题目都依赖它做数值运算。3.FindEles主类型同样从左到右递归对当前首元素F调用CountT, F若结果为1则保留到结果R否则丢弃递归处理Rest元组耗尽时返回R。因为遍历顺序即原始顺序所以输出天然保持原始顺序。验证各用例FindEles[1, 2, 2, 3, 3, 4, 5, 6, 6, 6]1→次数 1 保留2→次数 2 丢弃3→次数 2 丢弃4、5→次数 1 保留6→次数 3 丢弃得[1, 4, 5]✔FindEles[2, 2, 3, 3, 6, 6, 6]全部重复得[]✔FindEles[1, 2, 3]全部唯一得[1, 2, 3]✔FindEles[1, 2, number]number是宽类型只出现一次得[1, 2, number]✔FindEles[1, 2, number, number]Equalnumber, number为true计数为 2丢弃得[1, 2]✔另一种思路全局去重法除了「先统计次数再过滤」还有一条更符合直觉的路径先剔除重复再比较原数组与去重数组。思路如下递归实现RemoveDupT参考仓库中 05360-medium-unique 题目把元组去重对去重结果中的每个元素统计其在原数组中的出现次数只保留次数为 1 的元素。两种方案本质等价区别在于全局去重法先缩小了待统计的候选集当重复元素很多时递归分支更少而「逐元素计数」写法更直观、更容易迁移到其他数组问题。实际作答时选择自己更易推导、更不易出错的一种即可。与仓库同类题目的横向对比「找出只出现一次的元素」可以视为一系列数组类类型挑战的交叉应用仓库中有多道题目与之共享核心技巧相关题目复用/涉及的核心能力05153-medium-indexof判断元素在元组中的索引同样要处理number、any等类型相等的边界05117-medium-without从元组中剔除指定元素需要递归拼接05360-medium-unique元组去重需要判断是否已存在05310-medium-join递归遍历元组并逐个拼接元素09989-medium-tong-ji-shu-zu-zhong-de-yuan-su-ge-shu统计数组中每个元素的出现次数本题的核心子问题例如 05153-medium-indexof 的测试用例中出现了IndexOf[string, 1, number, a, any], any→4、IndexOf[any, 1], 1→1这样的断言说明处理any、宽类型时的相等判断同样必须足够严谨与本题的用例 4、5 是同一类边界问题。本地运行与验证如果你想在本地动手验证这个实现仓库根目录的 package.json 使用 pnpm 管理依赖packageManager: pnpm8.12.1并内置了type-challenges/utilsworkspace:*。可以按以下方式尝试将上述FindEles实现填入 template.ts用 TypeScript 编译器对 test-cases.ts 做类型检查例如pnpm install pnpm exec tsc --noEmit questions/09898-medium-zhao-chu-mu-biao-shu-zu-zhong-zhi-chu-xian-guo-yi-ci-de-yuan-su/test-cases.ts若所有ExpectEqual...断言编译通过不产生类型错误说明实现正确。也可以直接访问题目的在线挑战页面tsch.js.org/9898在浏览器中即时验证。此外更简便的方式是利用仓库中CountElementNumberToObject这类已存在的计数题作为参照先攻克子问题再回来完成本题。总结FindEles是 type-challenges 中等难度中非常典型的「递归 统计」型题目它教会我们三件事用元组长度当计数器C[length]是类型系统里做数值比较与统计的基础设施严格相等判断不可替代处理number、any等宽类型时朴素extends会因非对称性给出错误结论Equal风格的实现才能通过测试递归拼接保序按[infer F, ...infer Rest]逐项拆解并拼接结果才能保证输出保持原始顺序。掌握这套「计数 → 过滤」的模式后Unique、Without、IndexOf等大量数组类类型挑战都可以举一反三。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐Backstage v1.8.0 发布全解析事件系统落地、破坏性变更清单与升级迁移指南Backstage v1.8.0 发布全解析事件系统落地、破坏性变更清单与升级迁移指南 本篇技术指南以 Backstage 开源仓库中的 v1.8.0 变更日示例工程freeCodeCamp 每日编程挑战解析Challenge 121 Most Frequent——用 JavaScript 找出数组中出现次数最多的元素freeCodeCamp 每日编程挑战解析Challenge 121 Most Frequent——用 JavaScript 找出数组中出现次数最多的元素前端后端教育Swift Algorithm Club用两次二分查找在有序数组中高效统计元素出现次数Count OccurrencesSwift Algorithm Club用两次二分查找在有序数组中高效统计元素出现次数Count Occurrences 导读 在 Swift 开发中统示例工程教程上一篇逆向工程实战如何用二进制补丁技术破解微信QQ防撤回限制下一篇ESP32 Arduino终极开发指南从零到物联网的完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →