给定主范围和所有子范围时,我们需要找到缺失的范围。 主要范围:[-10,10]
子范围:[-10,-5],[-4,-3],[-2、3],[7、10]
假设:
1)范围值可以达到2 ^ 63。
2)子范围不会重叠,并且它们的顺序可以不同。 例如:可以是[-10,-5],[7,10],[-2,3],[-4,-3]
在这里找到缺失范围的最佳算法是什么?
答案 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)(因为它们之间通常不会重叠)。
最后,出现三种可能的情况:
在当前子间隔,检查它是否是链的延长。
一条链可以简单地用它的左右来表示 要找到增加的链,只需在左侧使用一个哈希表,在右侧使用另一个哈希表
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))