我根据@Sampo的摘要在TypeScript中编写了一个解决方案。该代码可以在下面找到。
在此过程中获得了一些见解。
只有在中间插入两个现有的排序键之间需要生成一个新的排序关键字,交换(即清理)不会不进行拆分(即新的中点)。如果您移动了两个项目,而您仅触摸了其中一项,则将丢失有关哪些两个元素更改了列表位置的信息。即使是一开始的要求,也请注意这是一个好主意
每隔1074:th中点分割一次,我们就需要标准化浮点范围。我们可以通过简单地检查新的中点是否满足不变量来检测到这一点。
a.sortKey < m && m < b.sortKey
缩放比例无关紧要,因为对排序键进行了归一化,所以归零仍然会在每个1074
中点分割时进行。如果从一开始就更广泛地分配数字,情况就不会改善。
排序键规范化非常罕见。您将摊销此成本,以至于标准化不明显。不过,如果您有1000多个元素,我会谨慎使用此方法。
export interface HasSortKey {
sortKey: number;
}
function normalizeList<T extends HasSortKey>(list: Array<T>) {
const normalized = new Array<T>(list.length);
for (let i = 0; i < list.length; i++) {
normalized[i] = { ...list[i], sortKey: i };
}
return normalized;
}
function insertItem<T extends HasSortKey>(
list: Array<T>,
index: number,
item: Partial<T>
): Array<T> {
if (list.length === 0) {
list.push({ ...item, sortKey: 0 } as T);
} else {
// list is non-empty
if (index === 0) {
list.splice(0, 0, { ...item, sortKey: list[0].sortKey - 1 } as T);
} else if (index < list.length) {
// midpoint, index is non-zero and less than length
const a = list[index - 1];
const b = list[index];
const m = (a.sortKey + b.sortKey) / 2;
if (!(a.sortKey < m && m < b.sortKey)) {
return insertItem(normalizeList(list), index, item);
}
list.splice(index, 0, { ...item, sortKey: m } as T);
} else if (index === list.length) {
list.push({ ...item, sortKey: list[list.length - 1].sortKey + 1 } as T);
}
}
return list;
}
export function main() {
const normalized: Array<number> = [];
let list: Array<{ n: number } & HasSortKey> = [];
list = insertItem(list, 0, { n: 0 });
for (let n = 1; n < 10 * 1000; n++) {
const list2 = insertItem(list, 1, { n });
if (list2 !== list) {
normalized.push(n);
}
list = list2;
}
let m = normalized[0];
console.log(
normalized.slice(1).map(n => {
const k = n - m;
m = n;
return k;
})
);
}