您必须扫描整个阵列,这需要O (n) 时间。一个简单的证明是,当您尝试 [2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, ..., 2, 1, 2] 时,一半的值将是局部最大值,因此任何检查这一点的算法都将至少采用 n / 2 操作(某种形式)。
这是一个相当简单的版本,它报告所有峰值,对第一个和最后一个索引进行单独处理,对其余索引进行通用处理。它假设您只想要真正的峰值而不是高原。如果您想要高原指数,则必须将一些 < / > 标志替换为 <= / >= 标志。
const localMaxima = (ns) => [
... (ns [0] > ns [1] ? [0] : []),
... Array .from ({length: ns.length - 1}, ((_, i) => i + 1))
.filter ((i) => ns [i - 1] < ns [i] && ns [i + 1] < ns [i]),
... (ns [ns .length - 1] > ns [ns .length - 2] ? [ns .length - 1] : [])
]
console .log (localMaxima ([1, 2, 1, 3, 5, 6, 4])) //=> [1, 5]
// ^ ^
console .log (localMaxima ([8, 5, 7, 5, 3, 0, 9])) //=> [0, 2, 6]
// ^ ^ ^
console .log (localMaxima ([2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2])) //=> [0, 2, 4, 6, 8, 10]
// ^ ^ ^ ^ ^ ^
.as-console-wrapper {max-height: 100% !important; top: 0}
请注意,第一个示例返回 [1, 5]。问题中的[2, 5] 只是一个错字吗?
更新
要求使此“更具可读性”的评论。
我绝对应该做一件事。我在这里内联了一个range 函数。没有理由这样做。使用单独的函数时,它更具可读性。 (range 是一个返回整数范围的简单函数。例如range (3, 12) //=> [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]。)
使用它会导致对我来说是一个可读性很强的版本:
const range = (lo, hi) => Array .from ({length: hi - lo + 1}, (_, i) => lo + i)
const localMaxima = (ns) => [
... (ns [0] > ns [1] ? [0] : []),
... range (1, ns.length - 2) .filter ((i) => ns [i - 1] < ns [i] && ns [i + 1] < ns [i]),
... (ns [ns .length - 1] > ns [ns .length - 2] ? [ns .length - 1] : [])
]
但是,我不认为这是被问到的。我认为请求的代码可能看起来像这样:
const localMaxima = (ns) => {
const includeStart = ns [0] > ns [1]
const includeEnd = ns [ns.length - 1] > ns [ns .length -2]
const intermediates = range (0, ns .length - 2)
.filter ((i) => ns [i - 1] < ns [i] && ns [i + 1] < ns [i])
if (includeStart) intermediates .unshift (0)
if (includeEnd) intermediates .push (ns .length - 1)
return intermediates
}
但我一点也不喜欢。我尽我所能处理不可变数据,unshift 和 push 调用对我来说太可怕了。
可能我最接近那个并且仍然照镜子的方式是
const localMaxima = (ns) => {
const includeStart = ns [0] > ns [1]
const includeEnd = ns [ns.length - 1] > ns [ns .length -2]
const intermediates = range (0, ns .length - 2)
.filter ((i) => ns [i - 1] < ns [i] && ns [i + 1] < ns [i])
const beginning = includeStart ? [0] : []
const ending = includeEnd ? [ns .length - 1] : []
return beginning .concat (intermediates) .concat (ending)
}
我也不太喜欢这些一次性变量赋值,但在像 JS 这样的语言中,它们有时很难避免。至少在这里他们仍然是const。我会避免替换这个:
const beginning = includeStart ? [0] : []
使用这种替代方法
let beginning
if (includeStart) {
beginning = [0]
} else {
beginning = []
}
因此,如果您只是不喜欢条件运算符(三元组),我们可能永远不会看到一致的意见!
我非常愿意在任何一天选择我的帖子-range 重构这些版本。但可读性在旁观者眼中,也许最后一个会让一些人高兴。