我试图在O(n)复杂性中执行以下任务:
Given an array [|x_1,x_2,...,x_n|] return the biggest s such that there exists
a segment of length s - [|x_i,x_(i+1),...,x_j|], in which the minimum value
is greater or equal (j-i+1).
更新: 这里有一个不正确的O(n)解决方案,我设法编写了正确的(至少我认为是这样),简单的O(n * log n)。这是:
let segment a =
let n = Array.length a in
let pointer_1 = ref 0 in
let pointer_2 = ref 0 in
let q = ref (put empty_queue (a.(0), 0)) in
let best = ref 0 in
begin
while (!pointer_2 < n) do
let size = (!pointer_2 - !pointer_1 + 1) in
q := put !q (a.(!pointer_2), !pointer_2);
let ((lmin, pmin), qright) = getmin !q in
if (size > lmin) then begin
best := max !best (size-1);
pointer_1 := pmin + 1;
pointer_2 := !pointer_2 + 1;
q := qright
end
else
begin
best := max !best (size);
pointer_2 := !pointer_2 + 1
end
done;
!best
end;;
其中q是对优先级队列的引用,该优先级队列首先返回最小元素。那么,这可以用O(n)以简单的方式完成吗?
答案 0 :(得分:1)
这是一个不错的小谜题!这是一个基于双端队列的解决方案。
我的算法与你的算法大致相同,只是我使用dequeue来跟踪最小值:队列包含一个递增的值序列:第一个是当前间隔的最小值,第二个是在第一个最小值之后开始的子间隔的最小值,依此类推。
当我使左指针超过最小值时,我从队列的左侧移除此值,队列的其余部分自然以剩余间隔的最小值开始。
当我前进右指针时,我会检查队列右侧的新值。如果新值较小,我会删除最右边的条目,然后再次检查剩余的队列。删除所有较大的值后,我可以在出列结束时添加新值。
当在数组中找到非正数值时,我必须添加一个特殊情况:同时推进两个指针。
注意:我使用(并返回)半开放区间,因为它们比封闭区间更不容易出错。
此代码仅经过轻微测试。要表明它是O(n),请注意每个值在出队时最多输入一次。
let segment a =
let len = Array.length a in
let p1 = ref 0 in
let p2 = ref 0 in
let bestlen = ref 0 in
let best = ref [(0,0)] in
let q = Dq.make len in
while !p2 < len do
let l = !p2 - !p1 in
if (Dq.is_empty q || Dq.peek_left q > l) && a.(!p2) > l
then begin
while not (Dq.is_empty q) && Dq.peek_right q > a.(!p2) do
Dq.pop_right q;
done;
Dq.push_right q a.(!p2);
incr p2;
end else if !p1 < !p2 then begin
if a.(!p1) = Dq.peek_left q then Dq.pop_left q;
incr p1;
end else begin
assert (Dq.is_empty q);
incr p1;
incr p2;
end;
let l = !p2 - !p1 in
if l > !bestlen then begin
bestlen := l;
best := [];
end;
if l = !bestlen then best := (!p1, !p2) :: !best;
done;
!best
;;
这是一个双端队列的简单实现,而不是特定于上面的代码:
module Dq = struct
type t = {
mutable hd : int;
data : int array;
mutable tl : int;
}
let make len = {
hd = 0;
data = Array.make len 0;
tl = 0;
};;
let push_right q x =
assert (q.tl < Array.length q.data);
q.data.(q.tl) <- x;
q.tl <- q.tl + 1;
;;
let is_empty q = q.tl = q.hd;;
let peek_right q =
assert (q.hd < q.tl);
q.data.(q.tl - 1)
;;
let peek_left q =
assert (q.hd < q.tl);
q.data.(q.hd)
;;
let pop_left q =
assert (q.hd < q.tl);
q.hd <- q.hd + 1;
;;
let pop_right q =
assert (q.hd < q.tl);
q.tl <- q.tl - 1;
;;
end