简体   繁体   English

范围小于 k 的子数组数

[英]Number of subarrays with range less than k

Given an (unsorted) array S and some integer k, find the number of pairs i,j such that the range of S[i...j] < k.给定一个(未排序的)数组 S 和一些 integer k,找出满足 S[i...j] < k 的对 i,j 的数量。 Where range is max(S[i...j]) - min(S[i...j]).其中范围是 max(S[i...j]) - min(S[i...j])。

I received this question in an interview and was only able to come up with a O(nlogn) solution after sorting S. However, I was told there is an O(n) solution.我在面试中收到这个问题,并且只能在对 S 进行排序后得出一个 O(nlogn) 的解决方案。但是,我被告知有一个 O(n) 的解决方案。 Any ideas?有任何想法吗?

Starting with i,j = 0, we could iterate j, keeping track of min and max. 从i开始,j = 0,我们可以迭代j,跟踪最小值和最大值。 When the range becomes >= k via raising max, we need to find a new i where min(s[i..j]) > max - k (analog for the other case). 当范围变得> = k时,通过提高最大值,我们需要找到一个新的i,其中min(s [i..j])> max - k(模拟另一种情况)。

Obviously we could find this index by searching backwards from j, but we need to search forward from i to keep the runtime linear (example: 1 2 3 4 5 6 with k = 4 would backtrack several elements with backwards search in each step, while forward search makes sure each i is only considered once). 显然我们可以通过从j向后搜索找到这个索引,但是我们需要从i向前搜索以保持运行时线性(例如:1 2 3 4 5 6,其中k = 4将在每个步骤中使用向后搜索回溯几个元素,而前向搜索确保每个i只被考虑一次)。

To be able to do so, we could keep two lists of indices with monotonously ascending / descending array values. 为了能够这样做,我们可以保留两个具有单调上升/下降数组值的索引列表。

So as we iterate j in the "outer" loop, we remove all indices with values bigger than s[j] from the ascending list and then append j. 因此,当我们在“外部”循环中迭代j时,我们从升序列表中删除值大于s [j]的所有索引,然后追加j。 Analog for the descending list. 模拟降序列表。 Since we always append one element and the number of removals can't exceed the number of additions, this part should still be linear. 由于我们总是附加一个元素并且删除的数量不能超过添加的数量,因此该部分应该仍然是线性的。

While searching a new i with a value that is sufficiently close to the new min/max in the "inner" loop, we remove the visited elements from the front of the lists. 在“内部”循环中搜索具有足够接近新的最小值/最大值的新i时,我们从列表的前面删除被访问的元素。

Edit : Code 编辑 :代码

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));
  }
}

Working notes: https://i.imgur.com/G2FlSoc.jpg O:) 工作说明: https//i.imgur.com/G2FlSoc.jpg O :)

You can do this with a variation of the classic 2-pointer technique. 您可以使用经典双指针技术的变体来实现此目的。 The difference is that you need to keep track of the start index of the range whose values fall within k, but also of the minimum and maximum value within this range, so that we know when the current value goes out of range (the value at the start index isn't necessarily the minimum or maximum). 区别在于您需要跟踪其值在k范围内的范围的起始索引,以及该范围内的最小值和最大值,以便我们知道当前值何时超出范围(值为起始指数不一定是最小值或最大值)。 Another thing to note is that when the current value goes out of range, the new start index isn't necessarily indicated by the minimum or maximum value, but has to be searched by iterating backwards starting from the current index. 需要注意的另一点是,当当前值超出范围时,新的起始索引不一定由最小值或最大值表示,而是必须通过从当前索引开始向后迭代来搜索。

As KSwama pointed out, there is a possibilitiy of having to iterate backwards over the same elements multiple times, so the time complexity will not be linear. 正如KSwama指出的那样,有可能不得不多次向后迭代相同的元素,因此时间复杂度将不是线性的。 I think the worst case would mean iterating over most elements up to k times, so the complexity is probably something like O(n×k). 我认为最坏的情况意味着迭代大多数元素高达k次,因此复杂度可能类似于O(n×k)。

Set the start index to 0, and min and max to S[0]. 将起始索引设置为0,将min和max设置为S [0]。 Then, iterate over the input and for each value S[i], adjust min or max to S[i] if necessary. 然后,迭代输入,对于每个值S [i],如果需要,将min或max调整为S [i]。 When you come to a value S[i] that is eg greater than or equal to min+k, set min and max to S[i], and then iterate backwards from i (while adjusting min) until you find a value S[j] that is less than or equal to max-k; 当你得到一个例如大于或等于min + k的值S [i]时,将min和max设置为S [i],然后从i向后迭代(同时调整min),直到找到值S [ j]小于或等于max-k; j+1 then becomes the new start index. 然后j + 1成为新的起始索引。 For each value S[i], the number of pairs it adds to the total is i-start. 对于每个值S [i],它添加到总数的对数是i-start。

Example: 例:

S = [1,3,-1,2,5,3,6,2,4,0]  
k = 5

We start with: 我们从:

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  

At this point we come to a value that is larger than min+k. 此时我们得到一个大于min + k的值。 So we set the current value S[i] as the new min and max, and iterate backwards (while updating min) until we find the first value that is less than or equal to max-k, which is S[2]=-1; 因此我们将当前值S [i]设置为新的min和max,并向后迭代(同时更新min),直到我们找到小于或等于max-k的第一个值,即S [2] = - 1; so S[3] becomes the new minimum, and 3 the new start index: 所以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  

At this point we come to a value that is less than or equal to max-k. 此时,我们得到一个小于或等于max-k的值。 So we set min and max to 0, and iterate backwards (while updating max) until we reach S[6], which is greater than or equal to min+k, so 7 becomes the new start index; 所以我们将min和max设置为0,然后向后迭代(同时更新max),直到达到S [6],大于或等于min + k,因此7成为新的起始索引; note that the new max is not S[start], but the higher value 4 that we passed on the way. 请注意,新的最大值不是S [start],而是我们在途中传递的值4。

i  S[p] start min  max pairs

9    0    7    0    4    2  

And the total number of pairs is 23 (if I've understood the way they're counted correctly). 对的总数是23(如果我已经理解它们被正确计算的方式)。


If you want to bring down the complexity to O(n), there are a number of options, but Stefan Haustein's answer is probably the way to go. 如果你想降低O(n)的复杂性,有很多选择,但Stefan Haustein的答案可能就是要走的路。

The accepted answer is unfortunately buggy.不幸的是,接受的答案是错误的。 For detailed explanation, refer https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/discuss/?currentPage=1&orderBy=most_votes&query=详细解释参考https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/discuss/?currentPage=1&orderBy=most_votes&query=

The above leetcode question isn't exactly what this SO question asks, but changing a one line of code will make it work for this question.上面的 leetcode 问题并不完全是这个 SO 问题所问的,但是更改一行代码将使它适用于这个问题。

Here's a C++ code (Refer https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/discuss/609771/JavaC%2B%2BPython-Deques-O(N) for explanation)这是一个C++的代码(参考https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/discuss/609771/JavaC%2B%2BPython- Deques-O(N)的解释)

unsigned long countPairs(vector<int>& nums, int limit) { //Number of sub-arrays with range less than or equal to k
    limit--; //Number of sub-arrays with range less than k
    deque<int>minDeque,maxDeque;
    int start=0;
    unsigned long ans=0;
    for(int end =0;end<nums.size();++end){
        int w = nums[end];
        while(!minDeque.empty() && nums[minDeque.back()]>w) minDeque.pop_back();
        minDeque.push_back(end);

        while(!maxDeque.empty() && nums[maxDeque.back()]<w) maxDeque.pop_back();
        maxDeque.push_back(end);

        if(!maxDeque.empty() and !minDeque.empty()) {
            while (nums[maxDeque.front()] - nums[minDeque.front()] > limit) {
                if (!maxDeque.empty() and nums[maxDeque.front()] == nums[start]) maxDeque.pop_front();
                if (!minDeque.empty() and nums[minDeque.front()] == nums[start]) minDeque.pop_front();
                start++;
            }
            ans += end - start + 1;
        }
    }
    return ans;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM