Skip to content

added searchAfter and searchBefore with some tests #2

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ bst.delete(18, 'world'); // bst.search(18) will now give ['hello']

There are three optional parameters you can pass the BST constructor, allowing you to enforce a key-uniqueness constraint, use a custom function to compare keys and use a custom function to check whether values are equal. These parameters are all passed in an object.

### Browsing throuhg all keys in the tree

You can use `searchAfter` and `searchBefore` to browse through all keys in the tree, like this:

```javascript
// this assumes that each value in the BST includes 'key' attribute
var node = bst.search(bst.getMinKey());
for( ; node.length > 0; node = bst.searchAfter(node[0].key) ) {
// do something with each node here
// node is now an array of values, as with search()
}
```

You can use `getMinKey` and `getMaxKey` to get started from the beginning or the end, or you can also use any other key value as a starting point.

### Uniqueness

```javascript
Expand Down
2 changes: 1 addition & 1 deletion lib/avltree.js
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ AVLTree.prototype.delete = function (key, value) {
/**
* Other functions we want to use on an AVLTree as if it were the internal _AVLTree
*/
['getNumberOfKeys', 'search', 'betweenBounds', 'prettyPrint', 'executeOnEveryNode'].forEach(function (fn) {
['getNumberOfKeys', 'getMinKey', 'getMaxKey', 'search', 'searchAfter', 'searchBefore', 'betweenBounds', 'prettyPrint', 'executeOnEveryNode'].forEach(function (fn) {
AVLTree.prototype[fn] = function () {
return this.tree[fn].apply(this.tree, arguments);
};
Expand Down
96 changes: 96 additions & 0 deletions lib/bst.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,102 @@ BinarySearchTree.prototype.search = function (key) {
}
};

/**
* Search for data coming right after a specific key
*/
BinarySearchTree.prototype.searchAfter = function (key) {
if (!this.hasOwnProperty('key')) { return []; }

if (this.compareKeys(this.key, key) === 0) {
// if there's a right child, the next key will be there
var cur = this.right;
if( cur ) {
// within the right branch, traverse left until leaf
while( cur.left )
cur = cur.left;
return cur.data;
}

// traverse up until you find a bigger key
cur = this.parent;
while( cur ) {
if (this.compareKeys(key, cur.key) < 0)
return cur.data;
cur = cur.parent;
}
return [];
}

if (this.compareKeys(key, this.key) < 0) {
if (this.left) {
return this.left.searchAfter(key);
} else {
return this.data;
}
} else {
if (this.right) {
return this.right.searchAfter(key);
} else {
// traverse up until you find a bigger key
var cur = this.parent;
while( cur ) {
if (this.compareKeys(key, cur.key) < 0)
return cur.data;
cur = cur.parent;
}
return [];
}
}
};

/**
* Search for data coming right before a specific key
*/
BinarySearchTree.prototype.searchBefore = function (key) {
if (!this.hasOwnProperty('key')) { return []; }

if (this.compareKeys(this.key, key) === 0) {
// if there's a left child, the previous key will be there
var cur = this.left;
if( cur ) {
// within the left branch, traverse right until leaf
while( cur.right )
cur = cur.right;
return cur.data;
}

// traverse up until you find a smaller key
cur = this.parent;
while( cur ) {
if (this.compareKeys(key, cur.key) > 0)
return cur.data;
cur = cur.parent;
}
return [];
}

if (this.compareKeys(key, this.key) < 0) {
if (this.left) {
return this.left.searchBefore(key);
} else {
// traverse up until you find a smaller key
var cur = this.parent;
while( cur ) {
if (this.compareKeys(key, cur.key) > 0)
return cur.data;
cur = cur.parent;
}
return [];
}
} else {
if (this.right) {
return this.right.searchBefore(key);
} else {
return this.data;
}
}
};


/**
* Return a function that tells whether a given key matches a lower bound
Expand Down
52 changes: 52 additions & 0 deletions test/bst.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,58 @@ describe('Binary search tree', function () {
bst.search(63).length.should.equal(0);
});

it('Can find ascending ordered data in a BST', function () {
var bst = new BinarySearchTree()
, i;

customUtils.getRandomArray(100).forEach(function (n) {
bst.insert(n, { key: n, value: 'some data for ' + n });
});

bst.checkIsBST();

var key = bst.getMinKey();
key.should.equal(0);
for (i = 1; i <= 100; i += 1) {
var next = bst.searchAfter(key);
if( i == 100 )
next.should.deep.equal([]);
else {
next.length.should.equal(1);
next = next[0];
next.key.should.equal(i);
next.key.should.above(key);
key = next.key;
}
}
});

it('Can find descending ordered data in a BST', function () {
var bst = new BinarySearchTree()
, i;

customUtils.getRandomArray(100).forEach(function (n) {
bst.insert(n, { key: n, value: 'some data for ' + n });
});

bst.checkIsBST();

var key = bst.getMaxKey();
key.should.equal(99);
for (i = 1; i <= 100; i += 1) {
var next = bst.searchBefore(key);
if( i == 100 )
next.should.deep.equal([]);
else {
next.length.should.equal(1);
next = next[0];
next.key.should.equal(99-i);
next.key.should.below(key);
key = next.key;
}
}
});

it('Can search for data between two bounds', function () {
var bst = new BinarySearchTree();

Expand Down