首页
/ freeCodeCamp 日常编程挑战解析:Challenge 151「Sorted Array?」数组排序状态判定

freeCodeCamp 日常编程挑战解析:Challenge 151「Sorted Array?」数组排序状态判定

2026-09-08 14:38:34作者:虞亚竹Luna

本文围绕 freeCodeCamp 开源课程中 daily-coding-challenges-javascript 块的 Challenge 151:Sorted Array?(对应挑战文档位于 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/69373793f5a867f769cde136.md),完整剖析题目要求、六个官方测试断言、参考解法的时间/空间复杂度,并结合仓库源码说明这类挑战从 Markdown 源文件到前端评测、后端 API 分发的完整运行链路,帮助你既会解这道题,又理解它在 freeCodeCamp 代码库中的真实位置。

题目要求:判断数组的排序状态

给定一个数字数组,需要判断其排列属于以下哪种情况,并返回对应的字符串:

数组状态 含义 返回值
升序(Ascending) 从最低到最高排列 "Ascending"
降序(Descending) 从最高到最低排列 "Descending"
未排序 既非升序也非降序 "Not sorted"

题面要求实现一个 isSorted(arr) 函数。所谓“升序”并不要求严格递增——从后续参考解法可以看出,只要任意相邻两元素没有出现“后者小于前者”,数组即判定为升序;同理“降序”要求任意相邻两元素没有出现“后者大于前者”。这一宽松定义天然覆盖了包含相等元素的边界场景。

六个官方测试断言:逐步锁定行为

题目通过 --hints-- 段落给出了 6 组可执行的验证断言(编写格式为 Node 环境下的 assert.equal):

assert.equal(isSorted([1, 2, 3, 4, 5]), "Ascending");
assert.equal(isSorted([10, 8, 6, 4, 2]), "Descending");
assert.equal(isSorted([1, 3, 2, 4, 5]), "Not sorted");
assert.equal(isSorted([3.14, 2.71, 1.61, 0.57]), "Descending");
assert.equal(isSorted([12.3, 23.4, 34.5, 45.6, 56.7, 67.8, 78.9]), "Ascending");
assert.equal(isSorted([0.4, 0.5, 0.3]), "Not sorted");

归纳起来,测试覆盖了四类关键场景:

  1. 正整数升序[1, 2, 3, 4, 5]):最简单的连续升序用例;
  2. 整数降序[10, 8, 6, 4, 2]):覆盖偶数步长降序;
  3. 乱序数组[1, 3, 2, 4, 5][0.4, 0.5, 0.3]):数组绝大部分有序、仅个别相邻元素倒序时也必须正确返回 "Not sorted",这是最容易写错的地方——例如只比较“首尾元素”或用一次排序后逐一比对坐标,都会在此类用例上失败;
  4. 浮点数序列:既验证了浮点升序(3.14 → 78.9 方向),也验证浮点降序,说明实现不能依赖取整或类型转换。

起步代码与参考解法

题目在 --seed-contents-- 段落提供如下函数骨架,要求你只补全函数体:

function isSorted(arr) {

  return arr;
}

官方在 --solutions-- 段落给出的参考实现采用双布尔标志单次遍历策略:

function isSorted(arr) {
  let ascending = true;
  let descending = true;

  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < arr[i - 1]) ascending = false;
    if (arr[i] > arr[i - 1]) descending = false;
  }

  if (ascending) return "Ascending";
  if (descending) return "Descending";
  return "Not sorted";
}

逐步拆解

  1. 初始化两个标志位:假设数组既是升序又是降序,都设为 true。对所有长度为 0 或 1 的数组(循环体不执行),最终会命中 if (ascending) 分支返回 "Ascending",符合题面语义;
  2. 逐对比较:从 i = 1 开始,每轮把 arr[i] 与前一元素 arr[i - 1] 比较:
    • 若出现 arr[i] < arr[i - 1],说明升序被破坏,ascending = false
    • 若出现 arr[i] > arr[i - 1],说明降序被破坏,descending = false
    • 相邻元素相等时,两个标志均保持不变,因此相等元素不构成“未排序”证据,这与题面宽松的排序定义一致;
  3. 收尾判定:遍历结束后检查标志位。注意两个 if 的顺序:在单元素或空数组(两标志仍为真)场景下,先返回 "Ascending"。其余场景三者互斥,判定正确。

复杂度分析

  • 时间复杂度 O(n):只需从第 2 个元素扫描到末尾一次,比较次数为 n - 1
  • 空间复杂度 O(1):仅使用两个布尔变量,不复制数组、不依赖排序或额外数据结构。

作为对比,若先对数组拷贝排序再与原始数组比对,需要 O(n log n) 时间与 O(n) 额外空间,且还要分别比对升序与降序两次;若借助 every((v, i) => i === 0 || v >= arr[i - 1]) 等函数式写法,可以更简洁但每调用一次仍是 O(n) 的线性遍历。参考解法通过一次遍历同时取消两个标志,是本题在时间与空间上都最优的经典实现。

深入仓库:这道题如何嵌入 freeCodeCamp 课程体系

只看解题还不够。Challenge 151 的实际价值在于它演示了 freeCodeCamp 如何把一道算法题组织成可被前端渲染、后台校验、并持久化分发的课程资产。

1. 挑战源文件的规范化结构

该挑战文档本体(69373793f5a867f769cde136.md)遵循 freeCodeCamp 挑战的统一 Markdown 规范,包含:

  • YAML frontmatterid(24 位十六进制唯一标识)、title(人类可读标题)、challengeType(值为 28,标识“每日 JavaScript 编码挑战”类型,与 show-daily-coding-challenge.tsx 中 JS 侧使用 challengeType: 28 的取值相互印证)、dashedName(用于生成 URL 的 kebab-case 名称);
  • # --description--:对学习者的题目描述与返回值约定;
  • # --hints--:以 assert.equal 形式给出的自动化验收断言;
  • # --seed-- / ## --seed-contents--:提供给学习者的初始代码;
  • # --solutions--:官方参考实现。

整份文件以“描述 + 测试 + 种子代码 + 解法”四段式驱动,仓库中同目录其余 300+ 个挑战 Markdown 均采用这一模式,属于可机器解析的标准化课程数据。

2. 从 Markdown 到在线评测的流转链路

这类 JS 挑战会经由两条并行的落地路径呈现给用户:

  • 每日挑战(Daily Coding Challenge)路径:挑战数据通过 tools/daily-challenges 下的播种脚本写入数据库。种子脚本会先启动带“Dev Playground”区块的客户端,通过 GraphQL 拉取挑战内容(数据结构见 tools/daily-challenges/types.ts,包含 descriptiontests[{testString, text}]challengeFiles 等字段),再执行 pnpm seed-daily-challenges 写入 DailyCodingChallenges 集合。前端页面 show-daily-coding-challenge.tsx 会调用后端接口获取当天题目;
  • API 分发路径:挑战由 freeCodeCamp API 统一对外提供。路由注册在 api/src/daily-coding-challenge/routes/daily-coding-challenge.ts,提供 6 个公开 GET 端点:/daily-coding-challenge/date/:date(按 YYYY-MM-DD 精确取题)、/daily-coding-challenge/day/:day(按 MM-DD 取题)、/daily-coding-challenge/today(取当日题,以美国中部时间 UTC 午夜为界)、/daily-coding-challenge/month/:month(按 YYYY-MM 列出某月题目)、/daily-coding-challenge/all(列出全部)、/daily-coding-challenge/newest(返回最近一期日期)。代码注释明确指出最后一个挑战日期为 2026-08-10,且 dateday 路由会拒绝返回晚于当天(美国中部时间)的“未来题目”,保证挑战逐日解锁的节奏。

3. 返回数据的结构与校验

接口返回的单题 JSON 形状定义于 api/src/daily-coding-challenge/schemas/daily-coding-challenge.ts:包含 id(ObjectId)、date(ISO 日期时间)、challengeNumbertitledescription,以及 javascriptpython 两套语言数据;每套语言数据又含 teststext + testString,其中 testString 正是 Challenge 151 中 assert.equal(...) 这类评测代码)与 challengeFilesfileKey + contents)。这解释了为什么同一道 isSorted 题在仓库中既存在于 JavaScript 日常挑战块、也配套 Python 变体——daily-coding-challenge-validator.tsclient/src/utils/daily-coding-challenge-validator.ts)用 Joi 对来自数据库的整份挑战结构做运行时校验,确保 JS/Python 两套语言数据完整、测试与种子文件齐全后才进入前端渲染。

理解了这条链路,再看 Challenge 151 的 isSorted,就不仅是“写一个循环”,而是一个最小但完整的闭环:算法正确性由 --hints-- 中的 assert.equal 决定,种子代码决定起始环境,testString 注入评测器执行,返回字符串与断言逐字比对后判定通过

小结

Challenge 151「Sorted Array?」用一次线性遍历配合两个布尔标志,以 O(n) 时间、O(1) 空间完成数组升序/降序/无序三态判定,是数组遍历与状态折叠类题目的标准示范。同时,通过阅读 69373793f5a867f769cde136.md 以及同目录、API 路由、校验器与播种脚本等周边源码,可以完整还原 freeCodeCamp 中一道日常编程挑战从题目编写、数据播种到接口分发与浏览器评测的全过程,为深入理解该开源课程体系提供了可复用的研读路径。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.89 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
602
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
396
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
526