From 677340e99cd90e11917f63a3cb1d874a932f630b Mon Sep 17 00:00:00 2001 From: Revone Date: Sat, 23 Dec 2023 09:41:04 +0800 Subject: [PATCH] refactor: Remove isSubtreeBST; using isBST alone is sufficient. Update BSTVariant to use STANDARD and INVERSE instead of MIN and MAX. feat: Enhance isBST to support the detection of inverse binary search trees. --- .../binary-tree/binary-tree.ts | 61 ++++++++----------- src/data-structures/binary-tree/bst.ts | 6 +- src/types/common.ts | 4 +- .../binary-tree/binary-tree.test.ts | 8 +-- .../data-structures/binary-tree/bst.test.ts | 18 +++++- 5 files changed, 50 insertions(+), 47 deletions(-) diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index 146ab3d..b26f4a4 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -1075,7 +1075,7 @@ export class BinaryTree< * possible values: * @returns a boolean value. */ - isSubtreeBST(beginRoot: BTNKeyOrNode, iterationType = this.iterationType): boolean { + isBST(beginRoot: BTNKeyOrNode = this.root, iterationType = this.iterationType): boolean { // TODO there is a bug beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return true; @@ -1088,47 +1088,34 @@ export class BinaryTree< return dfs(cur.left, min, numKey) && dfs(cur.right, numKey, max); }; - return dfs(beginRoot, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER); + const isStandardBST = dfs(beginRoot, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER); + const isInverseBST = dfs(beginRoot, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER); + return isStandardBST || isInverseBST; } else { - const stack = []; - let prev = Number.MIN_SAFE_INTEGER, - curr: N | null | undefined = beginRoot; - while (curr || stack.length > 0) { - while (curr) { - stack.push(curr); - curr = curr.left; + const checkBST = (checkMax = false) => { + const stack = []; + let prev = checkMax ? Number.MAX_SAFE_INTEGER : Number.MIN_SAFE_INTEGER; + // @ts-ignore + let curr: N | null | undefined = beginRoot; + while (curr || stack.length > 0) { + while (curr) { + stack.push(curr); + curr = curr.left; + } + curr = stack.pop()!; + const numKey = this.extractor(curr.key); + if (!curr || (!checkMax && prev >= numKey) || (checkMax && prev <= numKey)) return false; + prev = numKey; + curr = curr.right; } - curr = stack.pop()!; - const numKey = this.extractor(curr.key); - if (!curr || prev >= numKey) return false; - prev = numKey; - curr = curr.right; - } - return true; + return true; + }; + const isStandardBST = checkBST(false), + isInverseBST = checkBST(true); + return isStandardBST || isInverseBST; } } - /** - * Time Complexity: O(n) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(n) - * Space Complexity: O(1) - * - * The function checks if a binary tree is a binary search tree. - * @param iterationType - The parameter "iterationType" is used to specify the type of iteration to - * be used when checking if the binary tree is a binary search tree (BST). It is an optional - * parameter with a default value of "this.iterationType". The value of "this.iterationType" is - * expected to be - * @returns a boolean value. - */ - isBST(iterationType = this.iterationType): boolean { - if (this.root === null) return true; - return this.isSubtreeBST(this.root, iterationType); - } - /** * Time Complexity: O(n) * Space Complexity: O(1) diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 13048a8..b1656a3 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -121,7 +121,7 @@ export class BST< return this._root; } - protected _variant = BSTVariant.MIN; + protected _variant = BSTVariant.STANDARD; get variant() { return this._variant; @@ -390,7 +390,7 @@ export class BST< let current = this.ensureNode(beginRoot); if (!current) return undefined; - if (this._variant === BSTVariant.MIN) { + if (this._variant === BSTVariant.STANDARD) { // For BSTVariant.MIN, find the rightmost node while (current.right !== undefined) { current = current.right; @@ -761,7 +761,7 @@ export class BST< protected _compare(a: K, b: K): CP { const extractedA = this.extractor(a); const extractedB = this.extractor(b); - const compared = this.variant === BSTVariant.MIN ? extractedA - extractedB : extractedB - extractedA; + const compared = this.variant === BSTVariant.STANDARD ? extractedA - extractedB : extractedB - extractedA; return compared > 0 ? CP.gt : compared < 0 ? CP.lt : CP.eq; } diff --git a/src/types/common.ts b/src/types/common.ts index 8358d54..d7c9cf4 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -1,6 +1,6 @@ export enum BSTVariant { - MIN = 'MIN', - MAX = 'MAX' + STANDARD = 'STANDARD', + INVERSE = 'INVERSE' } export enum CP { diff --git a/test/unit/data-structures/binary-tree/binary-tree.test.ts b/test/unit/data-structures/binary-tree/binary-tree.test.ts index 61411f8..895b8c8 100644 --- a/test/unit/data-structures/binary-tree/binary-tree.test.ts +++ b/test/unit/data-structures/binary-tree/binary-tree.test.ts @@ -224,15 +224,15 @@ describe('BinaryTree', () => { new BinaryTreeNode(4, 4) ]); - expect(tree.isSubtreeBST(tree.getNode(4), IterationType.RECURSIVE)).toBe(true); - expect(tree.isSubtreeBST(tree.getNode(4), IterationType.ITERATIVE)).toBe(true); + expect(tree.isBST(tree.getNode(4), IterationType.RECURSIVE)).toBe(true); + expect(tree.isBST(tree.getNode(4), IterationType.ITERATIVE)).toBe(true); }); it('should isSubtreeBST', () => { tree.addMany([4, 2, 6, 1, 3, 5, 7, 4]); - expect(tree.isSubtreeBST(tree.getNode(4), IterationType.RECURSIVE)).toBe(true); - expect(tree.isSubtreeBST(tree.getNode(4), IterationType.ITERATIVE)).toBe(true); + expect(tree.isBST(tree.getNode(4), IterationType.RECURSIVE)).toBe(true); + expect(tree.isBST(tree.getNode(4), IterationType.ITERATIVE)).toBe(true); expect(tree.getNodes(2, undefined, false, null)).toEqual([]); expect(tree.getNodes(tree.getNodeByKey(2), undefined, false, tree.root)).toEqual([tree.getNodeByKey(2)]); }); diff --git a/test/unit/data-structures/binary-tree/bst.test.ts b/test/unit/data-structures/binary-tree/bst.test.ts index 0408813..32a69ba 100644 --- a/test/unit/data-structures/binary-tree/bst.test.ts +++ b/test/unit/data-structures/binary-tree/bst.test.ts @@ -1,4 +1,4 @@ -import { BinaryTreeNode, BST, BSTNode, CP, IterationType } from '../../../../src'; +import { BinaryTreeNode, BST, BSTNode, BSTVariant, CP, IterationType } from '../../../../src'; import { isDebugTest } from '../../../config'; const isDebug = isDebugTest; @@ -47,6 +47,8 @@ describe('BST operations test', () => { const leftMost = bst.getLeftMost(); expect(leftMost?.key).toBe(1); + expect(bst.isBST()).toBe(true); + const node15 = bst.getNode(15); const minNodeBySpecificNode = node15 && bst.getLeftMost(node15); expect(minNodeBySpecificNode?.key).toBe(12); @@ -795,6 +797,20 @@ describe('BST operations test recursively', () => { }); }); +describe('BST isBST', function () { + test('isBST', () => { + const bst = new BST(); + bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]); + expect(bst.isBST()).toBe(true); + }); + + test('isBST when variant is Max', () => { + const bst = new BST([1, 2, 3, 9, 8, 5, 6, 7, 4], { variant: BSTVariant.INVERSE }); + bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]); + expect(bst.isBST()).toBe(true); + }); +}); + describe('BST Performance test', function () { const bst = new BST(); const inputSize = 10000; // Adjust input sizes as needed