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.
This commit is contained in:
Revone 2023-12-23 09:41:04 +08:00
parent 0708509f3e
commit 677340e99c
5 changed files with 50 additions and 47 deletions

View file

@ -1075,7 +1075,7 @@ export class BinaryTree<
* possible values:
* @returns a boolean value.
*/
isSubtreeBST(beginRoot: BTNKeyOrNode<K, N>, iterationType = this.iterationType): boolean {
isBST(beginRoot: BTNKeyOrNode<K, N> = 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)

View file

@ -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;
}

View file

@ -1,6 +1,6 @@
export enum BSTVariant {
MIN = 'MIN',
MAX = 'MAX'
STANDARD = 'STANDARD',
INVERSE = 'INVERSE'
}
export enum CP {

View file

@ -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)]);
});

View file

@ -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<number, number>();
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<number, number>([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<number, number>();
const inputSize = 10000; // Adjust input sizes as needed