繁体   English   中英

范围小于 k 的子数组数

[英]Number of subarrays with range less than k

给定一个(未排序的)数组 S 和一些 integer k,找出满足 S[i...j] < k 的对 i,j 的数量。 其中范围是 max(S[i...j]) - min(S[i...j])。

我在面试中收到这个问题,并且只能在对 S 进行排序后得出一个 O(nlogn) 的解决方案。但是,我被告知有一个 O(n) 的解决方案。 有任何想法吗?

从i开始,j = 0,我们可以迭代j,跟踪最小值和最大值。 当范围变得> = k时,通过提高最大值,我们需要找到一个新的i,其中min(s [i..j])> max - k(模拟另一种情况)。

显然我们可以通过从j向后搜索找到这个索引,但是我们需要从i向前搜索以保持运行时线性(例如:1 2 3 4 5 6,其中k = 4将在每个步骤中使用向后搜索回溯几个元素,而前向搜索确保每个i只被考虑一次)。

为了能够这样做,我们可以保留两个具有单调上升/下降数组值的索引列表。

因此,当我们在“外部”循环中迭代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 :)

您可以使用经典双指针技术的变体来实现此目的。 区别在于您需要跟踪其值在k范围内的范围的起始索引,以及该范围内的最小值和最大值,以便我们知道当前值何时超出范围(值为起始指数不一定是最小值或最大值)。 需要注意的另一点是,当当前值超出范围时,新的起始索引不一定由最小值或最大值表示,而是必须通过从当前索引开始向后迭代来搜索。

正如KSwama指出的那样,有可能不得不多次向后迭代相同的元素,因此时间复杂度将不是线性的。 我认为最坏的情况意味着迭代大多数元素高达k次,因此复杂度可能类似于O(n×k)。

将起始索引设置为0,将min和max设置为S [0]。 然后,迭代输入,对于每个值S [i],如果需要,将min或max调整为S [i]。 当你得到一个例如大于或等于min + k的值S [i]时,将min和max设置为S [i],然后从i向后迭代(同时调整min),直到找到值S [ j]小于或等于max-k; 然后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的答案可能就是要走的路。

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

上面的 leetcode 问题并不完全是这个 SO 问题所问的,但是更改一行代码将使它适用于这个问题。

这是一个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