Skip to content
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
30 changes: 27 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ Binary Search Tree & AVL Tree (Self Balancing Tree) implementation in javascript
* [constructor](#constructor)
* [insert](#insert)
* [has](#has)
* [hasKey](#haskey)
* [find](#find)
* [findKey](#findkey)
* [min](#min)
* [max](#max)
* [lowerBound (floor)](#lowerbound-floor)
Expand Down Expand Up @@ -70,6 +72,8 @@ the compare function must return a number for the 3 cases:

There is already a default compare function for primitive values (number, string).

constructor also accepts an options param, where the comparison key prob name can be passed for object types in order to search by that key directly using findKey and hasKey.

##### JS
###### BinarySearchTree
```js
Expand All @@ -80,7 +84,7 @@ const employees = new BinarySearchTree((a, b) => a.id - b.id);
###### AvlTree
```js
const nums = new AvlTree();
const employees = new AvlTree((a, b) => a.id - b.id);
const employees = new AvlTree((a, b) => a.id - b.id, { key: 'id' });
```

##### TS
Expand All @@ -93,13 +97,13 @@ interface IEmployee {
###### BinarySearchTree
```js
const nums = new BinarySearchTree<number>();
const employees = new BinarySearchTree<IEmployee>((a, b) => a.id - b.id);
const employees = new BinarySearchTree<IEmployee>((a, b) => a.id - b.id, { key: 'id' });
```

###### AvlTree
```js
const nums = new AvlTree<number>();
const employees = new AvlTree<IEmployee>((a, b) => a.id - b.id);
const employees = new AvlTree<IEmployee>((a, b) => a.id - b.id, { key: 'id' });
```

### insert
Expand Down Expand Up @@ -140,6 +144,16 @@ employees.has({ id: 50 }); // true
employees.has({ id: 100 }); // false
```

### hasKey
O(log(n))

checks if a value exists by its key if the node's key prob is provided in the constructor.

```js
employees.has(50); // true
employees.has(100); // false
```

### find
O(log(n))

Expand All @@ -153,6 +167,16 @@ employees.find({ id: 60 }).getValue(); // { id: 60 }
employees.find({ id: 100 }); // null
```

### findKey
O(log(n))

finds a node by its key if the node's key prob is provided in the constructor.

```js
employees.findKey(60).getValue(); // { id: 60 }
employees.find(100); // null
```

### min
O(log(n))

Expand Down
3 changes: 2 additions & 1 deletion src/avlTree.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { BinarySearchTree } from './binarySearchTree';
import { AvlTreeNode } from './avlTreeNode';

export class AvlTree<T> extends BinarySearchTree<T> {
constructor(compare?: (a: T, b: T) => number);
constructor(compare?: (a: T, b: T) => number, options?: { key: string });
insert(value: T): AvlTree<T>;
find(value: T): AvlTreeNode<T> | null;
findKey(key: number|string): AvlTreeNode<T> | null;
max(node?: AvlTreeNode<T>): AvlTreeNode<T> | null;
min(node?: AvlTreeNode<T>): AvlTreeNode<T> | null;
lowerBound(value: T, includeEqual?: boolean): AvlTreeNode<T> | null;
Expand Down
4 changes: 2 additions & 2 deletions src/avlTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ const { AvlTreeNode } = require('./avlTreeNode');
* @extends BinarySearchTree
*/
class AvlTree extends BinarySearchTree {
constructor(compare) {
constructor(compare, options) {
if (compare && typeof compare !== 'function') {
throw new Error('AvlTree constructor expects a compare function');
}

super(compare);
super(compare, options);
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/binarySearchTree.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { BinarySearchTreeNode } from './binarySearchTreeNode';

export class BinarySearchTree<T> {
constructor(compare?: (a: T, b: T) => number);
constructor(compare?: (a: T, b: T) => number, options?: { key: string });
insert(value: T): BinarySearchTree<T>;
has(value: T): boolean;
hasKey(key: number|string): boolean;
find(value: T): BinarySearchTreeNode<T> | null;
findKey(key: number|string): BinarySearchTreeNode<T> | null;
max(node?: BinarySearchTreeNode<T>): BinarySearchTreeNode<T> | null;
min(node?: BinarySearchTreeNode<T>): BinarySearchTreeNode<T> | null;
lowerBound(value: T, includeEqual?: boolean): BinarySearchTreeNode<T> | null;
Expand Down
35 changes: 31 additions & 4 deletions src/binarySearchTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ const defaultCompare = (a, b) => {
* @class BinarySearchTree
*/
class BinarySearchTree {
constructor(compare) {
constructor(compare, options) {
if (compare && typeof compare !== 'function') {
throw new Error('BinarySearchTree constructor expects a compare function');
}

this._compare = compare || defaultCompare;
this._options = options || {};
this._root = null;
this._count = 0;
}
Expand Down Expand Up @@ -65,9 +66,9 @@ class BinarySearchTree {
}

/**
* Checks if a value exists in the tree by its key
* Checks if a value exists in the tree by its value
* @public
* @param {number|string} key
* @param {number|string|object} value
* @return {boolean}
*/
has(value) {
Expand All @@ -84,9 +85,22 @@ class BinarySearchTree {
}

/**
* Finds a node by its key
* Checks if a value exists in the tree by its key
* @public
* @param {number|string} key
* @return {boolean}
*/
hasKey(key) {
if (this._options.key === undefined || this._options.key === null) {
throw new Error('Missing key prop name in constructor options');
}
return this.has({ [this._options.key]: key });
}

/**
* Finds a node by its value
* @public
* @param {number|string|object} value
* @return {BinarySearchTreeNode}
*/
find(value) {
Expand All @@ -102,6 +116,19 @@ class BinarySearchTree {
return findRecursive(this._root);
}

/**
* Finds a node by its key
* @public
* @param {number|string} key
* @return {BinarySearchTreeNode}
*/
findKey(key) {
if (this._options.key === undefined || this._options.key === null) {
throw new Error('Missing key prop name in constructor options');
}
return this.find({ [this._options.key]: key });
}

/**
* Finds the node with max key (most right) in the tree
* @public
Expand Down
34 changes: 32 additions & 2 deletions test/binarySearchTree.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('BinarySearchTree tests', () => {
});

describe('.has(value)', () => {
it('checks if a node exists by key', () => {
it('checks if a node exists by value', () => {
expect(bst.has(50)).to.equal(true);
expect(bst.has(80)).to.equal(true);
expect(bst.has(30)).to.equal(true);
Expand All @@ -48,8 +48,23 @@ describe('BinarySearchTree tests', () => {
});
});

describe('.has(key)', () => {
it('checks if a node exists by key', () => {
const testTree = new BinarySearchTree((a, b) => a.id - b.id, { key: 'id' });
testTree.insert({ id: 1, name: 'a' });
testTree.insert({ id: 2, name: 'b' });
testTree.insert({ id: 3, name: 'c' });
expect(testTree.has({ id: 1 })).to.equal(true);
expect(testTree.has({ id: 2 })).to.equal(true);
expect(testTree.has({ id: 3 })).to.equal(true);
expect(testTree.hasKey(1)).to.equal(true);
expect(testTree.hasKey(2)).to.equal(true);
expect(testTree.hasKey(3)).to.equal(true);
});
});

describe('.find(value)', () => {
it('should search a node by its key in the tree', () => {
it('should search a node by its value in the tree', () => {
expect(bst.find(50)).to.be.instanceof(BinarySearchTreeNode);
expect(bst.find(80)).to.be.instanceof(BinarySearchTreeNode);
expect(bst.find(30)).to.be.instanceof(BinarySearchTreeNode);
Expand All @@ -61,6 +76,21 @@ describe('BinarySearchTree tests', () => {
});
});

describe('.findKey(key)', () => {
it('should search a node by its key in the tree', () => {
const testTree = new BinarySearchTree((a, b) => a.id - b.id, { key: 'id' });
testTree.insert({ id: 1, name: 'a' });
testTree.insert({ id: 2, name: 'b' });
testTree.insert({ id: 3, name: 'c' });
expect(testTree.find({ id: 1 }).getValue()).to.eql({ id: 1, name: 'a' });
expect(testTree.find({ id: 2 }).getValue()).to.eql({ id: 2, name: 'b' });
expect(testTree.find({ id: 3 }).getValue()).to.eql({ id: 3, name: 'c' });
expect(testTree.findKey(1).getValue()).to.eql({ id: 1, name: 'a' });
expect(testTree.findKey(2).getValue()).to.eql({ id: 2, name: 'b' });
expect(testTree.findKey(3).getValue()).to.eql({ id: 3, name: 'c' });
});
});

describe('.max()', () => {
it('get the node with max key', () => {
const max = bst.max();
Expand Down