Algorithm Implementation.
Bisect
//////////////////////////////////////////////Template/////////////////////////////////////////////////////////////
function Bisect() {
return { insort_right, insort_left, bisect_left, bisect_right }
function insort_right(a, x, lo = 0, hi = null) {
lo = bisect_right(a, x, lo, hi)
a.splice(lo, 0, x)
}
function bisect_right(a, x, lo = 0, hi = null) {
// > upper_bound
if (lo < 0) throw new Error('lo must be non-negative')
if (hi == null) hi = a.length
while (lo < hi) {
let mid = parseInt((lo + hi) / 2)
x < a[mid] ? (hi = mid) : (lo = mid + 1)
}
return lo
}
function insort_left(a, x, lo = 0, hi = null) {
lo = bisect_left(a, x, lo, hi)
a.splice(lo, 0, x)
}
function bisect_left(a, x, lo = 0, hi = null) {
// >= lower_bound
if (lo < 0) throw new Error('lo must be non-negative')
if (hi == null) hi = a.length
while (lo < hi) {
let mid = parseInt((lo + hi) / 2)
a[mid] < x ? (lo = mid + 1) : (hi = mid)
}
return lo
}
}
LIS(Longest Increasing Subsequence) implementation
const LIS = arr => {
const n = arr.length
// memo[j]: 长度为j的最长自增子序列最后一位的index(同长度最后一位最小).
// stores the index k of the smallest value X[k] such that there is an increasing subsequence of length j ending at X[k] on the range k ≤ i
// pre[k]: 以arr[k]结尾的LIS,arr[k]之前一个元素的index.
// stores the index of the predecessor of X[k] in the longest increasing subsequence ending at X[k]
const pre = Array(n), memo = Array(n + 1)
let len = 0 // length
for(let i = 0; i < n; i++) {
let lo = 1, hi = len
while(lo <= hi) {
let mid = Math.ceil((lo + hi) / 2)
if(arr[memo[mid]] < arr[i]) lo = mid + 1
else hi = mid - 1
}
const newL = lo
pre[i] = memo[newL - 1]
memo[newL] = i
if(newL > len) len = newL
}
const res = Array(len)
let k = memo[len]
for(let i = len - 1; i >= 0; i--) {
res[i] = arr[k]
k = pre[k]
}
return res
}
// construct. LIS
const LIS = X => {
const n = X.length
const P = Array(n), M = Array(n + 1)
let L = 0
for(let i = 0; i < n; i++) {
let lo = 1, hi = L
while(lo <= hi) {
let mid = Math.ceil((lo + hi) / 2)
if(X[M[mid]] < X[i]) lo = mid + 1
else hi = mid - 1
}
let newL = lo
P[i] = M[newL - 1]
M[newL] = i
if(newL > L) L = newL
}
let S = Array(L)
let k = M[L]
for(let i = L - 1; i >= 0; i--) {
S[i] = X[k]
k = P[k]
}
return S
}
function constructLIS(arr) {
const n = arr.length;
const L = Array(n);
for (let i = 0; i < L.length; i++) L[i] = [];
L[0].push(arr[0]);
for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
if (arr[i] > arr[j] && L[i].length < L[j].length + 1) {
L[i] = L[j].slice();
}
}
L[i].push(arr[i]);
}
let max = L[0];
for (let x of L) {
if (x.length > max.length) max = x;
}
return max;
}
/**
* @param {number[]} nums
* @return {number}
*/
const lengthOfLIS = function(nums) {
const stack = []
for(let e of nums) {
if(stack.length === 0 || e > stack[stack.length - 1]) {
stack.push(e)
continue
}
let l = 0, r = stack.length - 1, mid
while(l < r) {
const mid = l + ((r - l) >> 1)
if(e > stack[mid]) l = mid + 1
else r = mid
}
stack[l] = e
}
return stack.length
};
Convert base implementation
function convertFromBaseToBase(str, fromBase, toBase){
const num = parseInt(str, fromBase);
return num.toString(toBase);
}
floorIndex, ceilIndex implementation
function ceilIndex(t, l, r, key) {
while (r - l > 1) {
let m = (l + (r - l) / 2) >> 0
if (t[m] >= key) {
r = m
} else {
l = m
}
}
return r
}
function floorIndex(t, l, r, key) {
while (r - l > 1) {
let m = (l + (r - l) / 2) >> 0
if (t[m] <= key) {
l = m
} else {
r = m
}
}
return l
}
lower_bound implementation
// the first element in the range [first, last) which has a value not less than val.
// This means that the function returns the index of the next smallest number just
// greater than or equal to that number. If there are multiple values that are equal to val,
// lower_bound() returns the index of the first such value.
// Like C++'s std::lower_bound. Returns the first index at which
// `value` could be inserted without changing the ordering. Assumes
// the array is sorted.
//
// `first` and `last` are indices and `less` is an optionally-specified
// function that returns true if
// array[i] < value
// for some i and false otherwise.
//
// Usage: lower_bound(array, value, [less])
// lower_bound(array, first, last, value, [less])
function lower_bound(array, arg1, arg2, arg3, arg4) {
let first;
let last;
let value;
let less;
if (arg3 === undefined) {
first = 0;
last = array.length;
value = arg1;
less = arg2;
} else {
first = arg1;
last = arg2;
value = arg3;
less = arg4;
}
if (less === undefined) {
less = function (a, b) { return a < b; };
}
let len = last - first;
let middle;
let step;
while (len > 0) {
step = Math.floor(len / 2);
middle = first + step;
if (less(array[middle], value, middle)) {
first = middle;
first += 1;
len = len - step - 1;
} else {
len = step;
}
}
return first;
};
upper_bound implementation
/**
It returns the first element in the range [first, last) that
is greater than value, or last if no such element is found.
*/
function upperBound(array, func) {
let diff, len, i, current;
len = array.length;
i = 0;
while (len) {
diff = len >>> 1;
current = i + diff;
if (func(array[current])) {
len = diff;
} else {
i = current + 1;
len -= diff + 1;
}
}
return i;
}
Binary insert implementation
/**
* Takes in a __SORTED__ array and inserts the provided value into
* the correct, sorted, position.
* @param array the sorted array where the provided value needs to be inserted (in order)
* @param insertValue value to be added to the array
* @param comparator function that helps determine where to insert the value (
*/
function binaryInsert(array, insertValue, comparator = (a, b) => a - b) {
/*
* These two conditional statements are not required, but will avoid the
* while loop below, potentially speeding up the insert by a decent amount.
* */
if (array.length === 0 || comparator(array[0], insertValue) >= 0) {
array.splice(0, 0, insertValue)
return array;
} else if (array.length > 0 && comparator(array[array.length - 1], insertValue) <= 0) {
array.splice(array.length, 0, insertValue);
return array;
}
let left = 0, right = array.length;
let leftLast = 0, rightLast = right;
while (left < right) {
const inPos = Math.floor((right + left) / 2)
const compared = comparator(array[inPos], insertValue);
if (compared < 0) {
left = inPos;
} else if (compared > 0) {
right = inPos;
} else {
right = inPos;
left = inPos;
}
// nothing has changed, must have found limits. insert between.
if (leftLast === left && rightLast === right) {
break;
}
leftLast = left;
rightLast = right;
}
// use right, because Math.floor is used
array.splice(right, 0, insertValue);
return array
}
Knuth–Morris–Pratt algorithm implementation
function DFA(s) {
let i = 1
let j = 0
const len = s.length
const prefix = Array(len + 1).fill(0)
prefix[0] = -1
prefix[1] = 0
while (i < len) {
if (s[j] === s[i]) {
j++
i++
prefix[i] = j
} else {
if (j > 0) j = prefix[j]
else i++
}
}
return prefix
}
function search(text, pattern) {
let t = 0
let p = 0
const tLen = text.length
const pLen = pattern.length
const matches = []
const prefix = DFA(pattern)
while (t < tLen) {
if (pattern[p] === text[t]) {
p++
t++
if (p === pLen) {
matches.push(t)
p = prefix[p]
}
} else {
p = prefix[p]
if (p < 0) {
t++
p++
}
}
}
return matches
}
LeetCode-1392.Longest Happy Prefix
Binary Indexed Tree implementation
const lowBit = (x) => x & -x
class FenwickTree {
constructor(n) {
if (n < 1) return
this.sum = Array(n + 1).fill(0)
}
update(i, delta) {
if (i < 1) return
while (i < this.sum.length) {
this.sum[i] += delta
i += lowBit(i)
}
}
query(i) {
if (i < 1) return
let sum = 0
while (i > 0) {
sum += this.sum[i]
i -= lowBit(i)
}
return sum
}
}
LeetCode-307.Range Sum Query - Mutable
Segment Tree
class SegmentTree {
/* Constructor to construct segment tree from given array. This
constructor allocates memory for segment tree and calls
constructSTUtil() to fill the allocated memory */
constructor(arr, n) {
// Allocate memory for segment tree
//Height of segment tree
const x = Math.ceil(Math.log(n) / Math.log(2))
//Maximum size of segment tree
const max_size = 2 * Math.pow(2, x) - 1
this.st = new Array(max_size) // Memory allocation
this.constructSTUtil(arr, 0, n - 1, 0)
}
// A utility function to get the middle index from corner indexes.
getMid(s, e) {
return s + (((e - s) / 2) >> 0)
}
/* A recursive function to get the sum of values in given range
of the array. The following are parameters for this function.
st --> Pointer to segment tree
si --> Index of current node in the segment tree. Initially
0 is passed as root is always at index 0
ss & se --> Starting and ending indexes of the segment represented
by current node, i.e., st[si]
qs & qe --> Starting and ending indexes of query range */
getSumUtil(ss, se, qs, qe, si) {
// If segment of this node is a part of given range, then return
// the sum of the segment
if (qs <= ss && qe >= se) return this.st[si]
// If segment of this node is outside the given range
if (se < qs || ss > qe) return 0
// If a part of this segment overlaps with the given range
const mid = this.getMid(ss, se)
return (
this.getSumUtil(ss, mid, qs, qe, 2 * si + 1) +
this.getSumUtil(mid + 1, se, qs, qe, 2 * si + 2)
)
}
/* A recursive function to update the nodes which have the given
index in their range. The following are parameters
st, si, ss and se are same as getSumUtil()
i --> index of the element to be updated. This index is in
input array.
diff --> Value to be added to all nodes which have i in range */
updateValueUtil(ss, se, i, diff, si) {
// Base Case: If the input index lies outside the range of
// this segment
if (i < ss || i > se) return
// If the input index is in range of this node, then update the
// value of the node and its children
this.st[si] = this.st[si] + diff
if (se != ss) {
const mid = this.getMid(ss, se)
this.updateValueUtil(ss, mid, i, diff, 2 * si + 1)
this.updateValueUtil(mid + 1, se, i, diff, 2 * si + 2)
}
}
// The function to update a value in input array and segment tree.
// It uses updateValueUtil() to update the value in segment tree
updateValue(arr, n, i, new_val) {
// Check for erroneous input index
if (i < 0 || i > n - 1) {
return
}
// Get the difference between new value and old value
const diff = new_val - arr[i]
// Update the value in array
arr[i] = new_val
// Update the values of nodes in segment tree
this.updateValueUtil(0, n - 1, i, diff, 0)
}
// Return sum of elements in range from index qs (quey start) to
// qe (query end). It mainly uses getSumUtil()
getSum(n, qs, qe) {
// Check for erroneous input values
if (qs < 0 || qe > n - 1 || qs > qe) {
return -1
}
return this.getSumUtil(0, n - 1, qs, qe, 0)
}
// A recursive function that constructs Segment Tree for array[ss..se].
// si is index of current node in segment tree st
constructSTUtil(arr, ss, se, si) {
// If there is one element in array, store it in current node of
// segment tree and return
if (ss == se) {
this.st[si] = arr[ss]
return arr[ss]
}
// If there are more than one elements, then recur for left and
// right subtrees and store the sum of values in this node
const mid = this.getMid(ss, se)
this.st[si] =
this.constructSTUtil(arr, ss, mid, si * 2 + 1) +
this.constructSTUtil(arr, mid + 1, se, si * 2 + 2)
return this.st[si]
}
}
/**
const arr = [1, 3, 5, 7, 9, 11]
const n = arr.length
const t = new SegmentTree(arr, n)
const log = console.log
log(t.getSum(n, 1, 3))
t.updateValue(arr, n, 1, 10)
log(t.getSum(n, 1, 3))
*/
Union-Find implementation
class UF {
constructor(n) {
this.root = Array(n).fill(null).map((_, i) => i)
}
find(x) {
if (this.root[x] !== x) {
this.root[x] = this.find(this.root[x])
}
return this.root[x]
}
union(x, y) {
const xr = this.find(x)
const yr = this.find(y)
this.root[yr] = xr
}
}
class UnionFind {
constructor(n) {
this.parents = Array(n)
.fill(0)
.map((e, i) => i + 1)
this.ranks = Array(n).fill(0)
}
root(x) {
while(x !== this.parents[x]) {
this.parents[x] = this.parents[this.parents[x]]
x = this.parents[x]
}
return x
}
find(x) {
// if (x !== this.parents[x]) this.parents[x] = this.find(this.parents[x])
// return this.parents[x]
return this.root(x)
}
check(x, y) {
return this.root(x) === this.root(y)
}
union(x, y) {
const [rx, ry] = [this.find(x), this.find(y)]
if (this.ranks[rx] >= this.ranks[ry]) {
this.parents[ry] = rx
this.ranks[rx] += this.ranks[ry]
} else if (this.ranks[ry] > this.ranks[rx]) {
this.parents[rx] = ry
this.ranks[ry] += this.ranks[rx]
}
}
}
PriorityQueue implementation
class PriorityQueue {
constructor(comparator = (a, b) => a > b) {
this.heap = []
this.top = 0
this.comparator = comparator
}
size() {
return this.heap.length
}
isEmpty() {
return this.size() === 0
}
peek() {
return this.heap[this.top]
}
push(...values) {
values.forEach((value) => {
this.heap.push(value)
this.siftUp()
})
return this.size()
}
pop() {
const poppedValue = this.peek()
const bottom = this.size() - 1
if (bottom > this.top) {
this.swap(this.top, bottom)
}
this.heap.pop()
this.siftDown()
return poppedValue
}
replace(value) {
const replacedValue = this.peek()
this.heap[this.top] = value
this.siftDown()
return replacedValue
}
parent = (i) => ((i + 1) >>> 1) - 1
left = (i) => (i << 1) + 1
right = (i) => (i + 1) << 1
greater = (i, j) => this.comparator(this.heap[i], this.heap[j])
swap = (i, j) => ([this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]])
siftUp = () => {
let node = this.size() - 1
while (node > this.top && this.greater(node, this.parent(node))) {
this.swap(node, this.parent(node))
node = this.parent(node)
}
}
siftDown = () => {
let node = this.top
while (
(this.left(node) < this.size() && this.greater(this.left(node), node)) ||
(this.right(node) < this.size() && this.greater(this.right(node), node))
) {
let maxChild =
this.right(node) < this.size() &&
this.greater(this.right(node), this.left(node))
? this.right(node)
: this.left(node)
this.swap(node, maxChild)
node = maxChild
}
}
}
Quicksort implementation
function quickSort(arr) {
// your code here
sort(arr, 0, arr.length - 1)
}
function sort(arr, start, end) {
if(start >= end) return
const pivot = partition(arr, start, end)
sort(arr, start, pivot - 1)
sort(arr, pivot + 1, end)
}
function partition(arr, start, end) {
const mid = arr[start]
let l = start + 1, r = end
while(l <= r) {
if(arr[l] <= mid) l++
else {
swap(arr, l, r)
r--
}
}
swap(arr, start, r)
return r
}
function swap(arr, i, j) {
;[arr[i], arr[j]] = [arr[j], arr[i]]
}
Quicksort implementation
function QuickSelect(array, k, comparator) {
const compare = comparator || defaultcomparator;
if (array.length < k) {
return array;
}
const idx = select(array, k, compare);
if (idx !== k) console.log("could not complete quickselect");
return array;
}
const defaultcomparator = (a, b) => a < b;
function swap(array, index1, index2) {
const temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
function partition(array, leftindex, rightindex, pivotindex, compare) {
const pivotvalue = array[pivotindex];
swap(array, pivotindex, rightindex);
let storeindex = leftindex;
for (let i = leftindex; i < rightindex; i += 1) {
if (compare(array[i], pivotvalue)) {
swap(array, storeindex, i);
storeindex += 1;
}
}
swap(array, rightindex, storeindex);
return storeindex;
}
function select(array, k, compare) {
let leftindex = 0;
let rightindex = array.length - 1;
while (true) {
if (leftindex == rightindex) return leftindex;
let pivotindex = leftindex + Math.floor((rightindex - leftindex) / 2);
pivotindex = partition(array, leftindex, rightindex, pivotindex, compare);
if (k === pivotindex) return k;
if (k < pivotindex) rightindex = pivotindex - 1;
else leftindex = pivotindex + 1;
}
}
Mergesort implementation
/**
* @param {number[]} arr
*/
function mergeSort(arr) {
// your code here
if(arr.length < 2) return
const mid = Math.floor(arr.length / 2)
const left = arr.slice(0, mid)
const right = arr.slice(mid)
mergeSort(left)
mergeSort(right)
let l = 0, r = 0
while(l < left.length || r < right.length) {
if(r === right.length || (l < left.length && left[l] <= right[r])) {
arr[l + r] = left[l++]
} else {
arr[l + r] = right[r++]
}
}
}
// Merges two subarrays of arr[].
// First subarray is arr[l..m]
// Second subarray is arr[m+1..r]
function merge(arr, l, m, r) {
let i, j, k
const n1 = m - l + 1
const n2 = r - m
/* create temp arrays */
const L = Array(n1).fill(0)
const R = Array(n2).fill(0)
/* Copy data to temp arrays L[] and R[] */
for (i = 0; i < n1; i++) L[i] = arr[l + i]
for (j = 0; j < n2; j++) R[j] = arr[m + 1 + j]
/* Merge the temp arrays back into arr[l..r]*/
i = 0 // Initial index of first subarray
j = 0 // Initial index of second subarray
k = l // Initial index of merged subarray
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i]
i++
} else {
arr[k] = R[j]
j++
}
k++
}
/* Copy the remaining elements of L[], if there
are any */
while (i < n1) {
arr[k] = L[i]
i++
k++
}
/* Copy the remaining elements of R[], if there
are any */
while (j < n2) {
arr[k] = R[j]
j++
k++
}
}
/* l is for left index and r is right index of the
sub-array of arr to be sorted */
function mergeSort(arr, l, r) {
if (l < r) {
// Same as (l+r)/2, but avoids overflow for
// large l and h
const m = l + ((r - l) >> 1)
// Sort first and second halves
mergeSort(arr, l, m)
mergeSort(arr, m + 1, r)
merge(arr, l, m, r)
}
}
LeetCode-315. Count of Smaller Numbers After Self
LeetCode-327. Count of Range Sum
BinarySearch implementation
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
const BinarySearch = function(nums, target) {
const n = nums.length
let l = 0, r = n - 1
while(l <= r) {
const mid = l + ((r - l) >> 1)
if(nums[mid] === target) return mid
if(nums[mid] > target) r = mid - 1
else l = mid + 1
}
return l
};
/**
Why return low rather than high?
The last iteration is lo == hi == mid
When target > nums[mid] == nums[lo] == nums[hi], after loop lo = lo + 1 == high +1 which will be the correct index for insertion
When target < nums[mid] == nums[lo] == nums[hi], after loop hi = hi - 1 == low - 1 is not the correct index, should be low
Why does Binary search algorithm use floor and not ceiling - not in an half open range?
This all depends on how you update your left and right variable.
Normally, we use left = middle+1 and right = middle-1, with stopping criteria left = right.
In this case, ceiling or flooring the middle value doesn't matter.
However, if we use left = middle+1 and right = middle, we must take the floor of the middle value, otherwise we end up in an endless loop.
Consider finding 11 in array 11, 22.
We set left = 0 and right = 1, the middle is 0.5, if we take the ceiling, it would be 1.
Since 22 is larger than query, we need to cut the right half and move right boarder towards middle.
This works fine when the array is large, but since there are only two elements.
right = middle will again set right to 1. We have an infinite loop.
To sum up,
both ceiling and flooring work fine with left = middle+1 and right = middle-1
ceiling works fine with left = middle and right = middle-1
flooring works fine with left = middle+1 and right = middle
*/
function binarySearch(arr, compareFn, target) {
let left = 0; // inclusive
let right = arr.length; // exclusive
let found;
while (left < right) {
const middle = left + ((right - left) >> 1);
const compareResult = compareFn(target, arr[middle]);
if (compareResult > 0) {
left = middle + 1;
} else {
right = middle;
// We are looking for the lowest index so we can't return immediately.
found = !compareResult;
}
}
// left is the index if found, or the insertion point otherwise.
// ~left is a shorthand for -left - 1.
return found ? left : ~left;
};
Greatest common divisor implementation
function GCD(a, b) {
if (a === 0) return b
if (b === 0) return a
return GCD(Math.abs(a - b), Math.min(a, b))
}
// or
function gcd(a, b) {
return b ? gcd(b, a % b) : a
}
// or
function gcd(a, b) {
while (b) {
a %= b
b = [a, (a = b)][0]
}
return a
}
Least Common Multiple implementation
function lcm(a, b) {
return a / gcd(a, b) * b;
}
// another
function lcm(a, b) {
return a * b / gcd(a, b);
}
Manacher's Algorithm implementation
function manachersAlgorithm(s, N) {
const str = getModifiedString(s, N)
const len = 2 * N + 1
// expansion length
const P = new Array(len).fill(0)
// stores the center of the longest palindromic substring until now
let c = 0
// stores the right boundary of the longest palindromic substring until now
let r = 0
let maxLen = 0
for (let i = 0; i < len; i++) {
//get mirror index of i
const mirror = 2 * c - i
// see if the mirror of i is expanding beyond the left boundary
// of current longest palindrome at center c
// if it is, then take r - i as P[i]
// else take P[mirror] as P[i]
if (i < r) {
P[i] = Math.min(r - i, P[mirror])
}
//expand at i
let a = i + (1 + P[i])
let b = i - (1 + P[i])
while (a < len && b >= 0 && str.charAt(a) === str.charAt(b)) {
P[i]++
a++
b--
}
// check if the expanded palindrome at i is expanding beyond the
// right boundary of current longest palindrome at center c
// if it is, the new center is i
if (i + P[i] > r) {
c = i
r = i + P[i]
if (P[i] > maxLen) {
maxLen = P[i]
}
}
}
return maxLen
}
function getModifiedString(s, N) {
let sb = ''
for (let i = 0; i < N; i++) {
sb += '#'
sb += s.charAt(i)
}
sb += '#'
return sb
}
Gray code implementation
function BinaryToGray(num) {
// The operator >> is shift right. The operator ^ is exclusive or.
return num ^ (num >> 1);
}
// This function converts a reflected binary Gray code number to a binary number.
function GrayToBinary(num) {
let mask = num;
// Each Gray code bit is exclusive-ored with all more significant bits.
while (mask) {
mask >>= 1;
num ^= mask;
}
return num;
}
// A more efficient version for Gray codes 32 bits or fewer
// through the use of SWAR (SIMD within a register) techniques.
// It implements a parallel prefix XOR function. The assignment
// statements can be in any order.
//
// This function can be adapted for longer Gray codes by adding steps.
function GrayToBinary32(num) {
num ^= num >> 16;
num ^= num >> 8;
num ^= num >> 4;
num ^= num >> 2;
num ^= num >> 1;
return num;
}
Trie implementation
class TrieNode {
constructor(v, isComplete = false) {
this.val = v;
this.isComplete = isComplete;
this.children = new Map();
}
}
class Trie {
constructor() {
this.head = new TrieNode(null);
}
/**
* @param {string} word
* @return {Trie}
*/
addWord(word) {
const characters = Array.from(word);
let currentNode = this.head;
for (let charIndex = 0; charIndex < characters.length; charIndex++) {
const isComplete = charIndex === characters.length - 1;
const char = characters[charIndex];
if (currentNode.children.has(char)) {
currentNode = currentNode.children.get(char);
} else {
const child = new TrieNode(char, isComplete);
currentNode.children.set(char, child);
currentNode = child;
}
}
return this;
}
/**
* @param {string} word
* @return {Trie}
*/
deleteWord(word) {
const depthFirstDelete = (currentNode, charIndex = 0) => {
if (charIndex >= word.length) {
return;
}
const character = word[charIndex];
const nextNode = currentNode.children.get(character);
if (nextNode == null) {
return;
}
depthFirstDelete(nextNode, charIndex + 1);
if (charIndex === word.length - 1) {
nextNode.isComplete = false;
}
// childNode is deleted only if:
// - childNode has NO children
// - childNode.isComplete === false
if (nextNode.children.size === 0) {
currentNode.children.delete(character);
}
};
depthFirstDelete(this.head);
return this;
}
/**
* @param {string} word
* @return {string[]}
*/
suggestNextCharacters(word) {
const lastCharacter = this.getLastCharacterNode(word);
if (!lastCharacter) {
return null;
}
return this.suggestChildren(lastCharacter);
}
/**
* @param {TrieNode} node
* @return {TrieNode}
*/
suggestChildren(node) {
if (node == null) return [];
return [...node.children.keys()];
}
/**
* Check if complete word exists in Trie.
*
* @param {string} word
* @return {boolean}
*/
doesWordExist(word) {
const lastCharacter = this.getLastCharacterNode(word);
return !!lastCharacter && lastCharacter.isComplete;
}
/**
* @param {string} word
* @return {TrieNode}
*/
getLastCharacterNode(word) {
const characters = Array.from(word);
let currentNode = this.head;
for (let charIndex = 0; charIndex < characters.length; charIndex++) {
const char = characters[charIndex];
if (!currentNode.children.has(char)) {
return null;
}
currentNode = currentNode.children.get(char);
}
return currentNode;
}
}
Bloom filter implementation
class BloomFilter {
/**
* @param {number} size - the size of the storage.
*/
constructor(size = 100) {
// Bloom filter size directly affects the likelihood of false positives.
// The bigger the size the lower the likelihood of false positives.
this.size = size
this.storage = this.createStore(size)
}
/**
* @param {string} item
*/
insert(item) {
const hashValues = this.getHashValues(item)
hashValues.forEach((val) => this.storage.setValue(val))
}
/**
* @param {string} item
* @return {boolean}
*/
mayContain(item) {
const hashValues = this.getHashValues(item)
for (let hashIndex = 0; hashIndex < hashValues.length; hashIndex += 1) {
if (!this.storage.getValue(hashValues[hashIndex])) {
return false
}
}
return true
}
/**
* @param {number} size
* @return {Object}
*/
createStore(size) {
const storage = []
for (
let storageCellIndex = 0;
storageCellIndex < size;
storageCellIndex += 1
) {
storage.push(false)
}
const storageInterface = {
getValue(index) {
return storage[index]
},
setValue(index) {
storage[index] = true
},
}
return storageInterface
}
/**
* @param {string} item
* @return {number}
*/
hash1(item) {
let hash = 0
for (let charIndex = 0; charIndex < item.length; charIndex += 1) {
const char = item.charCodeAt(charIndex)
hash = (hash << 5) + hash + char
hash &= hash
hash = Math.abs(hash)
}
return hash % this.size
}
/**
* @param {string} item
* @return {number}
*/
hash2(item) {
let hash = 5381
for (let charIndex = 0; charIndex < item.length; charIndex += 1) {
const char = item.charCodeAt(charIndex)
hash = (hash << 5) + hash + char
}
return Math.abs(hash % this.size)
}
/**
* @param {string} item
* @return {number}
*/
hash3(item) {
let hash = 0
for (let charIndex = 0; charIndex < item.length; charIndex += 1) {
const char = item.charCodeAt(charIndex)
hash = (hash << 5) - hash
hash += char
hash &= hash
}
return Math.abs(hash % this.size)
}
/**
* Runs all 3 hash functions on the input and returns an array of results.
*
* @param {string} item
* @return {number[]}
*/
getHashValues(item) {
return [this.hash1(item), this.hash2(item), this.hash3(item)]
}
}
Inverse element implementation
function inverseElement(a, n) {
let N = n
if (GCD(a, n) == 1) {
let p = 1,
q = 0,
r = 0,
s = 1
let c, quot, new_r, new_s
while (n !== 0) {
c = modulo(a, n)
quot = Math.floor(a / n)
a = n
n = c
new_r = p - quot * r
new_s = q - quot * s
p = r
q = s
r = new_r
s = new_s
}
return modulo(p, N)
} else {
return null
}
}
function modulo(a, n) {
if (a >= 0) {
return a % n
} else {
return (a % n) + n
}
}
// another
function inverseElement(a, b) {
return quickPow(a, b - 2) % b
}
function quickPow(a, b) {
let ans = 1;
a = (a % p + p) % p;
for (; b; b >>= 1) {
if (b & 1) ans = (a * ans) % p;
a = (a * a) % p;
}
return ans;
}
Sieve of Eratosthenes implementation
/**
* @param {number} maxNumber
* @return {number[]}
*/
export default function sieveOfEratosthenes(maxNumber) {
const isPrime = new Array(maxNumber + 1).fill(true);
isPrime[0] = false;
isPrime[1] = false;
const primes = [];
for (let number = 2; number <= maxNumber; number += 1) {
if (isPrime[number] === true) {
primes.push(number);
let nextNumber = number * 2;
while (nextNumber <= maxNumber) {
isPrime[nextNumber] = false;
nextNumber += number;
}
}
}
return primes;
}
Square root implementation
function squareRoot(number, tolerance = 0.0001) {
if (number < 0) {
return null;
}
if (number === 0) {
return 0;
}
let root = 1;
const requiredDelta = 1 / (10 ** tolerance);
while (Math.abs(number - (root ** 2)) > requiredDelta) {
root -= ((root ** 2) - number) / (2 * root);
}
return Math.round(root * (10 ** tolerance)) / (10 ** tolerance);
}
Is power of two implementation
function isPowerOfTwoBitwise(number) {
if (number < 1) return false
return (number & (number - 1)) === 0;
}
Integer partition implementation
function integerPartition(number) {
const partitionMatrix = Array(number + 1)
.fill(null)
.map(() => {
return Array(number + 1).fill(null)
})
for (let numberIndex = 1; numberIndex <= number; numberIndex++) {
partitionMatrix[0][numberIndex] = 0
}
for (let summandIndex = 0; summandIndex <= number; summandIndex++) {
partitionMatrix[summandIndex][0] = 1
}
for (let summandIndex = 1; summandIndex <= number; summandIndex++) {
for (let numberIndex = 1; numberIndex <= number; numberIndex++) {
if (summandIndex > numberIndex) {
partitionMatrix[summandIndex][numberIndex] =
partitionMatrix[summandIndex - 1][numberIndex]
} else {
const combosWithoutSummand =
partitionMatrix[summandIndex - 1][numberIndex]
const combosWithSummand =
partitionMatrix[summandIndex][numberIndex - summandIndex]
partitionMatrix[summandIndex][numberIndex] =
combosWithoutSummand + combosWithSummand
}
}
}
return partitionMatrix[number][number]
}
Power implementation
function power(base, power) {
if (power === 0) return 1
if (power % 2 === 0) {
const multiplier = fastPowering(base, power / 2)
return multiplier * multiplier
}
const multiplier = fastPowering(base, Math.floor(power / 2))
return multiplier * multiplier * base
}
Combinations implementation
// combinations without repetition
function comb(n, r) {
if (n < r) return 0;
let res = 1;
if (n - r < r) r = n - r;
for (let i = n, j = 1; i >= 1 && j <= r; --i, ++j) {
res = res * i;
}
for (let i = r; i >= 2; --i) {
res = res / i;
}
return res;
}
Bell number implementation
// Bell triangle method
function bellNumber(n) {
const bell = Array.from({ length: n + 1 }, () => Array(n + 1).fill(0))
bell[0][0] = 1
for (let i = 1; i <= n; i++) {
bell[i][0] = bell[i - 1][i - 1]
for (let j = 1; j <= i; j++)
bell[i][j] = bell[i - 1][j - 1] + bell[i][j - 1]
}
return bell[n][0]
}
Partition a set into k subsets implementation
// Returns count of different partitions of n
// elements in k subsets
function countP(n, k) {
const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0))
// Base cases
for (let i = 0; i <= n; i++) dp[i][0] = 0
for (let i = 0; i <= k; i++) dp[0][k] = 0
// Bottom up
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= k; j++) {
if (j == 1 || i == j) dp[i][j] = 1
else dp[i][j] = j * dp[i - 1][j] + dp[i - 1][j - 1]
}
}
return dp[n][k]
}
TreeSet implementation
class TreeSet {
constructor(comparator) {
this.length = 0
this.elements = []
if (comparator) this.comparator = comparator
else this.comparator = (a, b) => a > b ? 1 : (a < b ? -1 : 0)
}
size() {
return this.elements.length
}
last() {
return this.elements[this.length - 1]
}
first() {
return this.elements[0]
}
isEmpty() {
return this.size() === 0
}
pollLast() {
if (this.length > 0) {
this.length--
return this.elements.splice(this.length, 1)
}
return null
}
pollFirst() {
if (this.length > 0) {
this.length--
return this.elements.splice(0, 1)
}
return null
}
add(element) {
let index = this.binarySearch(element)
if(index >= 0) return
index = -index - 1
this.elements.splice(index, 0, element)
this.length++
}
/**
* Performs a binary search of value in array
* @param {number[]} array - Array in which value will be searched. It must be sorted.
* @param {number} value - Value to search in array
* @return {number} If value is found, returns its index in array. Otherwise, returns
* a negative number indicating where the value should be inserted: -(index + 1)
*/
binarySearch(value) {
let low = 0
let high = this.elements.length - 1
while (low <= high) {
let mid = low + ((high - low) >>> 1)
let midValue = this.elements[mid]
let cmp = this.comparator(midValue, value)
if (cmp < 0) low = mid + 1
else if (cmp > 0) high = mid - 1
else return mid
}
return -(low + 1)
}
}
Red black tree implementation
class Comparator {
constructor(compareFunction) {
this.compare = compareFunction || Comparator.defaultCompareFunction
}
static defaultCompareFunction(a, b) {
if (a === b) {
return 0
}
return a < b ? -1 : 1
}
equal(a, b) {
return this.compare(a, b) === 0
}
lessThan(a, b) {
return this.compare(a, b) < 0
}
greaterThan(a, b) {
return this.compare(a, b) > 0
}
lessThanOrEqual(a, b) {
return this.lessThan(a, b) || this.equal(a, b)
}
greaterThanOrEqual(a, b) {
return this.greaterThan(a, b) || this.equal(a, b)
}
reverse() {
const compareOriginal = this.compare
this.compare = (a, b) => compareOriginal(b, a)
}
}
class LinkedListNode {
constructor(value, next = null) {
this.value = value
this.next = next
}
toString(callback) {
return callback ? callback(this.value) : `${this.value}`
}
}
class LinkedList {
constructor(comparatorFunction) {
this.head = null
this.tail = null
this.compare = new Comparator(comparatorFunction)
}
prepend(value) {
const newNode = new LinkedListNode(value, this.head)
this.head = newNode
if (!this.tail) {
this.tail = newNode
}
return this
}
append(value) {
const newNode = new LinkedListNode(value)
if (!this.head) {
this.head = newNode
this.tail = newNode
return this
}
this.tail.next = newNode
this.tail = newNode
return this
}
delete(value) {
if (!this.head) {
return null
}
let deletedNode = null
while (this.head && this.compare.equal(this.head.value, value)) {
deletedNode = this.head
this.head = this.head.next
}
let currentNode = this.head
if (currentNode !== null) {
while (currentNode.next) {
if (this.compare.equal(currentNode.next.value, value)) {
deletedNode = currentNode.next
currentNode.next = currentNode.next.next
} else {
currentNode = currentNode.next
}
}
}
if (this.compare.equal(this.tail.value, value)) {
this.tail = currentNode
}
return deletedNode
}
find({ value = undefined, callback = undefined }) {
if (!this.head) {
return null
}
let currentNode = this.head
while (currentNode) {
if (callback && callback(currentNode.value)) {
return currentNode
}
if (value !== undefined && this.compare.equal(currentNode.value, value)) {
return currentNode
}
currentNode = currentNode.next
}
return null
}
deleteTail() {
const deletedTail = this.tail
if (this.head === this.tail) {
this.head = null
this.tail = null
return deletedTail
}
let currentNode = this.head
while (currentNode.next) {
if (!currentNode.next.next) {
currentNode.next = null
} else {
currentNode = currentNode.next
}
}
this.tail = currentNode
return deletedTail
}
deleteHead() {
if (!this.head) {
return null
}
const deletedHead = this.head
if (this.head.next) {
this.head = this.head.next
} else {
this.head = null
this.tail = null
}
return deletedHead
}
fromArray(values) {
values.forEach((value) => this.append(value))
return this
}
toArray() {
const nodes = []
let currentNode = this.head
while (currentNode) {
nodes.push(currentNode)
currentNode = currentNode.next
}
return nodes
}
toString(callback) {
return this.toArray()
.map((node) => node.toString(callback))
.toString()
}
reverse() {
let currNode = this.head
let prevNode = null
let nextNode = null
while (currNode) {
nextNode = currNode.next
currNode.next = prevNode
prevNode = currNode
currNode = nextNode
}
this.tail = this.head
this.head = prevNode
return this
}
}
const defaultHashTableSize = 32
class HashTable {
constructor(hashTableSize = defaultHashTableSize) {
this.buckets = Array(hashTableSize)
.fill(null)
.map(() => new LinkedList())
this.keys = {}
}
hash(key) {
const hash = Array.from(key).reduce(
(hashAccumulator, keySymbol) => hashAccumulator + keySymbol.charCodeAt(0),
0
)
return hash % this.buckets.length
}
set(key, value) {
const keyHash = this.hash(key)
this.keys[key] = keyHash
const bucketLinkedList = this.buckets[keyHash]
const node = bucketLinkedList.find({
callback: (nodeValue) => nodeValue.key === key,
})
if (!node) {
bucketLinkedList.append({ key, value })
} else {
node.value.value = value
}
}
delete(key) {
const keyHash = this.hash(key)
delete this.keys[key]
const bucketLinkedList = this.buckets[keyHash]
const node = bucketLinkedList.find({
callback: (nodeValue) => nodeValue.key === key,
})
if (node) {
return bucketLinkedList.delete(node.value)
}
return null
}
get(key) {
const bucketLinkedList = this.buckets[this.hash(key)]
const node = bucketLinkedList.find({
callback: (nodeValue) => nodeValue.key === key,
})
return node ? node.value.value : undefined
}
has(key) {
return Object.hasOwnProperty.call(this.keys, key)
}
getKeys() {
return Object.keys(this.keys)
}
}
class BinaryTreeNode {
constructor(value = null) {
this.left = null
this.right = null
this.parent = null
this.value = value
this.meta = new HashTable()
this.nodeComparator = new Comparator()
}
get leftHeight() {
if (!this.left) {
return 0
}
return this.left.height + 1
}
get rightHeight() {
if (!this.right) {
return 0
}
return this.right.height + 1
}
get height() {
return Math.max(this.leftHeight, this.rightHeight)
}
get balanceFactor() {
return this.leftHeight - this.rightHeight
}
get uncle() {
if (!this.parent) {
return undefined
}
if (!this.parent.parent) {
return undefined
}
if (!this.parent.parent.left || !this.parent.parent.right) {
return undefined
}
if (this.nodeComparator.equal(this.parent, this.parent.parent.left)) {
return this.parent.parent.right
}
return this.parent.parent.left
}
setValue(value) {
this.value = value
return this
}
setLeft(node) {
if (this.left) {
this.left.parent = null
}
this.left = node
if (this.left) {
this.left.parent = this
}
return this
}
setRight(node) {
if (this.right) {
this.right.parent = null
}
this.right = node
if (node) {
this.right.parent = this
}
return this
}
removeChild(nodeToRemove) {
if (this.left && this.nodeComparator.equal(this.left, nodeToRemove)) {
this.left = null
return true
}
if (this.right && this.nodeComparator.equal(this.right, nodeToRemove)) {
this.right = null
return true
}
return false
}
replaceChild(nodeToReplace, replacementNode) {
if (!nodeToReplace || !replacementNode) {
return false
}
if (this.left && this.nodeComparator.equal(this.left, nodeToReplace)) {
this.left = replacementNode
return true
}
if (this.right && this.nodeComparator.equal(this.right, nodeToReplace)) {
this.right = replacementNode
return true
}
return false
}
static copyNode(sourceNode, targetNode) {
targetNode.setValue(sourceNode.value)
targetNode.setLeft(sourceNode.left)
targetNode.setRight(sourceNode.right)
}
traverseInOrder() {
let traverse = []
if (this.left) {
traverse = traverse.concat(this.left.traverseInOrder())
}
traverse.push(this.value)
if (this.right) {
traverse = traverse.concat(this.right.traverseInOrder())
}
return traverse
}
toString() {
return this.traverseInOrder().toString()
}
}
class BinarySearchTreeNode extends BinaryTreeNode {
constructor(value = null, compareFunction = undefined) {
super(value)
this.compareFunction = compareFunction
this.nodeValueComparator = new Comparator(compareFunction)
}
insert(value) {
if (this.nodeValueComparator.equal(this.value, null)) {
this.value = value
return this
}
if (this.nodeValueComparator.lessThan(value, this.value)) {
if (this.left) {
return this.left.insert(value)
}
const newNode = new BinarySearchTreeNode(value, this.compareFunction)
this.setLeft(newNode)
return newNode
}
if (this.nodeValueComparator.greaterThan(value, this.value)) {
if (this.right) {
return this.right.insert(value)
}
const newNode = new BinarySearchTreeNode(value, this.compareFunction)
this.setRight(newNode)
return newNode
}
return this
}
find(value) {
if (this.nodeValueComparator.equal(this.value, value)) {
return this
}
if (this.nodeValueComparator.lessThan(value, this.value) && this.left) {
return this.left.find(value)
}
if (this.nodeValueComparator.greaterThan(value, this.value) && this.right) {
return this.right.find(value)
}
return null
}
contains(value) {
return !!this.find(value)
}
remove(value) {
const nodeToRemove = this.find(value)
if (!nodeToRemove) {
throw new Error('Item not found in the tree')
}
const { parent } = nodeToRemove
if (!nodeToRemove.left && !nodeToRemove.right) {
if (parent) {
parent.removeChild(nodeToRemove)
} else {
nodeToRemove.setValue(undefined)
}
} else if (nodeToRemove.left && nodeToRemove.right) {
const nextBiggerNode = nodeToRemove.right.findMin()
if (!this.nodeComparator.equal(nextBiggerNode, nodeToRemove.right)) {
this.remove(nextBiggerNode.value)
nodeToRemove.setValue(nextBiggerNode.value)
} else {
nodeToRemove.setValue(nodeToRemove.right.value)
nodeToRemove.setRight(nodeToRemove.right.right)
}
} else {
const childNode = nodeToRemove.left || nodeToRemove.right
if (parent) {
parent.replaceChild(nodeToRemove, childNode)
} else {
BinaryTreeNode.copyNode(childNode, nodeToRemove)
}
}
nodeToRemove.parent = null
return true
}
findMin() {
if (!this.left) {
return this
}
return this.left.findMin()
}
}
class BinarySearchTree {
constructor(nodeValueCompareFunction) {
this.root = new BinarySearchTreeNode(null, nodeValueCompareFunction);
this.nodeComparator = this.root.nodeComparator;
}
insert(value) {
return this.root.insert(value);
}
contains(value) {
return this.root.contains(value);
}
remove(value) {
return this.root.remove(value);
}
toString() {
return this.root.toString();
}
}
const RED_BLACK_TREE_COLORS = {
red: 'red',
black: 'black',
}
const COLOR_PROP_NAME = 'color'
class RedBlackTree extends BinarySearchTree {
insert(value) {
const insertedNode = super.insert(value)
if (this.nodeComparator.equal(insertedNode, this.root)) {
this.makeNodeBlack(insertedNode)
} else {
this.makeNodeRed(insertedNode)
}
this.balance(insertedNode)
return insertedNode
}
remove(value) {
throw new Error(
`Can't remove ${value}. Remove method is not implemented yet`
)
}
balance(node) {
if (this.nodeComparator.equal(node, this.root)) {
return
}
if (this.isNodeBlack(node.parent)) {
return
}
const grandParent = node.parent.parent
if (node.uncle && this.isNodeRed(node.uncle)) {
this.makeNodeBlack(node.uncle)
this.makeNodeBlack(node.parent)
if (!this.nodeComparator.equal(grandParent, this.root)) {
this.makeNodeRed(grandParent)
} else {
return
}
this.balance(grandParent)
} else if (!node.uncle || this.isNodeBlack(node.uncle)) {
if (grandParent) {
let newGrandParent
if (this.nodeComparator.equal(grandParent.left, node.parent)) {
if (this.nodeComparator.equal(node.parent.left, node)) {
newGrandParent = this.leftLeftRotation(grandParent)
} else {
newGrandParent = this.leftRightRotation(grandParent)
}
} else {
if (this.nodeComparator.equal(node.parent.right, node)) {
newGrandParent = this.rightRightRotation(grandParent)
} else {
newGrandParent = this.rightLeftRotation(grandParent)
}
}
if (newGrandParent && newGrandParent.parent === null) {
this.root = newGrandParent
this.makeNodeBlack(this.root)
}
this.balance(newGrandParent)
}
}
}
leftLeftRotation(grandParentNode) {
const grandGrandParent = grandParentNode.parent
let grandParentNodeIsLeft
if (grandGrandParent) {
grandParentNodeIsLeft = this.nodeComparator.equal(
grandGrandParent.left,
grandParentNode
)
}
const parentNode = grandParentNode.left
const parentRightNode = parentNode.right
parentNode.setRight(grandParentNode)
grandParentNode.setLeft(parentRightNode)
if (grandGrandParent) {
if (grandParentNodeIsLeft) {
grandGrandParent.setLeft(parentNode)
} else {
grandGrandParent.setRight(parentNode)
}
} else {
parentNode.parent = null
}
this.swapNodeColors(parentNode, grandParentNode)
return parentNode
}
leftRightRotation(grandParentNode) {
const parentNode = grandParentNode.left
const childNode = parentNode.right
const childLeftNode = childNode.left
childNode.setLeft(parentNode)
parentNode.setRight(childLeftNode)
grandParentNode.setLeft(childNode)
return this.leftLeftRotation(grandParentNode)
}
rightRightRotation(grandParentNode) {
const grandGrandParent = grandParentNode.parent
let grandParentNodeIsLeft
if (grandGrandParent) {
grandParentNodeIsLeft = this.nodeComparator.equal(
grandGrandParent.left,
grandParentNode
)
}
const parentNode = grandParentNode.right
const parentLeftNode = parentNode.left
parentNode.setLeft(grandParentNode)
grandParentNode.setRight(parentLeftNode)
if (grandGrandParent) {
if (grandParentNodeIsLeft) {
grandGrandParent.setLeft(parentNode)
} else {
grandGrandParent.setRight(parentNode)
}
} else {
parentNode.parent = null
}
this.swapNodeColors(parentNode, grandParentNode)
return parentNode
}
rightLeftRotation(grandParentNode) {
const parentNode = grandParentNode.right
const childNode = parentNode.left
const childRightNode = childNode.right
childNode.setRight(parentNode)
parentNode.setLeft(childRightNode)
grandParentNode.setRight(childNode)
return this.rightRightRotation(grandParentNode)
}
makeNodeRed(node) {
node.meta.set(COLOR_PROP_NAME, RED_BLACK_TREE_COLORS.red)
return node
}
makeNodeBlack(node) {
node.meta.set(COLOR_PROP_NAME, RED_BLACK_TREE_COLORS.black)
return node
}
isNodeRed(node) {
return node.meta.get(COLOR_PROP_NAME) === RED_BLACK_TREE_COLORS.red
}
isNodeBlack(node) {
return node.meta.get(COLOR_PROP_NAME) === RED_BLACK_TREE_COLORS.black
}
isNodeColored(node) {
return this.isNodeRed(node) || this.isNodeBlack(node)
}
swapNodeColors(firstNode, secondNode) {
const firstColor = firstNode.meta.get(COLOR_PROP_NAME)
const secondColor = secondNode.meta.get(COLOR_PROP_NAME)
firstNode.meta.set(COLOR_PROP_NAME, secondColor)
secondNode.meta.set(COLOR_PROP_NAME, firstColor)
}
}
TreeMap implementation
///////////////////////////////////////////////////// Template /////////////////////////////////////////////////////////////////
function Bisect() {
return { insort_right, insort_left, bisect_left, bisect_right }
function insort_right(a, x, lo = 0, hi = null) {
lo = bisect_right(a, x, lo, hi);
a.splice(lo, 0, x);
}
function bisect_right(a, x, lo = 0, hi = null) { // > upper_bound
if (lo < 0) throw new Error('lo must be non-negative');
if (hi == null) hi = a.length;
while (lo < hi) {
let mid = parseInt((lo + hi) / 2);
a[mid] > x ? hi = mid : lo = mid + 1;
}
return lo;
}
function insort_left(a, x, lo = 0, hi = null) {
lo = bisect_left(a, x, lo, hi);
a.splice(lo, 0, x);
}
function bisect_left(a, x, lo = 0, hi = null) { // >= lower_bound
if (lo < 0) throw new Error('lo must be non-negative');
if (hi == null) hi = a.length;
while (lo < hi) {
let mid = parseInt((lo + hi) / 2);
a[mid] < x ? lo = mid + 1 : hi = mid;
}
return lo;
}
}
/*
usage:
let ts = new TreeMap();
let ts = new TreeMap([[3, 1], [7, 1], [7, 2], [1, 1], [3, 2]]); // Map { 1 => 1, 3 => 2, 7 => 2 } (console.log(ts.show()))
*/
function TreeMap(g) {
let ts = [], m = new Map(), bisect = new Bisect();
initialize();
return { put, ceilingKey, higherKey, lowerKey, floorKey, ceilingEntry, higherEntry, lowerEntry, floorEntry, remove, contains, size, clear, show };
function initialize() {
if (g) {
for (const [k, v] of g) {
if (!m.has(k)) bisect.insort_right(ts, k);
m.set(k, v);
}
}
}
function put(k, v) {
if (!m.has(k)) bisect.insort_right(ts, k); // ts has no duplicates/unique key
m.set(k, v); // update key with most recent value
}
function ceilingKey(e) { // >= lower_bound
let idx = bisect.bisect_right(ts, e);
let res = ts[idx - 1] == e ? e : ts[bisect.bisect_right(ts, e)];
return res == undefined ? null : res;
}
function higherKey(e) { // > upper_bound
let idx = bisect.bisect_right(ts, e);
let res = ts[idx] > e ? ts[idx] : ts[bisect.bisect_right(ts, e) + 1];
return res == undefined ? null : res;
}
function floorKey(e) { // <=
let idx = bisect.bisect_left(ts, e);
let res = ts[idx] == e ? e : ts[bisect.bisect_left(ts, e) - 1];
return res == undefined ? null : res;
}
function lowerKey(e) { // <
let idx = bisect.bisect_left(ts, e);
let res = ts[idx] < e ? ts[idx] : ts[bisect.bisect_left(ts, e) - 1];
return res == undefined ? null : res;
}
function data(k) {
return k == null ? null : { key: k, value: m.get(k) }
}
function ceilingEntry(k) {
return data(ceilingKey(k));
}
function higherEntry(k) {
return data(higherKey(k));
}
function floorEntry(k) {
return data(floorKey(k));
}
function lowerEntry(k) {
return data(lowerKey(k));
}
function remove(e) {
let idx = bisect.bisect_left(ts, e);
if (ts[idx] == e) ts.splice(idx, 1);
m.delete(e);
}
function contains(e) {
return m.has(e);
}
function size() {
return ts.length;
}
function clear() {
ts = [];
m.clear();
}
function show() {
let res = new Map();
for (const x of ts) res.set(x, m.get(x));
return res;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////