Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix lowerBound & upperBound #43

Merged
merged 6 commits into from
Aug 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.

<table>
<tr>
Expand All @@ -229,19 +229,24 @@ finds the node with the biggest key less or equal a given value k.
<th align="center">runtime</th>
</tr>
<tr>
<td>k: T (number | string)</td>
<td>
k: T (number | string)
<br />
includeEqual: boolean
</td>
<td align="center"><a href="#binarysearchtreenodet-u">BinarySearchTreeNode&lt;T, U&gt;</a> | <a href="#avltreenodet-u">AvlTreeNode&lt;T, U&gt;</a></td>
<td align="center">O(log(n))</td>
</tr>
</table>

```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.

<table>
<tr>
Expand All @@ -250,14 +255,20 @@ finds the node with the smallest key bigger than a given value k.
<th align="center">runtime</th>
</tr>
<tr>
<td>k: T (number | string)</td>
<td>
k: T (number | string)
<br />
includeEqual: boolean
</td>
<td align="center"><a href="#binarysearchtreenodet-u">BinarySearchTreeNode&lt;T, U&gt;</a> | <a href="#avltreenodet-u">AvlTreeNode&lt;T, U&gt;</a></td>
<td align="center">O(log(n))</td>
</tr>
</table>

```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
```

Expand Down
8 changes: 5 additions & 3 deletions src/avlTree.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { BinarySearchTree } from './binarySearchTree';
import { AvlTreeNode } from './avlTreeNode';

export class AvlTree<T extends number|string, U = undefined> extends BinarySearchTree<T, U> {
insert(key: T, value: U): AvlTreeNode<T, U>;
insert(key: T, value?: U): AvlTreeNode<T, U>;
find(key: T): AvlTreeNode<T, U>;
max(node?: AvlTreeNode<T, U>): AvlTreeNode<T, U>;
min(node?: AvlTreeNode<T, U>): AvlTreeNode<T, U>;
lowerBound(k: T, node?: AvlTreeNode<T, U>): AvlTreeNode<T, U>;
upperBound(k: T, node?: AvlTreeNode<T, U>): AvlTreeNode<T, U>;
lowerBound(k: T, includeEqual?: boolean): AvlTreeNode<T, U>;
floor(k: T, includeEqual?: boolean): AvlTreeNode<T, U>;
upperBound(k: T, includeEqual?: boolean): AvlTreeNode<T, U>;
ceil(k: T, includeEqual?: boolean): AvlTreeNode<T, U>;
root(): AvlTreeNode<T, U>;
traverseInOrder(cb: (node: AvlTreeNode<T, U>) => void): void;
traversePreOrder(cb: (node: AvlTreeNode<T, U>) => void): void;
Expand Down
8 changes: 5 additions & 3 deletions src/binarySearchTree.d.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { BinarySearchTreeNode } from './binarySearchTreeNode';

export class BinarySearchTree<T extends number|string, U = undefined> {
insert(key: T, value: U): BinarySearchTreeNode<T, U>;
insert(key: T, value?: U): BinarySearchTreeNode<T, U>;
has(key: T): boolean;
find(key: T): BinarySearchTreeNode<T, U>;
max(node?: BinarySearchTreeNode<T, U>): BinarySearchTreeNode<T, U>;
min(node?: BinarySearchTreeNode<T, U>): BinarySearchTreeNode<T, U>;
lowerBound(k: T, node?: BinarySearchTreeNode<T, U>): BinarySearchTreeNode<T, U>;
upperBound(k: T, node?: BinarySearchTreeNode<T, U>): BinarySearchTreeNode<T, U>;
lowerBound(k: T, includeEqual?: boolean): BinarySearchTreeNode<T, U>;
floor(k: T, includeEqual?: boolean): BinarySearchTreeNode<T, U>;
upperBound(k: T, includeEqual?: boolean): BinarySearchTreeNode<T, U>;
ceil(k: T, includeEqual?: boolean): BinarySearchTreeNode<T, U>;
root(): BinarySearchTreeNode<T, U>;
count(): number;
remove(k: T): boolean;
Expand Down
84 changes: 58 additions & 26 deletions src/binarySearchTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
24 changes: 23 additions & 1 deletion test/binarySearchTree.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)', () => {
Expand Down