给定(未排序的)数组S和某个整数k,找到对i,j的数量,使得S [i ... j]的范围<1。 ķ。范围是max(S [i ... j]) - min(S [i ... j])。
我在一次采访中收到了这个问题,并且在排序S之后只能得到一个O(nlogn)解决方案。但是,我被告知有一个O(n)解决方案。有什么想法吗?
答案 0 :(得分:2)
从i开始,j = 0,我们可以迭代j,跟踪最小值和最大值。当通过提高max使范围变为&gt; = k时,我们需要找到新的i,其中min(s [i..j])&gt; max - k(模拟另一种情况)。
显然我们可以通过从j向后搜索找到这个索引,但是我们需要从i向前搜索以保持运行时线性(例如:1 2 3 4 5 6,其中k = 4将回溯几个元素,每个元素都向后搜索步骤,而前向搜索确保每个我只考虑一次)。
为了能够这样做,我们可以保留两个具有单调上升/下降数组值的索引列表。
因此,当我们在“外部”循环中迭代j时,我们从升序列表中删除值大于s [j]的所有索引,然后追加j。模拟降序列表。由于我们总是附加一个元素并且删除的数量不能超过添加的数量,因此该部分应该仍然是线性的。
在“内部”循环中搜索具有足够接近新的最小/最大值的新i时,我们从列表的前面删除访问过的元素。
修改:代码
import java.util.LinkedList;
public class Ranges {
public static int countRanges(int[] s, int k) {
int i = 0;
int min = s[0];
int max = s[0];
LinkedList<Integer> ascending = new LinkedList();
ascending.add(0);
LinkedList<Integer> descending = new LinkedList();
descending.add(0);
System.out.println("[0...0]");
int count = 1;
for (int j = 1; j < s.length; j++) {
int value = s[j];
while (!ascending.isEmpty() && s[ascending.getLast()] > value) {
ascending.removeLast();
}
ascending.add(j);
while (!descending.isEmpty() && s[descending.getLast()] < value) {
descending.removeLast();
}
descending.add(j);
if (s[j] > max) {
max = s[j];
if (max - min >= k) {
while(max - s[ascending.getFirst()] >= k) {
ascending.removeFirst();
}
i = ascending.getFirst();
min = s[i];
while (descending.getFirst() < i) {
descending.removeFirst();
}
}
} else if (s[j] < min) {
min = s[j];
if (max - min >= k) {
while(s[descending.getFirst()] - min >= k) {
descending.removeFirst();
}
i = descending.getFirst();
max = s[i];
while (ascending.getFirst() < i) {
ascending.removeFirst();
}
}
}
System.out.println("[" + i + "..." + j + "]");
count += j - i + 1; // New subarrays involving j
}
return count;
}
public static void main(String[] args) {
final int[] s = new int[] {1, 7, 2, 3, 4, 1, 2, 5, 6};
final int k = 3;
System.out.println("count: " + countRanges(s, k));
}
}
工作说明:https://i.imgur.com/G2FlSoc.jpg O:)
答案 1 :(得分:1)
您可以使用经典双指针技术的变体来完成此操作。区别在于您需要跟踪其值在k范围内的范围的起始索引,以及该范围内的最小值和最大值,以便我们知道当前值何时超出范围(值为起始指数不一定是最小值或最大值)。需要注意的另一点是,当当前值超出范围时,新的起始索引不一定由最小值或最大值表示,而是必须通过从当前索引开始向后迭代来搜索。
正如KSwama指出的那样,有可能不得不多次向后迭代相同的元素,因此时间复杂度不是线性的。我认为最坏的情况意味着迭代大多数元素高达k次,因此复杂度可能类似于O(n×k)。
将起始索引设置为0,将min和max设置为S [0]。然后,迭代输入,对于每个值S [i],如果需要,将min或max调整为S [i]。当你得到一个值S [i],例如大于或等于min + k,将min和max设置为S [i],然后从i向后迭代(同时调整min),直到找到小于或等于max-k的值S [j];然后j + 1成为新的起始索引。对于每个值S [i],它添加到总数的对数是i-start。
示例:
S = [1,3,-1,2,5,3,6,2,4,0]
k = 5
我们从:
开始i S[p] start min max pairs
0 1 0 1 1 -
1 3 0 1 3 1
2 -1 0 -1 3 2
3 2 0 -1 3 3
4 5
此时我们得到一个大于min + k的值。因此我们将当前值S [i]设置为新的min和max,并向后迭代(同时更新min),直到我们找到小于或等于max-k的第一个值,即S [2] = - 1;所以S [3]成为新的最小值,3是新的起始指数:
i S[p] start min max pairs
4 5 3 2 5 1
5 3 3 2 5 2
6 6 3 2 6 3
7 2 3 2 6 4
8 4 3 2 6 5
9 0
此时我们得到一个小于或等于max-k的值。所以我们将min和max设置为0,然后向后迭代(同时更新max),直到达到S [6],大于或等于min + k,因此7成为新的起始索引;请注意,新的最大值不是S [start],而是我们在途中传递的值4。
i S[p] start min max pairs
9 0 7 0 4 2
对的总数是23(如果我已经理解了它们被正确计算的方式)。
如果你想降低O(n)的复杂性,有很多选择,但Stefan Haustein的答案可能就是这样。