From d7f428f9947b78d380010a70e193c5dccd9598aa Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Mon, 9 Aug 2021 01:25:49 +0200 Subject: [PATCH 1/8] fix lowerBound & upperBound --- README.md | 17 ++++++---- src/avlTree.d.ts | 6 ++-- src/binarySearchTree.d.ts | 6 ++-- src/binarySearchTree.js | 62 ++++++++++++++++++++--------------- test/binarySearchTree.test.js | 24 +++++++++++++- 5 files changed, 75 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 0a481b3..2e46864 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ Binary Search Tree & AVL Tree (Self Balancing Tree) implementation in javascript * [.find(key)](#findkey) * [.min()](#min) * [.max()](#max) - * [.lowerBound(k)](#lowerboundk) - * [.upperBound(k)](#upperboundk) + * [.lowerBound(k[, includeEqual])](#lowerboundk-includeEqual) + * [.upperBound(k[, includeEqual])](#upperboundk-includeEqual) * [.root()](#root) * [.count()](#count) * [.traverseInOrder(cb)](#traverseinordercb) @@ -219,8 +219,8 @@ console.log(max.getKey()); // 90 console.log(max.getValue()); // v4 ``` -### .lowerBound(k) -finds the node with the biggest key less or equal a given value k. +### .lowerBound(k[, includeEqual]) +finds the node with the biggest key less or equal a given value k. You can eliminate equal keys by passing second param as false. @@ -236,12 +236,13 @@ finds the node with the biggest key less or equal a given value k.
```js -console.log(bst.lowerBound(60).getKey()); // 50 +console.log(bst.lowerBound(60).getKey()); // 60 +console.log(bst.lowerBound(60, false).getKey()); // 50 console.log(bst.lowerBound(10)); // null ``` -### .upperBound(k) -finds the node with the smallest key bigger than a given value k. +### .upperBound(k[, includeEqual]) +finds the node with the smallest key bigger or equal a given value k. You can eliminate equal keys by passing second param as false. @@ -258,6 +259,8 @@ finds the node with the smallest key bigger than a given value k. ```js console.log(bst.upperBound(75).getKey()); // 80 +console.log(bst.upperBound(80).getKey()); // 80 +console.log(bst.upperBound(80, false).getKey()); // 90 console.log(bst.upperBound(110)); // null ``` diff --git a/src/avlTree.d.ts b/src/avlTree.d.ts index a750723..272e92a 100644 --- a/src/avlTree.d.ts +++ b/src/avlTree.d.ts @@ -2,12 +2,12 @@ import { BinarySearchTree } from './binarySearchTree'; import { AvlTreeNode } from './avlTreeNode'; export class AvlTree extends BinarySearchTree { - insert(key: T, value: U): AvlTreeNode; + insert(key: T, value?: U): AvlTreeNode; find(key: T): AvlTreeNode; max(node?: AvlTreeNode): AvlTreeNode; min(node?: AvlTreeNode): AvlTreeNode; - lowerBound(k: T, node?: AvlTreeNode): AvlTreeNode; - upperBound(k: T, node?: AvlTreeNode): AvlTreeNode; + lowerBound(k: T, includeEqual?: boolean): AvlTreeNode; + upperBound(k: T, includeEqual?: boolean): AvlTreeNode; root(): AvlTreeNode; traverseInOrder(cb: (node: AvlTreeNode) => void): void; traversePreOrder(cb: (node: AvlTreeNode) => void): void; diff --git a/src/binarySearchTree.d.ts b/src/binarySearchTree.d.ts index dba0b5e..487151e 100644 --- a/src/binarySearchTree.d.ts +++ b/src/binarySearchTree.d.ts @@ -1,13 +1,13 @@ import { BinarySearchTreeNode } from './binarySearchTreeNode'; export class BinarySearchTree { - insert(key: T, value: U): BinarySearchTreeNode; + insert(key: T, value?: U): BinarySearchTreeNode; has(key: T): boolean; find(key: T): BinarySearchTreeNode; max(node?: BinarySearchTreeNode): BinarySearchTreeNode; min(node?: BinarySearchTreeNode): BinarySearchTreeNode; - lowerBound(k: T, node?: BinarySearchTreeNode): BinarySearchTreeNode; - upperBound(k: T, node?: BinarySearchTreeNode): BinarySearchTreeNode; + lowerBound(k: T, includeEqual?: boolean): BinarySearchTreeNode; + upperBound(k: T, includeEqual?: boolean): BinarySearchTreeNode; root(): BinarySearchTreeNode; count(): number; remove(k: T): boolean; diff --git a/src/binarySearchTree.js b/src/binarySearchTree.js index 5cacd58..e4ec6b4 100644 --- a/src/binarySearchTree.js +++ b/src/binarySearchTree.js @@ -146,48 +146,58 @@ class BinarySearchTree { * Returns the node with the biggest key less or equal to k * @public * @param {number|string} k + * @param {boolean} includeEqual * @return {BinarySearchTreeNode|null} */ - lowerBound(k, current = this._root) { - if (current === null) { - return null; - } + lowerBound(k, includeEqual = true) { + let lowerBound = null; - if (current.getKey() === k) { - return current; - } + const lowerBoundRecursive = (current) => { + if (current === null) { + return lowerBound; + } - if (current.getKey() > k) { - return this.lowerBound(k, current.getLeft()); - } + const currentKey = current.getKey(); + if (currentKey < k || (includeEqual && currentKey === k)) { + if (lowerBound === null || lowerBound.getKey() <= currentKey) { + lowerBound = current; + } + return lowerBoundRecursive(current.getRight()); + } - if (current.hasRight() && current.getRight().getKey() <= k) { - return this.lowerBound(k, current.getRight()); - } + return lowerBoundRecursive(current.getLeft()); + }; - return current; + return lowerBoundRecursive(this._root); } /** - * Returns the node with the smallest key bigger than k + * Returns the node with the smallest key bigger or equal k * @public * @param {number|string} k + * @param {boolean} includeEqual * @return {BinarySearchTreeNode|null} */ - upperBound(k, current = this._root) { - if (current === null) { - return null; - } + upperBound(k, includeEqual = true) { + let upperBound = null; - if (current.getKey() <= k) { - return this.upperBound(k, current.getRight()); - } + const upperBoundRecursive = (current) => { + if (current === null) { + return upperBound; + } - if (current.hasLeft() && current.getLeft().getKey() > k) { - return this.upperBound(k, current.getLeft()); - } + const currentKey = current.getKey(); + if (currentKey > k || (includeEqual && currentKey === k)) { + if (upperBound === null || upperBound.getKey() >= currentKey) { + upperBound = current; + } + return upperBoundRecursive(current.getLeft()); + } - return current; + return upperBoundRecursive(current.getRight()); + }; + + return upperBoundRecursive(this._root); } /** diff --git a/test/binarySearchTree.test.js b/test/binarySearchTree.test.js index cb48568..2b82a07 100644 --- a/test/binarySearchTree.test.js +++ b/test/binarySearchTree.test.js @@ -82,22 +82,44 @@ describe('BinarySearchTree tests', () => { describe('.lowerBound(k)', () => { it('gets the node with biggest key less or equal k', () => { - expect(bst.lowerBound(60).getKey()).to.equal(50); + expect(bst.lowerBound(60).getKey()).to.equal(60); + expect(bst.lowerBound(60, false).getKey()).to.equal(50); }); it('returns null when k is less than all tree keys', () => { expect(bst.lowerBound(10)).to.equal(null); }); + + it('returns the biggest lower bound of multiple lower bounds', () => { + const lowerBst = new BinarySearchTree(); + lowerBst.insert(20); + lowerBst.insert(7); + lowerBst.insert(15); + lowerBst.insert(9); + expect(lowerBst.lowerBound(10).getKey()).to.equal(9); + }); }); describe('.upperBound(k)', () => { it('gets the node with smallest key bigger than k', () => { expect(bst.upperBound(75).getKey()).to.equal(80); + expect(bst.upperBound(80).getKey()).to.equal(80); + expect(bst.upperBound(80, false).getKey()).to.equal(90); }); it('returns null when k is bigger than all tree keys', () => { expect(bst.upperBound(110)).to.equal(null); }); + + it('returns the smallest upper bound of multiple upper bounds', () => { + const upperBst = new BinarySearchTree(); + upperBst.insert(-133195046); + upperBst.insert(-49109668); + upperBst.insert(115062875); + upperBst.insert(-38206732); + upperBst.insert(49311742); + expect(upperBst.upperBound(49303013).getKey()).to.equal(49311742); + }); }); describe('.traverseInOrder(cb)', () => { From 91d81f80f29928e5e1e992e4feb66aba51721ce0 Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Mon, 9 Aug 2021 01:33:03 +0200 Subject: [PATCH 2/8] add floor & ceil delegates --- src/avlTree.d.ts | 2 ++ src/binarySearchTree.d.ts | 2 ++ src/binarySearchTree.js | 22 ++++++++++++++++++++++ test/binarySearchTree.test.js | 4 ++-- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/avlTree.d.ts b/src/avlTree.d.ts index 272e92a..3bed1cd 100644 --- a/src/avlTree.d.ts +++ b/src/avlTree.d.ts @@ -7,7 +7,9 @@ export class AvlTree extends BinarySearc max(node?: AvlTreeNode): AvlTreeNode; min(node?: AvlTreeNode): AvlTreeNode; lowerBound(k: T, includeEqual?: boolean): AvlTreeNode; + floor(k: T, includeEqual?: boolean): AvlTreeNode; upperBound(k: T, includeEqual?: boolean): AvlTreeNode; + ceil(k: T, includeEqual?: boolean): AvlTreeNode; root(): AvlTreeNode; traverseInOrder(cb: (node: AvlTreeNode) => void): void; traversePreOrder(cb: (node: AvlTreeNode) => void): void; diff --git a/src/binarySearchTree.d.ts b/src/binarySearchTree.d.ts index 487151e..9c981b0 100644 --- a/src/binarySearchTree.d.ts +++ b/src/binarySearchTree.d.ts @@ -7,7 +7,9 @@ export class BinarySearchTree { max(node?: BinarySearchTreeNode): BinarySearchTreeNode; min(node?: BinarySearchTreeNode): BinarySearchTreeNode; lowerBound(k: T, includeEqual?: boolean): BinarySearchTreeNode; + floor(k: T, includeEqual?: boolean): BinarySearchTreeNode; upperBound(k: T, includeEqual?: boolean): BinarySearchTreeNode; + ceil(k: T, includeEqual?: boolean): BinarySearchTreeNode; root(): BinarySearchTreeNode; count(): number; remove(k: T): boolean; diff --git a/src/binarySearchTree.js b/src/binarySearchTree.js index e4ec6b4..586dd7e 100644 --- a/src/binarySearchTree.js +++ b/src/binarySearchTree.js @@ -171,6 +171,17 @@ class BinarySearchTree { return lowerBoundRecursive(this._root); } + /** + * delegate to lowerBound + * @public + * @param {number|string} k + * @param {boolean} includeEqual + * @return {BinarySearchTreeNode|null} + */ + floor(k, includeEqual = true) { + return this.lowerBound(k, includeEqual); + } + /** * Returns the node with the smallest key bigger or equal k * @public @@ -200,6 +211,17 @@ class BinarySearchTree { return upperBoundRecursive(this._root); } + /** + * delegate to upperBound + * @public + * @param {number|string} k + * @param {boolean} includeEqual + * @return {BinarySearchTreeNode|null} + */ + ceil(k, includeEqual = true) { + return this.upperBound(k, includeEqual); + } + /** * Returns the root node * @public diff --git a/test/binarySearchTree.test.js b/test/binarySearchTree.test.js index 2b82a07..83d903f 100644 --- a/test/binarySearchTree.test.js +++ b/test/binarySearchTree.test.js @@ -96,7 +96,7 @@ describe('BinarySearchTree tests', () => { lowerBst.insert(7); lowerBst.insert(15); lowerBst.insert(9); - expect(lowerBst.lowerBound(10).getKey()).to.equal(9); + expect(lowerBst.floor(10).getKey()).to.equal(9); }); }); @@ -118,7 +118,7 @@ describe('BinarySearchTree tests', () => { upperBst.insert(115062875); upperBst.insert(-38206732); upperBst.insert(49311742); - expect(upperBst.upperBound(49303013).getKey()).to.equal(49311742); + expect(upperBst.ceil(49303013).getKey()).to.equal(49311742); }); }); From c2a278acbb68796a979be5d5dc92cb748d9d2036 Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Mon, 9 Aug 2021 01:38:11 +0200 Subject: [PATCH 3/8] update --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2e46864..5487caf 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ Binary Search Tree & AVL Tree (Self Balancing Tree) implementation in javascript * [.find(key)](#findkey) * [.min()](#min) * [.max()](#max) - * [.lowerBound(k[, includeEqual])](#lowerboundk-includeEqual) - * [.upperBound(k[, includeEqual])](#upperboundk-includeEqual) + * [.lowerBound(k[, includeEqual]) (floor)](#lowerboundk-includeEqual-floor-k-includeEqual) + * [.upperBound(k[, includeEqual]) (ceil)](#upperboundk-includeEqual-ceil-k-includeEqual) * [.root()](#root) * [.count()](#count) * [.traverseInOrder(cb)](#traverseinordercb) @@ -219,7 +219,7 @@ console.log(max.getKey()); // 90 console.log(max.getValue()); // v4 ``` -### .lowerBound(k[, includeEqual]) +### .lowerBound(k[, includeEqual]) (.floor(k[, includeEqual])) finds the node with the biggest key less or equal a given value k. You can eliminate equal keys by passing second param as false.
@@ -241,7 +241,7 @@ console.log(bst.lowerBound(60, false).getKey()); // 50 console.log(bst.lowerBound(10)); // null ``` -### .upperBound(k[, includeEqual]) +### .upperBound(k[, includeEqual]) (.ceil(k[, includeEqual])) finds the node with the smallest key bigger or equal a given value k. You can eliminate equal keys by passing second param as false.
From 4dfb5f27c652ce2318fa9cd53c0117eefef83c8d Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Mon, 9 Aug 2021 01:39:33 +0200 Subject: [PATCH 4/8] update --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5487caf..e938bae 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ Binary Search Tree & AVL Tree (Self Balancing Tree) implementation in javascript * [.find(key)](#findkey) * [.min()](#min) * [.max()](#max) - * [.lowerBound(k[, includeEqual]) (floor)](#lowerboundk-includeEqual-floor-k-includeEqual) - * [.upperBound(k[, includeEqual]) (ceil)](#upperboundk-includeEqual-ceil-k-includeEqual) + * [.lowerBound(k[, includeEqual]) (floor)](#lowerboundk-includeEqual-floor) + * [.upperBound(k[, includeEqual]) (ceil)](#upperboundk-includeEqual-ceil) * [.root()](#root) * [.count()](#count) * [.traverseInOrder(cb)](#traverseinordercb) @@ -219,7 +219,7 @@ console.log(max.getKey()); // 90 console.log(max.getValue()); // v4 ``` -### .lowerBound(k[, includeEqual]) (.floor(k[, includeEqual])) +### .lowerBound(k[, includeEqual]) (.floor) finds the node with the biggest key less or equal a given value k. You can eliminate equal keys by passing second param as false.
@@ -241,7 +241,7 @@ console.log(bst.lowerBound(60, false).getKey()); // 50 console.log(bst.lowerBound(10)); // null ``` -### .upperBound(k[, includeEqual]) (.ceil(k[, includeEqual])) +### .upperBound(k[, includeEqual]) (.ceil) finds the node with the smallest key bigger or equal a given value k. You can eliminate equal keys by passing second param as false.
From 5a1436c7708adef6648e9fcfec75815c82802f41 Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Mon, 9 Aug 2021 01:40:48 +0200 Subject: [PATCH 5/8] update --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e938bae..8b8f874 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ console.log(max.getValue()); // v4 ``` ### .lowerBound(k[, includeEqual]) (.floor) -finds the node with the biggest key less or equal a given value k. You can eliminate equal keys by passing second param as false. +finds the node with the biggest key less or equal a given value k. You can eliminate equal keys by passing second param as false. `.floor` is a delegate to the same function.
@@ -242,7 +242,7 @@ console.log(bst.lowerBound(10)); // null ``` ### .upperBound(k[, includeEqual]) (.ceil) -finds the node with the smallest key bigger or equal a given value k. You can eliminate equal keys by passing second param as false. +finds the node with the smallest key bigger or equal a given value k. You can eliminate equal keys by passing second param as false. `.ceil` is a delegate to the same function.
From 6dde4e408cac0eeb4c9ec8a1ce10826eda31acc5 Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Mon, 9 Aug 2021 01:46:24 +0200 Subject: [PATCH 6/8] update --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8b8f874..6725b06 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,11 @@ finds the node with the biggest key less or equal a given value k. You can elimi - + @@ -251,7 +255,11 @@ finds the node with the smallest key bigger or equal a given value k. You can el - + From 734b781bfd3949b1b0c6729738aed5ed6856985b Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Mon, 9 Aug 2021 01:50:27 +0200 Subject: [PATCH 7/8] v4.3.0 --- CHANGELOG.md | 10 +++++++++- package.json | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29f9c34..849154e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +## [4.3.0] - 2021-08-09 +### Added +- `.floor` & `.ceil` as delegates to `.lowerBound` & `upperBound`. + +### Fixed +- `.lowerBound` & `upperBound` now finds the precise bound when multiple ones exist. +- make param type (value) optional on `.insert`. + ## [4.2.2] - 2021-06-20 ### Fixed - index.d.ts - ## [4.2.1] - 2021-06-20 ### Fixed diff --git a/package.json b/package.json index 05b3137..1a7554c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@datastructures-js/binary-search-tree", - "version": "4.2.2", + "version": "4.3.0", "description": "binary search tree & avl tree (self balancing tree) implementation in javascript", "main": "index.js", "scripts": { From 2c46860fc6016f84d405900e19c7e1ae0c274a97 Mon Sep 17 00:00:00 2001 From: Eyas Ranjous Date: Mon, 9 Aug 2021 01:51:28 +0200 Subject: [PATCH 8/8] v4.3.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 849154e..3fb7941 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - `.lowerBound` & `upperBound` now finds the precise bound when multiple ones exist. -- make param type (value) optional on `.insert`. +- make param (value) optional on `.insert`. ## [4.2.2] - 2021-06-20
runtime
k: T (number | string) + k: T (number | string) +
+ includeEqual: boolean +
BinarySearchTreeNode<T, U> | AvlTreeNode<T, U> O(log(n))
runtime
k: T (number | string) + k: T (number | string) +
+ includeEqual: boolean +
BinarySearchTreeNode<T, U> | AvlTreeNode<T, U> O(log(n))