在算法题中,Node.js 程序通常需要一次性读取标准输入,再根据题目格式解析。相比在业务代码中逐次等待用户输入,在线评测更适合使用 fs.readFileSync(0, 'utf8'):写法短、速度快,也更容易处理多行数据。
1. 最常用的写法:按空白字符切分
当输入中的数据都是数字,并且空格和换行只用于分隔数据时,可以直接把整份输入拆成 token:
const fs = require('fs')
const input = fs.readFileSync(0, 'utf8').trim().split(/\s+/)
let index = 0
const n = Number(input[index++])
const nums = Array.from({ length: n }, () => Number(input[index++]))
console.log(nums.reduce((sum, num) => sum + num, 0))
例如输入:
5
1 2 3 4 5
输出为:
15
这里的 /\s+/ 可以同时匹配空格、换行和制表符,因此不必关心数据具体分布在哪一行。
2. 需要保留行结构时
如果题目中的字符串可能包含空格,或者每一行有独立含义,就应该先按行切分:
const fs = require('fs')
const lines = fs.readFileSync(0, 'utf8').trimEnd().split(/\r?\n/)
const n = Number(lines[0])
const text = lines[1]
console.log(n)
console.log(text)
使用 /\r?\n/ 可以兼容 Linux 的 \n 和 Windows 的 \r\n。
3. 一个完整示例
假设输入格式如下:
- 第一行包含
N、M、P - 第二行包含
N个整数 - 第三行包含
M个整数 - 计算所有
min(a[i] + b[j], P)的和
可以这样写:
const fs = require('fs')
const tokens = fs.readFileSync(0, 'utf8').trim().split(/\s+/)
let index = 0
const n = Number(tokens[index++])
const m = Number(tokens[index++])
const p = Number(tokens[index++])
const a = Array.from({ length: n }, () => Number(tokens[index++]))
const b = Array.from({ length: m }, () => Number(tokens[index++]))
let result = 0
for (let i = 0; i < n; i++) {
for (let j = 0; j < m; j++) {
result += Math.min(a[i] + b[j], p)
}
}
console.log(result)
原始输入经过 split 后得到的都是字符串。一定要先用 Number 转换,否则:
'2' + '3' // '23',不是 5
这也是 Node.js 算法题中非常常见的错误。
4. 使用 readline
readline 适合希望逐行处理输入的场景:
const readline = require('readline')
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
const lines = []
rl.on('line', (line) => {
lines.push(line)
})
rl.on('close', () => {
const [n, m] = lines[0].split(/\s+/).map(Number)
const a = lines[1].split(/\s+/).map(Number)
const b = lines[2].split(/\s+/).map(Number)
console.log({ n, m, a, b })
})
不过对于大多数在线评测题,一次性读取输入通常更简洁。因此,没有流式处理需求时,优先使用 fs.readFileSync(0, 'utf8') 即可。
5. 常见问题
输入末尾有换行怎么办?
通常可以先调用 trim(),再切分:
const tokens = input.trim().split(/\s+/)
但如果题目允许空字符串作为有效输入,就不要直接使用 trim(),而应根据题意处理。
为什么不要只写 split(' ')?
split(' ') 只能处理单个空格,遇到多个空格或换行时容易产生空字符串。算法题中更稳妥的写法是:
input.trim().split(/\s+/)
数值很大时怎么办?
JavaScript 的 Number 不能精确表示所有大整数。若数值可能超过 Number.MAX_SAFE_INTEGER,应使用 BigInt:
const value = BigInt(token)
console.log(value.toString())
注意,BigInt 不能直接与 Number 混合运算。
输出很多内容怎么办?
频繁调用 console.log 可能影响性能。可以先把结果放入数组,最后一次性输出:
const output = []
output.push('first result')
output.push('second result')
console.log(output.join('\n'))
总结
Node.js 算法题的输入处理可以记住两个模板:
// 按空白字符读取
const tokens = require('fs').readFileSync(0, 'utf8').trim().split(/\s+/)
// 按行读取
const lines = require('fs').readFileSync(0, 'utf8').trimEnd().split(/\r?\n/)
只要根据题目选择正确的解析方式,并记得把数字字符串转换为 Number 或 BigInt,就能覆盖绝大多数在线评测场景。