diff --git a/README.md b/README.md
index 0a481b3..6725b06 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]) (floor)](#lowerboundk-includeEqual-floor)
+ * [.upperBound(k[, includeEqual]) (ceil)](#upperboundk-includeEqual-ceil)
* [.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]) (.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. `.floor` is a delegate to the same function.
@@ -229,19 +229,24 @@ finds the node with the biggest key less or equal a given value k.
| runtime |
- | k: T (number | string) |
+
+ k: T (number | string)
+
+ includeEqual: boolean
+ |
BinarySearchTreeNode<T, U> | AvlTreeNode<T, U> |
O(log(n)) |
```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]) (.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. `.ceil` is a delegate to the same function.
@@ -250,7 +255,11 @@ finds the node with the smallest key bigger than a given value k.
| runtime |
- | k: T (number | string) |
+
+ k: T (number | string)
+
+ includeEqual: boolean
+ |
BinarySearchTreeNode<T, U> | AvlTreeNode<T, U> |
O(log(n)) |
@@ -258,6 +267,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..3bed1cd 100644
--- a/src/avlTree.d.ts
+++ b/src/avlTree.d.ts
@@ -2,12 +2,14 @@ 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;
+ 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 dba0b5e..9c981b0 100644
--- a/src/binarySearchTree.d.ts
+++ b/src/binarySearchTree.d.ts
@@ -1,13 +1,15 @@
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;
+ 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 5cacd58..586dd7e 100644
--- a/src/binarySearchTree.js
+++ b/src/binarySearchTree.js
@@ -146,48 +146,80 @@ 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
+ * delegate to lowerBound
* @public
* @param {number|string} k
+ * @param {boolean} includeEqual
* @return {BinarySearchTreeNode|null}
*/
- upperBound(k, current = this._root) {
- if (current === null) {
- return null;
- }
+ floor(k, includeEqual = true) {
+ return this.lowerBound(k, includeEqual);
+ }
- if (current.getKey() <= k) {
- return this.upperBound(k, current.getRight());
- }
+ /**
+ * Returns the node with the smallest key bigger or equal k
+ * @public
+ * @param {number|string} k
+ * @param {boolean} includeEqual
+ * @return {BinarySearchTreeNode|null}
+ */
+ upperBound(k, includeEqual = true) {
+ let upperBound = null;
- if (current.hasLeft() && current.getLeft().getKey() > k) {
- return this.upperBound(k, current.getLeft());
- }
+ const upperBoundRecursive = (current) => {
+ if (current === null) {
+ return upperBound;
+ }
- return current;
+ const currentKey = current.getKey();
+ if (currentKey > k || (includeEqual && currentKey === k)) {
+ if (upperBound === null || upperBound.getKey() >= currentKey) {
+ upperBound = current;
+ }
+ return upperBoundRecursive(current.getLeft());
+ }
+
+ return upperBoundRecursive(current.getRight());
+ };
+
+ 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);
}
/**
diff --git a/test/binarySearchTree.test.js b/test/binarySearchTree.test.js
index cb48568..83d903f 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.floor(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.ceil(49303013).getKey()).to.equal(49311742);
+ });
});
describe('.traverseInOrder(cb)', () => {