feat: Add keys, values, and clone methods to all binary tree data structures.

This commit is contained in:
Revone 2023-11-27 18:01:59 +08:00
parent 523221357c
commit 9a9571431c
9 changed files with 236 additions and 24 deletions

View file

@ -102,9 +102,10 @@ export class BinaryTreeNode<V = any, N extends BinaryTreeNode<V, N> = BinaryTree
* 8. Full Trees: Every node has either 0 or 2 children.
* 9. Complete Trees: All levels are fully filled except possibly the last, filled from left to right.
*/
export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode<V, BinaryTreeNodeNested<V>>, TREE extends BinaryTree<V, N, TREE> = BinaryTree<V, N, BinaryTreeNested<V, N>>>
implements IBinaryTree<V, N, TREE> {
export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode<V, BinaryTreeNodeNested<V>>, TREE extends BinaryTree<V, N, TREE> = BinaryTree<V, N, BinaryTreeNested<V, N>>>
implements IBinaryTree<V, N, TREE> {
iterationType = IterationType.ITERATIVE
/**
@ -1723,6 +1724,66 @@ export class BinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNode
return ans;
}
/**
* Time complexity: O(n)
* Space complexity: O(n)
*/
/**
* Time complexity: O(n)
* Space complexity: O(n)
*
* The function "keys" returns an array of keys from a given object.
* @returns an array of BTNKey objects.
*/
keys(): BTNKey[] {
const keys: BTNKey[] = [];
for (const entry of this) {
keys.push(entry[0]);
}
return keys;
}
/**
* Time complexity: O(n)
* Space complexity: O(n)
*/
/**
* Time complexity: O(n)
* Space complexity: O(n)
*
* The function "values" returns an array of values from a map-like object.
* @returns The `values()` method is returning an array of values (`V`) from the entries in the
* object.
*/
values(): (V | undefined)[] {
const values: (V | undefined)[] = [];
for (const entry of this) {
values.push(entry[1]);
}
return values;
}
/**
* Time complexity: O(n)
* Space complexity: O(n)
*/
/**
* Time complexity: O(n)
* Space complexity: O(n)
*
* The `clone` function creates a new tree object and copies all the nodes from the original tree to
* the new tree.
* @returns The `clone()` method is returning a cloned instance of the `TREE` object.
*/
clone(): TREE {
const cloned = this.createTree();
this.bfs(node => cloned.add([node.key, node.value]));
return cloned;
}
/**
* Time complexity: O(n)
* Space complexity: O(1)

View file

@ -318,6 +318,24 @@ export class TreeMultimap<V = any, N extends TreeMultimapNode<V, N> = TreeMultim
this._count = 0;
}
/**
* Time complexity: O(n)
* Space complexity: O(n)
*/
/**
* Time complexity: O(n)
* Space complexity: O(n)
*
* The `clone` function creates a deep copy of a tree object.
* @returns The `clone()` method is returning a cloned instance of the `TREE` object.
*/
override clone(): TREE {
const cloned = this.createTree();
this.bfs(node => cloned.add([node.key, node.value], node.count));
return cloned;
}
/**
* Time Complexity: O(1) - constant time, as it performs basic pointer assignments.
* Space Complexity: O(1) - constant space, as it only uses a constant amount of memory.

View file

@ -2,7 +2,7 @@ import { AVLTree, CP } from 'avl-tree-typed';
describe('AVL Tree Test', () => {
it('should perform various operations on a AVL Tree', () => {
const arr: [number, number][] = [[11, 11], [3, 3], [15, 15], [1, 1], [8, 8], [13, 13], [16, 16], [2, 2], [6, 6], [9, 9], [12,12], [14, 14], [4, 4], [7, 7], [10,10], [5,5]];
const arr: [number, number][] = [[11, 11], [3, 3], [15, 15], [1, 1], [8, 8], [13, 13], [16, 16], [2, 2], [6, 6], [9, 9], [12, 12], [14, 14], [4, 4], [7, 7], [10, 10], [5, 5]];
const tree = new AVLTree();
for (const i of arr) tree.add(i);

View file

@ -187,20 +187,20 @@ describe('Individual package BST operations test', () => {
expect(objBST).toBeInstanceOf(BST);
objBST.add([11, { key: 11, keyA: 11 }]);
objBST.add([3, { key: 3, keyA: 3 }]);
const values:[number, { key: number; keyA: number }][] = [
[15,{ key: 15, keyA: 15 }],
const values: [number, { key: number; keyA: number }][] = [
[15, { key: 15, keyA: 15 }],
[1, { key: 1, keyA: 1 }],
[8, { key: 8, keyA: 8 }],
[13,{ key: 13, keyA: 13 }],
[16,{ key: 16, keyA: 16 }],
[13, { key: 13, keyA: 13 }],
[16, { key: 16, keyA: 16 }],
[2, { key: 2, keyA: 2 }],
[6, { key: 6, keyA: 6 }],
[9, { key: 9, keyA: 9 }],
[12,{ key: 12, keyA: 12 }],
[14,{ key: 14, keyA: 14 }],
[12, { key: 12, keyA: 12 }],
[14, { key: 14, keyA: 14 }],
[4, { key: 4, keyA: 4 }],
[7, { key: 7, keyA: 7 }],
[10,{ key: 10, keyA: 10 }],
[10, { key: 10, keyA: 10 }],
[5, { key: 5, keyA: 5 }]
];

View file

@ -4,17 +4,17 @@ import { getRandomIntArray, magnitude } from '../../../utils';
const suite = new Benchmark.Suite();
const biTree = new BinaryTree<number>();
const { N_LOG_N } = magnitude;
const arr = getRandomIntArray(N_LOG_N, 0, N_LOG_N, true);
const { THOUSAND } = magnitude;
const arr = getRandomIntArray(THOUSAND, 0, THOUSAND, true);
suite
.add(`${N_LOG_N.toLocaleString()} add randomly`, () => {
.add(`${THOUSAND.toLocaleString()} add randomly`, () => {
biTree.clear();
for (let i = 0; i < arr.length; i++) {
biTree.add(arr[i]);
}
})
.add(`${N_LOG_N.toLocaleString()} add & delete randomly`, () => {
.add(`${THOUSAND.toLocaleString()} add & delete randomly`, () => {
biTree.clear();
for (let i = 0; i < arr.length; i++) {
biTree.add(arr[i]);
@ -23,23 +23,28 @@ suite
biTree.delete(arr[i]);
}
})
.add(`${N_LOG_N.toLocaleString()} addMany`, () => {
.add(`${THOUSAND.toLocaleString()} addMany`, () => {
biTree.clear();
biTree.addMany(arr);
})
.add(`${N_LOG_N.toLocaleString()} get`, () => {
.add(`${THOUSAND.toLocaleString()} get`, () => {
for (let i = 0; i < arr.length; i++) {
biTree.get(arr[i]);
}
})
.add(`${N_LOG_N.toLocaleString()} dfs`, () => {
for (let i = 0; i < N_LOG_N; i++) biTree.dfs();
.add(`${THOUSAND.toLocaleString()} has`, () => {
for (let i = 0; i < arr.length; i++) {
biTree.get(arr[i]);
}
})
.add(`${N_LOG_N.toLocaleString()} bfs`, () => {
for (let i = 0; i < N_LOG_N; i++) biTree.bfs();
.add(`${THOUSAND.toLocaleString()} dfs`, () => {
for (let i = 0; i < THOUSAND; i++) biTree.dfs();
})
.add(`${N_LOG_N.toLocaleString()} morris`, () => {
for (let i = 0; i < N_LOG_N; i++) biTree.morris(n => n, 'pre');
.add(`${THOUSAND.toLocaleString()} bfs`, () => {
for (let i = 0; i < THOUSAND; i++) biTree.bfs();
})
.add(`${THOUSAND.toLocaleString()} morris`, () => {
for (let i = 0; i < THOUSAND; i++) biTree.morris(n => n, 'pre');
});
export { suite };

View file

@ -341,4 +341,20 @@ describe('AVLTree iterative methods test', () => {
expect(entries.length).toBe(3);
expect(entries).toEqual([[1, 'a'], [2, 'b'], [3, 'c']]);
});
test('should clone work well', () => {
const cloned = avl.clone();
expect(cloned.root?.left?.key).toBe(1);
expect(cloned.root?.right?.value).toBe('c');
});
test('should keys', () => {
const keys = avl.keys();
expect(keys).toEqual([1, 2, 3]);
});
test('should values', () => {
const values = avl.values();
expect(values).toEqual(['a', 'b', 'c']);
});
});

View file

@ -564,7 +564,6 @@ describe('BinaryTree', () => {
});
});
describe('BinaryTree iterative methods test', () => {
let binaryTree: BinaryTree<string>;
beforeEach(() => {
@ -617,4 +616,20 @@ describe('BinaryTree iterative methods test', () => {
expect(entries.length).toBe(3);
expect(entries).toEqual([[2, 'b'], [1, 'a'], [3, 'c']]);
});
test('should clone work well', () => {
const cloned = binaryTree.clone();
expect(cloned.root?.left?.key).toBe(2);
expect(cloned.root?.right?.value).toBe('c');
});
test('should keys', () => {
const keys = binaryTree.keys();
expect(keys).toEqual([2, 1, 3]);
});
test('should values', () => {
const values = binaryTree.values();
expect(values).toEqual(['b', 'a', 'c']);
});
});

View file

@ -900,4 +900,20 @@ describe('BST iterative methods test', () => {
expect(entries.length).toBe(3);
expect(entries).toEqual([[1, 'a'], [2, 'b'], [3, 'c']]);
});
test('should clone work well', () => {
const cloned = bst.clone();
expect(cloned.root?.left).toBe(undefined);
expect(cloned.root?.right?.value).toBe('b');
});
test('should keys', () => {
const keys = bst.keys();
expect(keys).toEqual([1, 2, 3]);
});
test('should values', () => {
const values = bst.values();
expect(values).toEqual(['a', 'b', 'c']);
});
});

View file

@ -1,4 +1,12 @@
import { CP, IterationType, TreeMultimap, TreeMultimapNode } from '../../../../src';
import {
AVLTreeNode,
BinaryTreeNode,
BSTNode,
CP,
IterationType,
TreeMultimap,
TreeMultimapNode
} from '../../../../src';
import { isDebugTest } from '../../../config';
const isDebug = isDebugTest;
@ -592,3 +600,76 @@ describe('TreeMultimap Performance test', function () {
isDebug && console.log('---lesserOrGreaterTraverse', performance.now() - startL);
});
});
describe('TreeMultimap iterative methods test', () => {
let treeMM: TreeMultimap<string>;
beforeEach(() => {
treeMM = new TreeMultimap<string>();
treeMM.add([1, 'a'], 10);
treeMM.add([2, 'b'], 10);
treeMM.add([3, 'c'], 1);
});
test('The node obtained by get Node should match the node type', () => {
const node3 = treeMM.getNode(3);
expect(node3).toBeInstanceOf(BinaryTreeNode);
expect(node3).toBeInstanceOf(BSTNode);
expect(node3).toBeInstanceOf(AVLTreeNode);
});
test('forEach should iterate over all elements', () => {
const mockCallback = jest.fn();
treeMM.forEach((entry) => {
mockCallback(entry);
});
expect(mockCallback.mock.calls.length).toBe(3);
expect(mockCallback.mock.calls[0][0]).toEqual([1, 'a']);
expect(mockCallback.mock.calls[1][0]).toEqual([2, 'b']);
expect(mockCallback.mock.calls[2][0]).toEqual([3, 'c']);
});
test('filter should return a new tree with filtered elements', () => {
const filteredTree = treeMM.filter(([key]) => key > 1);
expect(filteredTree.size).toBe(2);
expect([...filteredTree]).toEqual([[2, 'b'], [3, 'c']]);
});
test('map should return a new tree with modified elements', () => {
const mappedTree = treeMM.map(([key]) => (key * 2).toString());
expect(mappedTree.size).toBe(3);
expect([...mappedTree]).toEqual([[1, '2'], [2, '4'], [3, '6']]);
});
test('reduce should accumulate values', () => {
const sum = treeMM.reduce((acc, [key]) => acc + key, 0);
expect(sum).toBe(6);
});
test('[Symbol.iterator] should provide an iterator', () => {
const entries = [];
for (const entry of treeMM) {
entries.push(entry);
}
expect(entries.length).toBe(3);
expect(entries).toEqual([[1, 'a'], [2, 'b'], [3, 'c']]);
});
test('should clone work well', () => {
expect(treeMM.count).toBe(21)
const cloned = treeMM.clone();
expect(cloned.root?.left?.key).toBe(1);
expect(cloned.root?.right?.value).toBe('c');
});
test('should keys', () => {
const keys = treeMM.keys();
expect(keys).toEqual([1, 2, 3]);
});
test('should values', () => {
const values = treeMM.values();
expect(values).toEqual(['a', 'b', 'c']);
});
});