小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
说明:文章部分内容及图片出自网络,如有侵权请与我本人联系(主页有公众号:小攻城狮学前端)
作者:小只前端攻城狮、 主页:小只前端攻城狮的主页、 来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
题目描述
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
分析:题意求max((i-j)*min(a[i],a[j])),i > j。
我们发现a[j]>=a[i]的时候,min值取a[i],所以j越小越好,满足a[j]>=a[i]的那些j可以构成一个答案值ans1,ans1不一定等于最终答案。那么a[j]<a[i]的情况怎么办?把数组倒序,再求一遍!把这个答案记为ans2,则max(ans1,ans2)为所求。
所以我们只需要求最小的j,满足a[j]>=a[i]。
解法1:线段树。
线段树维护区间max,如果左侧区间max>=a[i],就递归左侧,否则递归右侧。
let ls = x => (x << 1), rs = x => (x << 1 | 1)
let nd = new Array(200010).fill(0)
function build(x, l, r, a) {
if (l === r) {
nd[x] = a[l - 1]
return
}
let mid = (l + r) >> 1
build(ls(x), l, mid, a)
build(rs(x), mid + 1, r, a)
nd[x] = Math.max(nd[ls(x)], nd[rs(x)])
}
function query(x, l, r, v) {
if (l === r) return l
let mid = (l + r) >> 1
if (nd[ls(x)] >= v) return query(ls(x), l, mid, v)
return query(rs(x), mid + 1, r, v)
}
var maxArea = function(a) {
const n = a.length
let solve = (a) => {
build(1, 1, n, a)
let ans = 0
for (let i = 0; i < n; ++i) {
let idx = query(1, 1, n, a[i]) - 1
if (idx > i) continue
ans = Math.max(ans, a[i] * (i - idx))
}
return ans
}
let ans = solve(a)
a.reverse()
ans = Math.max(ans, solve(a))
return ans
};
解法2:排序+后缀min
把数值和下标绑在一起,按照数值升序排序,如果数值相同则随意排。记为b数组。
找到第一个大于等于a[i]的下标idx,则
min(b[idx~n-1][1])(0-indexed)为所求。所以用到了后缀min。
var maxArea = function(a) {
const n = a.length
let lower_bound = (a, x) => {
let l = 0, r = a.length
while (l < r) {
let mid = (l + r) >> 1
if (a[mid] >= x) r = mid
else l = mid + 1
}
return l
}
let solve = (a) => {
let b = a.map((v, i) => [v, i])
b.sort((x, y) => x[0] ^ y[0] ? x[0] - y[0] : x[1] - y[1])
let val = b.map(v => v[0]), mn = b.map(v => v[1])
for (let i = n - 2; ~i; --i) mn[i] = Math.min(mn[i], mn[i + 1])
let ans = 0
for (let i = 0; i < n; ++i) {
let idx = lower_bound(val, a[i])
ans = Math.max(ans, a[i] * (i - mn[idx]))
}
return ans
}
let ans = solve(a)
a.reverse()
ans = Math.max(ans, solve(a))
return ans
};
感谢阅读,希望能对你有所帮助,文章若有错误或者侵权,可以在评论区留言或在我的主页添加公众号联系我。
写作不易,如果觉得不错,可以「点赞」+「评论」 谢谢支持❤