feat: Allow BinaryTree to support adding duplicate keys. docs: BinaryTree

This commit is contained in:
Revone 2024-12-02 14:32:43 +13:00
parent a19080bb53
commit b759eecf59
5 changed files with 86 additions and 10 deletions

View file

@ -1017,7 +1017,7 @@ pnpm perf:rbtree
<td>Counter</td>
</tr>
<tr>
<td>LinkedHashMap&lt;K, V&gt;</td>
<td>ES6 Map&lt;K, V&gt;</td>
<td>-</td>
<td>LinkedHashMap&lt;K, V&gt;</td>
<td>OrderedDict</td>

View file

@ -152,9 +152,10 @@ export class BinaryTree<K = any, V = any, R = object, MK = any, MV = any, MR = o
) {
super();
if (options) {
const { iterationType, toEntryFn, isMapMode } = options;
const { iterationType, toEntryFn, isMapMode, isDuplicate } = options;
if (iterationType) this.iterationType = iterationType;
if (isMapMode !== undefined) this._isMapMode = isMapMode;
if (isDuplicate !== undefined) this._isDuplicate = isDuplicate;
if (typeof toEntryFn === 'function') this._toEntryFn = toEntryFn;
else if (toEntryFn) throw TypeError('toEntryFn must be a function type');
}
@ -170,6 +171,12 @@ export class BinaryTree<K = any, V = any, R = object, MK = any, MV = any, MR = o
return this._isMapMode;
}
protected _isDuplicate = false;
get isDuplicate() {
return this._isDuplicate;
}
protected _store = new Map<K, V | undefined>();
get store() {
@ -356,8 +363,8 @@ export class BinaryTree<K = any, V = any, R = object, MK = any, MV = any, MR = o
* Space Complexity: O(1)
*
* The function `isRange` checks if the input parameter is an instance of the `Range` class.
* @param {BTNRep<K, V, BinaryTreeNode<K, V>> | NodePredicate<BinaryTreeNode<K, V>> | Range<K>}
* keyNodeEntryOrPredicate - The `keyNodeEntryOrPredicate` parameter in the `isRange` function can be
* @param {BTNRep<K, V, BinaryTreeNode<K, V>> | NodePredicate<BinaryTreeNode<K, V>> | Range<K>} keyNodeEntryOrPredicate
* - The `keyNodeEntryOrPredicate` parameter in the `isRange` function can be
* of type `BTNRep<K, V, BinaryTreeNode<K, V>>`, `NodePredicate<BinaryTreeNode<K, V>>`, or
* `Range<K>`. The function checks if the `keyNodeEntry
* @returns The `isRange` function is checking if the `keyNodeEntryOrPredicate` parameter is an
@ -461,11 +468,13 @@ export class BinaryTree<K = any, V = any, R = object, MK = any, MV = any, MR = o
if (!cur) continue;
// Check for duplicate keys when newNode is not null
if (newNode !== null && cur.key === newNode.key) {
this._replaceNode(cur, newNode);
if (this._isMapMode) this._setValue(cur.key, newValue);
return true; // If duplicate keys are found, no insertion is performed
if (!this._isDuplicate) {
// Check for duplicate keys when newNode is not null
if (newNode !== null && cur.key === newNode.key) {
this._replaceNode(cur, newNode);
if (this._isMapMode) this._setValue(cur.key, newValue);
return true; // If duplicate keys are found, no insertion is performed
}
}
// Record the first possible insertion location found

View file

@ -7,6 +7,7 @@ export type BinaryTreeOptions<K, V, R> = {
iterationType?: IterationType;
toEntryFn?: ToEntryFn<K, V, R>;
isMapMode?: boolean;
isDuplicate?: boolean;
}
export type BinaryTreePrintOptions = { isShowUndefined?: boolean; isShowNull?: boolean; isShowRedBlackNIL?: boolean };

View file

@ -2,7 +2,7 @@ import type { BinaryTreeOptions } from './binary-tree';
import { Comparable } from '../../utils';
import { OptValue } from '../../common';
export type BSTOptions<K, V, R> = BinaryTreeOptions<K, V, R> & {
export type BSTOptions<K, V, R> = Omit<BinaryTreeOptions<K, V, R>, 'isDuplicate'> & {
specifyComparable?: (key: K) => Comparable
isReverse?: boolean;
}

View file

@ -1434,3 +1434,69 @@ describe('BinaryTree not map mode iterative methods test', () => {
expect(cloned.get(cloned.root?.right)).toBe('c');
});
});
describe('classic use', () => {
it('@example determine loan approval using a decision tree', () => {
// Decision tree structure
const loanDecisionTree = new BinaryTree<string>(
['stableIncome', 'goodCredit', 'Rejected', 'Approved', 'Rejected'],
{ isDuplicate: true }
);
function determineLoanApproval(
node?: BinaryTreeNode<string> | null,
conditions?: { [key: string]: boolean }
): string {
if (!node) throw new Error('Invalid node');
// If it's a leaf node, return the decision result
if (!node.left && !node.right) return node.key;
// Check if a valid condition exists for the current node's key
return conditions?.[node.key]
? determineLoanApproval(node.left, conditions)
: determineLoanApproval(node.right, conditions);
}
// Test case 1: Stable income and good credit score
expect(determineLoanApproval(loanDecisionTree.root, { stableIncome: true, goodCredit: true })).toBe('Approved');
// Test case 2: Stable income but poor credit score
expect(determineLoanApproval(loanDecisionTree.root, { stableIncome: true, goodCredit: false })).toBe('Rejected');
// Test case 3: No stable income
expect(determineLoanApproval(loanDecisionTree.root, { stableIncome: false, goodCredit: true })).toBe('Rejected');
// Test case 4: No stable income and poor credit score
expect(determineLoanApproval(loanDecisionTree.root, { stableIncome: false, goodCredit: false })).toBe('Rejected');
});
it('@example evaluate the arithmetic expression represented by the binary tree', () => {
const expressionTree = new BinaryTree<number | string>(['+', 3, '*', null, null, 5, '-', null, null, 2, 8]);
function evaluate(node?: BinaryTreeNode<number | string> | null): number {
if (!node) return 0;
if (typeof node.key === 'number') return node.key;
const leftValue = evaluate(node.left); // Evaluate the left subtree
const rightValue = evaluate(node.right); // Evaluate the right subtree
// Perform the operation based on the current node's operator
switch (node.key) {
case '+':
return leftValue + rightValue;
case '-':
return leftValue - rightValue;
case '*':
return leftValue * rightValue;
case '/':
return rightValue !== 0 ? leftValue / rightValue : 0; // Handle division by zero
default:
throw new Error(`Unsupported operator: ${node.key}`);
}
}
expect(evaluate(expressionTree.root)).toBe(-27);
});
});