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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ 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]
## [5.1.0] - 2022-12-08

### Added
- ability to abort tree traversal using a callback.

## [5.0.2] - 2022-08-21

### Fixed
Expand Down
39 changes: 36 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ employees.count(); // 7
### traverseInOrder
O(n)

traverses the tree in order (left-node-right).
traverses the tree in order (left-node-right). it also accepts an optional second param as a callback to abort traversal when it returns true.

```js
nums.traverseInOrder((node) => console.log(node.getValue()));
Expand All @@ -256,12 +256,23 @@ employees.traverseInOrder((node) => console.log(node.getValue()));
{ id: 80 }
{ id: 90 }
*/

let counter = 0;
const abortCb = () => counter > 1;
employees.traverseInOrder((node) => {
console.log(node.getValue());
counter += 1;
}, abortCb);
/*
{ id: 20 }
{ id: 30 }
*/
```

### traversePreOrder
O(n)

traverses the tree pre order (node-left-right).
traverses the tree pre order (node-left-right). it also accepts an optional second param as a callback to abort traversal when it returns true.

```js
nums.traversePreOrder((node) => console.log(node.getValue()));
Expand All @@ -285,12 +296,23 @@ employees.traversePreOrder((node) => console.log(node.getValue()));
{ id: 60 }
{ id: 90 }
*/

let counter = 0;
const abortCb = () => counter > 1;
employees.traversePreOrder((node) => {
console.log(node.getValue());
counter += 1;
}, abortCb);
/*
{ id: 50 }
{ id: 30 }
*/
```

### traversePostOrder
O(n)

traverses the tree post order (left-right-node).
traverses the tree post order (left-right-node). it also accepts an optional second param as a callback to abort traversal when it returns true.

```js
nums.traversePostOrder((node) => console.log(node.getValue()));
Expand All @@ -314,6 +336,17 @@ employees.traversePostOrder((node) => console.log(node.getValue()));
{ id: 80 }
{ id: 50 }
*/

let counter = 0;
const abortCb = () => counter > 1;
employees.traversePostOrder((node) => {
console.log(node.getValue());
counter += 1;
}, abortCb);
/*
{ id: 20 }
{ id: 40 }
*/
```

### remove
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@datastructures-js/binary-search-tree",
"version": "5.0.2",
"version": "5.1.0",
"description": "binary search tree & avl tree (self balancing tree) implementation in javascript",
"main": "index.js",
"types": "index.d.ts",
Expand Down
6 changes: 3 additions & 3 deletions src/binarySearchTree.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export class BinarySearchTree<T> {
root(): BinarySearchTreeNode<T> | null;
count(): number;
remove(value: T): boolean;
traverseInOrder(cb: (node: BinarySearchTreeNode<T>) => void): void;
traversePreOrder(cb: (node: BinarySearchTreeNode<T>) => void): void;
traversePostOrder(cb: (node: BinarySearchTreeNode<T>) => void): void;
traverseInOrder(cb: (node: BinarySearchTreeNode<T>) => void, abortCb?: () => boolean): void;
traversePreOrder(cb: (node: BinarySearchTreeNode<T>) => void, abortCb?: () => boolean): void;
traversePostOrder(cb: (node: BinarySearchTreeNode<T>) => void, abortCb?: () => boolean): void;
clear(): void;
}
17 changes: 11 additions & 6 deletions src/binarySearchTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,15 +289,17 @@ class BinarySearchTree {
* Traverses the tree in-order (left-node-right)
* @public
* @param {function} cb
* @param {function} [abortCb]
*/
traverseInOrder(cb) {
traverseInOrder(cb, abortCb) {
if (typeof cb !== 'function') {
throw new Error('.traverseInOrder expects a callback function');
}

const traverseRecursive = (current) => {
if (current === null) return;
if (current === null || (abortCb && abortCb())) return;
traverseRecursive(current.getLeft());
if (abortCb && abortCb()) return;
cb(current);
traverseRecursive(current.getRight());
};
Expand All @@ -309,14 +311,15 @@ class BinarySearchTree {
* Traverses the tree pre-order (node-left-right)
* @public
* @param {function} cb
* @param {function} [abortCb]
*/
traversePreOrder(cb) {
traversePreOrder(cb, abortCb) {
if (typeof cb !== 'function') {
throw new Error('.traversePreOrder expects a callback function');
}

const traverseRecursive = (current) => {
if (current === null) return;
if (current === null || (abortCb && abortCb())) return;
cb(current);
traverseRecursive(current.getLeft());
traverseRecursive(current.getRight());
Expand All @@ -329,16 +332,18 @@ class BinarySearchTree {
* Traverses the tree post-order (left-right-node)
* @public
* @param {function} cb
* @param {function} [abortCb]
*/
traversePostOrder(cb) {
traversePostOrder(cb, abortCb) {
if (typeof cb !== 'function') {
throw new Error('.traversePostOrder expects a callback function');
}

const traverseRecursive = (current) => {
if (current === null) return;
if (current === null || (abortCb && abortCb())) return;
traverseRecursive(current.getLeft());
traverseRecursive(current.getRight());
if (abortCb && abortCb()) return;
cb(current);
};

Expand Down
30 changes: 30 additions & 0 deletions test/binarySearchTree.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ describe('BinarySearchTree tests', () => {
bst.traverseInOrder((node) => keys.push(node.getValue()));
expect(keys).to.deep.equal([20, 30, 40, 50, 60, 80, 90]);
});

it('traverse in order and allow aborting traversal', () => {
const keys = [];
let counter = 0;
bst.traverseInOrder((node) => {
keys.push(node.getValue());
counter += 1;
}, () => counter > 2);
expect(keys).to.deep.equal([20, 30, 40]);
});
});

describe('.traversePreOrder(cb)', () => {
Expand All @@ -131,6 +141,16 @@ describe('BinarySearchTree tests', () => {
bst.traversePreOrder((node) => keys.push(node.getValue()));
expect(keys).to.deep.equal([50, 30, 20, 40, 80, 60, 90]);
});

it('traverse pre order and allow aborting traversal', () => {
const keys = [];
let counter = 0;
bst.traversePreOrder((node) => {
keys.push(node.getValue());
counter += 1;
}, () => counter > 2);
expect(keys).to.deep.equal([50, 30, 20]);
});
});

describe('.traversePostOrder(cb)', () => {
Expand All @@ -139,6 +159,16 @@ describe('BinarySearchTree tests', () => {
bst.traversePostOrder((node) => keys.push(node.getValue()));
expect(keys).to.deep.equal([20, 40, 30, 60, 90, 80, 50]);
});

it('traverse post order and allow aborting traversal', () => {
const keys = [];
let counter = 0;
bst.traversePostOrder((node) => {
keys.push(node.getValue());
counter += 1;
}, () => counter > 2);
expect(keys).to.deep.equal([20, 40, 30]);
});
});

describe('.remove(value)', () => {
Expand Down