UNPKG

@iabtcf/core

Version:

Ensures consistent encoding and decoding of TC Signals for the iab. Transparency and Consent Framework (TCF).

268 lines (267 loc) 9.96 kB
import { Cloneable } from '../Cloneable.js'; export class BinarySearchTree extends Cloneable { root = null; getRoot() { return this.root; } isEmpty() { // if root is undefined or null then by definition this is empty return !(this.root); } add(value) { // create new node object const node = { value: value, left: null, right: null, }; let current; // first item? if (this.isEmpty()) { this.root = node; } else { // start at the root current = this.root; // infinite loop, figure out where to put it while (true) { // if the value is less than current value; go left if (value < current.value) { // if it's empty, we can insert if (current.left === null) { // insert on the left current.left = node; // our work is done here break; } else { /** * if there's something there already, we'll reset the pointer and * wait for the next loop to do something ie. keep traversing */ current = current.left; } } else if (value > current.value) { // if the value is greater than our current value; go right if (current.right === null) { // there's nothing to the right, so put it here current.right = node; break; } else { /** * if there's something there already, we'll reset the pointer and * wait for the next loop to do something ie. keep traversing */ current = current.right; } } else { /** * If it's neither greater than the right or less than the right then * it is equal to the current nodes value. In that case we won't do * anything with it because we will only insert unique values. */ break; } } } } /** * performs Morris in-order traversal * @return {number[]} sorted array */ get() { const retr = []; let current = this.root; while (current) { if (!current.left) { retr.push(current.value); // if there is no left child, visit current node current = current.right; // then we go the right branch } else { // find the right most leaf of root.left node. let pre = current.left; // when pre.right == null, it means we go to the right most leaf // when pre.right == current, it means the right most leaf has been visited in the last round while (pre.right && pre.right != current) { pre = pre.right; } // this means the pre.right has been set, it's time to go to current node if (pre.right == current) { pre.right = null; // means the current node is pointed by left right most child // the left branch has been visited, it's time to push the current node retr.push(current.value); current = current.right; } else { // the fist time to visit the pre node, make its right child point to current node pre.right = current; current = current.left; } } } return retr; } contains(value) { let retr = false; let current = this.root; while (current) { if (current.value === value) { retr = true; break; } else if (value > current.value) { current = current.right; } else if (value < current.value) { current = current.left; } } return retr; } min(current = this.root) { let retr; while (current) { if (current.left) { current = current.left; } else { retr = current.value; current = null; } } return retr; } max(current = this.root) { let retr; while (current) { if (current.right) { current = current.right; } else { retr = current.value; current = null; } } return retr; } remove(value, current = this.root) { // we start at the root, so the parent is null let parent = null; let parentSide = 'left'; while (current) { if (value < current.value) { // set our parent to the current value parent = current; // value is less than current value, so go left current = current.left; parentSide = 'left'; } else if (value > current.value) { // set our parent to the current value parent = current; // value is greater than current value, so go right current = current.right; parentSide = 'right'; } else { /** * if it's neither greater than or less than, then it's equal so BINGO! * we've found it * * If we have children, we've got to figure out what to do with * them once we are no longer around... Woah, code is like real * life... * * There are three cases we care about when it comes to this removal * process: * * 1. No children -- If not children we just delete an do nothing * else, no harm no foul. * * 2. One child -- Just link the parent's link to current to the * child. * * 3. Two children -- Find the minimum value from the right subtree * replace us with the minimum value and of course remove that * minimum value from the right stubtree */ if (!current.left && !current.right) { // case 1 there are no children easy peasy lemon squeezy if (parent) { parent[parentSide] = null; } else { this.root = null; } } else if (!current.left) { // no left side only right, so link right if (parent) { parent[parentSide] = current.right; } else { this.root = current.right; } } else if (!current.right) { // no right side only left, so link left if (parent) { parent[parentSide] = current.left; } else { this.root = current.left; } } else { /** * case 3 just like real life, if you delete a parent the more kids * that parent has the more complicated things get... in this case we * have two children. We're gonna have to figure out who goes where. */ const minVal = this.min(current.right); // little bit of recursion... this.remove(minVal, current.right); current.value = minVal; } current = null; } } } /** * Build Binary Search Tree from the ordered number array. * The depth of the tree will be the `log2` of the array length. * @param {number[]} values number array in ascending order * @return {BinarySearchTree} Binary Search Tree */ static build(values) { if (!values || values.length === 0) { return null; } else if (values.length === 1) { const tree = new BinarySearchTree(); tree.add(values[0]); return tree; } else { const rootIndex = values.length >> 1; const tree = new BinarySearchTree(); tree.add(values[rootIndex]); const root = tree.getRoot(); if (root) { if (rootIndex + 1 < values.length) { const rightTree = BinarySearchTree.build(values.slice(rootIndex + 1)); root.right = rightTree ? rightTree.getRoot() : null; } if (rootIndex - 1 > 0) { const leftTree = BinarySearchTree.build(values.slice(0, rootIndex - 1)); root.left = leftTree ? leftTree.getRoot() : null; } } return tree; } } }