【问题标题】:Finding the longest gap between multiple intervals寻找多个区间之间的最长间隔
【发布时间】:2019-11-05 10:57:12
【问题描述】:

假设您有一个变量 $n 表示时间线上的多个分区,以及一个可变长度的间隔数组:

$n = 10;
$intervals = [
  [1, 2],
  [2, 2],
  [5, 6],
  [8, 10],
];

问题是在时间轴上找到这些间隔之间的最大差距。对于上述问题,我们有两个长度为 2 和 1 的间隙,所以答案应该是 2。为了更好地可视化它:

我的直截了当的方法效率不高...

  1. 初始化一个长度为 $n 的空时间线数组,其中每个元素设置为“E”,如空。
  2. Foreach 在每个间隔上循环,并从间隔开始到间隔结束创建另一个 for 循环,并将时间线数组中的这些元素设置为“T”。
  3. 遍历时间线数组并启动一个 $counter

我可以做哪些改进?

请注意:

  • 间隔始终根据其起始位置进行排序。
  • 间隔不必从时间线的开头开始,也不必在时间线的末尾结束。因此,在第一个间隔之前可能会有一个间隙,在最后一个间隔之后可能会有一个间隙。
  • 间隔可以重叠。所以简单地计算下一个间隔的开始减去这个间隔的结束是行不通的......考虑这个例子:[1,5] [2,4] [6,10] [6,8]

【问题讨论】:

  • 您的$intervals 是否总是按顺序排列?
  • @NickParsons 谢谢,是的,我的意思是 2 和 1。区间总是按其起始位置排序。

标签: php arrays algorithm logic


【解决方案1】:
<?php

$n = 10;
$intervals = [
  [1, 2],
  [2, 2],
  [5, 6],
  [8, 10]
];

$non_overlapping = [];
$start = -1;
$end = -1;

foreach($intervals as $index => $interval){
    if($start == -1) $start = $interval[0];
    if($end == -1) $end = $interval[1];

    if($index == 0) continue; // since it's first index

    if($interval[0] >= $end){
        $non_overlapping[] = [$start,$end];
        $start = $interval[0];
        $end = $interval[1];
    }else{
        $end = max($end,$interval[1]);
    }
}

$non_overlapping[] = [$start,$end];
$maximum_gap = 0;
$prev_end = 0;

foreach($non_overlapping as $index => $interval){
    $maximum_gap = max($maximum_gap,$interval[0] - $prev_end - 1);
    $prev_end = $interval[1];
}

$maximum_gap = max($maximum_gap,$n - $prev_end);

echo $maximum_gap;
  • 由于您的区间是根据开始时间排序的,因此我们制作了一个新的非重叠区间数组。
  • 现在,我们只需将新的开始时间与之前的结束时间相减,最后将最后的结束时间与$n 本身相减,然后找到最大差距。

【讨论】:

  • @sandrows 很高兴为您提供帮助 :)
  • 对于 $n=100 有 40 个间隔,执行时间是 ~ 68 us...选择你的作为答案。
【解决方案2】:

更新的解决方案:-

$intervals = [
    [1,2],
    [2,2],
    [5,6],
    [8,10]
];
$rr = [];
foreach($intervals as $v){
    $rr[] = range($v[0],$v[1]);
}
$n = 10;
$range      = range(1,$n);
$diff =     array_diff($range,array_values(array_unique(array_merge(...$rr))));
$r = groupConsecutive($diff);

$max = 0;
if (count($r)) {
    foreach ($r as $gap) {
        $length = 1;
        if (is_array($gap)) $length = count($gap);
        if ($max < $length) $max = $length;
    }
}

echo $max;

function  groupConsecutive($array) {
   $ret  = array();
   $temp = array();
   foreach($array as $val) {
      if(next($array) == ($val + 1))
         $temp[] = $val;
      else
         if(count($temp) > 0) {
            $temp[] = $val;
            $ret[]  = $temp;
            $temp   = array();
         }
         else
            $ret[] = $val;
   }
   return $ret;
}

DEMO

【讨论】:

  • 这是找最长的间隔,不是吗?
  • 我想找到最长的间隙而不是最长的间隔。
  • @sandrows 这就是它的最后一个元素数组有 8,10 ,它有最长的间隙
  • 最长间隔是指间隔之间的空闲块,而不是间隔持续时间。请查看我发布的图片以获得更好的可视化效果。
  • 不错的方法@RakeshJakhar,但是,对于跨越 3 个块的间隔(考虑 [2,4] [5,8] [9,10]),如果你有两个间隔更多超过 1 个空闲块,计数器不断增加(考虑 [1,1] [5,6] [9,10])。
【解决方案3】:

如果分区已排序(如您的示例中所示)。您可以遍历分区并创建一个新数组,其中包含:

  • 第一个数组中区间的原始位置
  • 分区的第一个元素与前一个分区的最后一个元素的距离。

这样,您将获得分区之间的间隙列表。然后,您只需对这些空白进行排序并取最后一个。

但正如我所说,这是假设您的分区已排序

有一个例子,然后你按值的第二个元素对$gaps 进行排序并取最后一个。

<?php                                                                                                                                                                                                                                                         

$n = 10;
$intervals = [
  [1, 2],
  [2, 2],
  [5, 6],
  [8, 10],
];

$gaps = array();

$prev_range_max = 1;
for ($i = 0; $i < count($intervals); $i++) {
    $gaps[] = Array($i, $intervals[$i][0] - $prev_range_max);
    $prev_range_max = $intervals[$i][1];
}

【讨论】:

  • 考虑到第一个间隔之前是否存在间隙的好方法,但是,它不考虑最后一个间隔之后的间隙。
  • 它不适用于重叠间隔,请考虑这些 [1,5] [2,4] [6,10] [6,8]。它还将间隔的结束计算为间隙,因此您需要在每次计算中减去 1。
【解决方案4】:

我们可以有一个简单的 O(n) 时间 O(1) 空间算法来跟踪我们在迭代按开始位置排序的间隔时所处的间隔。当前间隔开始于:

left = interval[0][0]
right = interval[0][1]
i = 1 // pointer
n = list length
max_gap = 0

那么,

while i < n and interval[i][0] <= right:
  // update right
  right = max(right, interval[i][1])
  i = i + 1

现在,如果没有间隙,我们要么位于列表的末尾,要么处于至少从 (right + 1) 开始的新间隔(我认为在您的情况下不符合差距)。

所以这里更新当前最大的gap,重复while程序。

max_gap = max(
  max_gap,
  interval[i][0] - right - 1
)
left = interval[i][0]
right = interval[i][1]

JavaScript 示例:

function f(A){
  console.log(JSON.stringify(A))
  let left = A[0][0]
  let right = A[0][1]
  console.log(`i: 0, (${ left }, ${ right })`)
  let i = 1 // pointer
  let n = A.length
  let max_gap = 0

  while (i < n){
    while (i < n && A[i][0] <= right){
      // update right
      right = Math.max(right, A[i][1])
      console.log(`i: ${ i }, (${ left }, ${ right })`)
      i = i + 1
    }

    if (i < n){
      max_gap = Math.max(
        max_gap,
        A[i][0] - right - 1
      )
      console.log(`i: ${ i }, max_gap: ${ max_gap }`)
      left = A[i][0]
      right = A[i][1]
    }
  }

  return max_gap
}

let examples = [
  [[1,5], [2,4], [6,10], [6,8]],
  [[1, 2], [2, 2], [5, 6], [8, 10]],
  [[2,4], [5,8], [9,10]],
  [[1,1], [5,6], [9,10]]
]

for (let ex of examples)
  console.log(`result: ${ f(ex) }\n\n`)

【讨论】:

  • 对于区间 [2,4],[5,8],[9,10],输出应该是 1 而不是零。由于时间线的开头有一个间隙,您也没有考虑时间线中有多少块,您将 n 设置为间隔长度。
  • @sandrows 您在问题描述中的示例,[1, 2], [5, 6]... 表示差距为 2。这意味着您将差距定义为 start - end - 1 (5 - 2 - 1 = 2)。这似乎与您要求 [2,4],[5,8],[9,10] 产生 1 的差距相矛盾。其次,您为什么要提出我的代码将 n 设置为的问题?你能解释一下吗?
  • 我的示例有两个间隙,一个长度为 2,另一个长度为 1(即在第 7 个块上)。我并没有说间隙必须具有最小数量的块才能被视为间隙,它的长度可能是 1 或 2 或更多
  • 关于 n... .因此,从时间线的开始到间隔的开始有 4 个块的间隙,从间隔的结束到时间线的结束有 3 个间隔。
  • @sandrows 关于[2,4],[5,8],[9,10] output should be 1 - 请解决您的示例(将[1, 2], [5, 6] 显示为 2 的差距)与您的请求之间的冲突 - 我看不出如何制作代码有 2..5 代表 2 的差距,同时有 4..5 或 8..9 代表 1 的差距。关于你对n 使用的描述——我不明白。请提供示例以帮助解释,以及代码失败的示例以及您的预期输出。
【解决方案5】:

我正在描述一种非常有效的方法-

  1. 如果间隔未根据开始时间排序- 首先根据开始时间对区间数组进行排序。 最坏情况复杂度 O(nlogn)。如果它已排序,那么我们不必做任何事情。

  2. 从第二个元素开始遍历排序的区间数组,如果开始时间大于最后一个区间的结束时间,则每次迭代 - 计算差值。

  3. 比较每次迭代中的差异,找出最大差异。

    复杂性- 未排序数组- O(nlogn)+ O(n) 排序数组 - O(n)。

伪代码--

例子--

【讨论】:

  • 这不适用于重叠间隔,考虑 [1,5] [2,4] [6,10] [6,8] 它应该输出 0,但是,使用这种方法它将输出1.您还需要考虑时间线开始/结束时的差距;在第一个间隔开始之前和最后一个间隔结束之后。
  • 嗨,我给出了一个伪代码和一个模拟,请检查一下。
  • 在您的示例中,您返回了 maxgap = 1。但是,如果您将其可视化,它应该为零。
  • 我在测试用例模拟中犯了一个错误,我已经更新了它。算法是正确的。 [1,5][2,3][7,10][6,8] 根据算法,第三次迭代应该针对 [6,8] 我错误地做了 [7,10].. 因为首先我们必须根据开始时间对间隔进行排序,因此 [6,8] 排在第一位。在第三次迭代后 - lastbound = max(5,8)= 8 并且 maxgap 保持为 0。在 [7,10] 的第四次迭代后它将是 lastbound 10 并且 maxgap 将保持为 0。
猜你喜欢
  • 1970-01-01
  • 2016-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多