Предполагая только один пропущенный интервал:
Мы можем сделать это в 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))