在给定范围内找到缺失范围

时间:2020-03-10 05:57:00

标签: algorithm data-structures

给定主范围和所有子范围时,我们需要找到缺失的范围。 主要范围:[-10,10]

子范围:[-10,-5],[-4,-3],[-2、3],[7、10]

假设:

1)范围值可以达到2 ^ 63。

2)子范围不会重叠,并且它们的顺序可以不同。 例如:可以是[-10,-5],[7,10],[-2,3],[-4,-3]

在这里找到缺失范围的最佳算法是什么?

4 个答案:

答案 0 :(得分:1)

假设间隔未排序,我看不到避免排序成本,因为每个间隔可以是一个单例([n,n])。对于比较排序,该成本可以为O(n log n),对于基数排序,该成本可以为O(n)。从现在开始,我们假设输入间隔已排序并且不包含任何重叠。这是O(n)单遍Python实现:

xs =  [[-10, -5] , [-4, -3], [-2, 3], [7, 10]]
bounds = (-10, 10)
missing = list()

# pre-processing
xs_sorted = sorted(xs)

# pre-processing a missing range on the lower bound
if bounds[0] < xs_sorted[0][0]:
  missing.append((bounds[0], xs_sorted[0][0]-1))

def f_reduce(a, b):
  if a[1] + 1 == b[0]:
    # merge contiguous intervals
    return (a[0], b[1])
  else:
    # gap detected; add the gap to the missing range list
    # and move to the next value
    missing.append((a[1]+1, b[0]-1))
    return b

from functools import reduce
reduce(f_reduce, xs_sorted)

# post-processing on a missing range on the upper bound
if bounds[1] > xs_sorted[-1][1]:
  missing.append((xs_sorted[-1][1]+1, bounds[1]))

print(missing)
# [(4, 6)]

方法是使用带有臭味的功能样式reduce。当函数f_reduce遇到两个间隔(a, b)(c, d)时,如果(a, d),我们将返回一个复合间隔b + 1 == c。否则,检测并存储间隙。返回的间隔为(c, d)。当间隔的两个极端范围上出现间隙时,预处理和后处理步骤将处理令人讨厌的情况。

答案 1 :(得分:0)

尝试使用以下方法,它适用于O(n),其中n是范围宽度。

// entire range is initially 0.
    int arr[range_max - range_min + 2] = {0};
    //for each sub_range increment the values by 1.
    for(int i = 0; i<n; i++){
        arr[sub_range_min[i] - range_min] += 1;
        arr[sub_range_min[i] - range_max + 1] -= 1;
    }
    for(int i = 1; i< range_max - range_min + 2; i++){
        arr[i] += arr[i-1];
    }
    // all the uncovered area in the range by the sub_ranges will be marked 0 in the array.

答案 2 :(得分:0)

看起来您可以遍历数组并在其中找到索引i

xi != y(i-1)

(y(i-1), xi) 

是答案

答案 3 :(得分:0)

假设仅缺少一个一个间隔:

我们可以在O(k)中执行此操作,其中k是子范围的数量。

制作一个相连子范围的链条(如Chasles)(因为它们之间通常不会重叠)。

最后,出现三种可能的情况:

  • 只有一条链:缺少的子范围位于开头
  • 或最后
  • 两条链:位于两者之间

在当前子间隔,检查它是否是链的延长。

  • 如果不创建另一个链。
  • 如果是,请增加链接,例如ssssnake。然后也许它将那条链与另一条链连接起来。然后将两个链和子间隔减少为一个大链

一条链可以简单地用它的左右来表示 要找到增加的链,只需在左侧使用一个哈希表,在右侧使用另一个哈希表

function getMissingSub (subs, minRange, maxRange) {
  const chainsByLeft = new Map () // left -> [left, whatsoever]
  const chainsByRight = new Map () // right -> [whatsoever, right]
  // before: [[a, [a, whatsoever]]]
  // after: [[newVal, [newval, whatsoever]]]
  function prolongeLeft (x, newVal) {
    const chain = chainsByLeft.get(x)
    const old = chain[0]
    chain[0] = newVal
    chainsByLeft.set(newVal, chain)
    chainsByLeft.delete(old)
    return chain
  }
  function prolongeRight (x, newVal) {
    const chain = chainsByRight.get(x)
    const old = chain[1]
    chain[1] = newVal
    chainsByRight.set(newVal, chain)
    chainsByRight.delete(old)
    return chain
  }
  subs.forEach(([a,b]) => {
    if (chainsByLeft.has(b) || chainsByRight.has(a)) {
      if (chainsByLeft.has(b)) {
        // prolonge on the left
        const chain = prolongeLeft(b, a)
        if (chainsByRight.has(a) ) {
          prolongeRight(a, chain[1])
        }
      } else {
        const chain = prolongeRight(a, b)
        if (chainsByLeft.has(b) ) {
          prolongeLeft(b, chain[0])
        }
      }
    } else {
      // new chain
      const chain = [a, b]
      chainsByLeft.set(a, chain)
      chainsByRight.set(b, chain)
    }
  })
  let missingRange
  if (chainsByLeft.size === 1) {
    const [, [left, right]] = chainsByLeft.entries().next().value
    if (left === minRange) {
      missingRange = [right, maxRange]
    } else {
      missingRange = [minRange, left]
    }
  } else {
    const [[, [l1, r1]], [, [l2, r2]]] = chainsByLeft.entries()
    if (r1 < r2) {
      missingRange = [r1, l2]
    } else {
      missingRange = [r2, l1]
    }
  }
  return { missingRange, chainsByLeft }
}
const dump = ({ missingRange: [a,b] }) => console.log(`missing [${a}, ${b}]`)
dump(getMissingSub([[0, 1],[1, 2]], 0, 4))
dump(getMissingSub([[0, 1],[1, 2]], -1, 2))
dump(getMissingSub([[0, 1],[2, 3]], 0, 3))
dump(getMissingSub([[-10, -5] , [-4, -3], [-2, 3], [7, 10]], -10, 10))


如果您有几个缺失的范围,显然可以有两个以上的链,那么您可能需要一种排序链的顺序并直接找到连续链之间的距离

//COPY PASTED FROM BEFORE
function getMissingSub (subs, minRange, maxRange) {
  const chainsByLeft = new Map () // left -> [left, whatsoever]
  const chainsByRight = new Map () // right -> [whatsoever, right]
  // before: [[a, [a, whatsoever]]]
  // after: [[newVal, [newval, whatsoever]]]
  function prolongeLeft (x, newVal) {
    const chain = chainsByLeft.get(x)
    const old = chain[0]
    chain[0] = newVal
    chainsByLeft.set(newVal, chain)
    chainsByLeft.delete(old)
    return chain
  }
  function prolongeRight (x, newVal) {
    const chain = chainsByRight.get(x)
    const old = chain[1]
    chain[1] = newVal
    chainsByRight.set(newVal, chain)
    chainsByRight.delete(old)
    return chain
  }
  subs.forEach(([a,b]) => {
    if (chainsByLeft.has(b) || chainsByRight.has(a)) {
      if (chainsByLeft.has(b)) {
        // prolonge on the left
        const chain = prolongeLeft(b, a)
        if (chainsByRight.has(a) ) {
          prolongeRight(a, chain[1])
        }
      } else {
        const chain = prolongeRight(a, b)
        if (chainsByLeft.has(b) ) {
          prolongeLeft(b, chain[0])
        }
      }
    } else {
      // new chain
      const chain = [a, b]
      chainsByLeft.set(a, chain)
      chainsByRight.set(b, chain)
    }
  })
  let missingRange
  if (chainsByLeft.size === 1) {
    const [, [left, right]] = chainsByLeft.entries().next().value
    if (left === minRange) {
      missingRange = [right, maxRange]
    } else {
      missingRange = [minRange, left]
    }
  } else {
    const [[, [l1, r1]], [, [l2, r2]]] = chainsByLeft.entries()
    if (r1 < r2) {
      missingRange = [r1, l2]
    } else {
      missingRange = [r2, l1]
    }
  }
  return { missingRange, chainsByLeft }
}

//ENDCOYP PASTED

function getMissingSubs(subs, minRange, maxRange) {
  const { missingRange, chainsByLeft } = getMissingSub.apply(null, arguments)
  const missingRanges = []
  ;[[minRange, minRange], ...chainsByLeft.values(), [maxRange, maxRange]]
    .sort((a,b) => a[0]-b[0]).reduce((chain, next) => {
      if (chain[1] !== next[0]) {
        missingRanges.push([chain[1], next[0]])
      }
      return next
    })
  return { missingRanges }
}
const dump2 = ({ missingRanges }) => console.log(`missing2 ${JSON.stringify(missingRanges)}`)
dump2(getMissingSubs([[0, 1],[1, 2]], 0, 4))
dump2(getMissingSubs([[0, 1],[1, 2]], -1, 2))
dump2(getMissingSubs([[0, 1],[2, 3]], 0, 3))
dump2(getMissingSubs([[-10, -5] , [-4, -3], [-2, 3], [7, 10]], -10, 10))