fix bug in trie

This commit is contained in:
Joeri Exelmans 2025-05-10 20:28:40 +02:00
parent 075cc1244f
commit fe6e86e1a4
2 changed files with 112 additions and 24 deletions

View file

@ -6,6 +6,21 @@ export const emptyTrie = {
children: [],
};
// invariant: the keys of a trie node should always be sorted
export const isProperlySorted = trie => {
for (let i=0; i<trie.children.length-1; i++) {
if (trie.children[i] >= trie.children[i+1]) {
return false; // not properly sorted!
}
}
for (const [_, node] of trie.children) {
if (!isProperlySorted(node)) {
return false;
}
}
return true;
}
// find maximal common prefix, and whether string A is smaller than B
const commonPrefix = (strA, strB) => {
let i=0
@ -41,6 +56,14 @@ const __binarySearch = (ls, key, min, max) => {
return __binarySearch(ls, key, middle+1, max);
}
const check = trie => {
// uncomment if you think shit is broken:
// if (!isProperlySorted(trie)) {
// throw new Error('not properly sorted!')
// }
return trie;
}
// insert (key,value) into trie.
export const insert = trie => key => value => {
if (key.length === 0) {
@ -54,54 +77,59 @@ export const insert = trie => key => value => {
const [insertPos, prefix] = binarySearch(trie.children, key);
if (insertPos === trie.children.length) {
// insert node at end
return {
return check({
value: trie.value,
children: [
...trie.children,
[key, {value, children:[]}],
],
};
});
}
if (prefix.length === 0) {
// nothing in common...
// insert new node into children
return {
return check({
value: trie.value,
children: trie.children.toSpliced(
insertPos, // insert position
0, // delete nothing
[key, {value, children:[]}],
),
};
});
}
const [haveKey, haveChildNode] = trie.children[insertPos];
if (prefix.length === haveKey.length) {
// recurse
return {
return check({
value: trie.value,
children: trie.children.with(
insertPos, // position to update
[haveKey, insert(haveChildNode)(key.slice(prefix.length))(value)],
)
}
})
}
// otherwise, split entry:
const havePostFix = haveKey.slice(prefix.length);
const postFix = key.slice(prefix.length);
return {
return check({
value: trie.value,
children: trie.children.with(
insertPos, // position to update
[prefix, {
children: [
[havePostFix, haveChildNode],
[postFix, {value, children: []}
],
]}],
children: (havePostFix < postFix)
? [
[havePostFix, haveChildNode],
[postFix, {value, children: []}],
]
: [
[postFix, {value, children: []}],
[havePostFix, haveChildNode],
],
}],
),
};
});
};
// given a prefix, return a string X such that prefix+X is a possibly larger prefix for the same entries as the original prefix.