Test and debug the bug related to BinaryTreeNode's handling of object types.Revise the design of the constructor to incorporate abstract methods for enforcing the requirement that subclasses must implement the _createVertex and _createEdge methods.

This commit is contained in:
Revone 2023-08-23 11:39:01 +08:00
parent 60e08d3ac5
commit 849e3c6bca
18 changed files with 1872 additions and 82 deletions

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@
* @license MIT License
*/
import {BST, BSTNode} from './bst';
import type {AVLTreeDeleted, BinaryTreeNodeId, RecursiveAVLTreeNode} from '../types';
import type {AVLTreeDeleted, AVLTreeOptions, BinaryTreeNodeId, RecursiveAVLTreeNode} from '../types';
import {IBinaryTreeNode} from '../interfaces';
@ -15,6 +15,9 @@ export class AVLTreeNode<T, FAMILY extends AVLTreeNode<T, FAMILY> = RecursiveAVL
}
export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode<number>> extends BST<N> {
constructor(options?: AVLTreeOptions) {
super(options);
}
override _createNode(id: BinaryTreeNodeId, val: N['val'], count?: number): N {
const node = new AVLTreeNode<N['val'], N>(id, val, count);

View file

@ -12,23 +12,15 @@ import type {
BinaryTreeNodeId,
BinaryTreeNodePropertyName,
DFSOrderPattern,
KeyValObject,
NodeOrPropertyName,
RecursiveBinaryTreeNode,
ResultByProperty,
ResultsByProperty
} from '../types';
import {BinaryTreeOptions, FamilyPosition, LoopType} from '../types';
import {IBinaryTree, IBinaryTreeNode} from '../interfaces';
/* This enumeration defines the position of a node within a family tree composed of three associated nodes, where 'root' represents the root node of the family tree, 'left' represents the left child node, and 'right' represents the right child node. */
export enum FamilyPosition {root, left, right}
/**
* Enum representing different loop types.
*
* - `iterative`: Indicates the iterative loop type (with loops that use iterations).
* - `recursive`: Indicates the recursive loop type (with loops that call themselves).
*/
export enum LoopType { iterative = 1, recursive = 2}
export class BinaryTreeNode<T, FAMILY extends BinaryTreeNode<T, FAMILY> = RecursiveBinaryTreeNode<T>> implements IBinaryTreeNode<T, FAMILY> {
@ -163,11 +155,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode<n
* isDuplicatedVal based on the provided options.
* @param [options] - An optional object that can contain the following properties:
*/
constructor(options?: {
loopType?: LoopType,
autoIncrementId?: boolean,
isDuplicatedVal?: boolean
}) {
constructor(options?: BinaryTreeOptions) {
if (options !== undefined) {
const {
loopType = LoopType.iterative,
@ -296,7 +284,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode<n
* @returns The function `add` returns a `N` object if a new node is inserted, or `null` if no new node
* is inserted, or `undefined` if the insertion fails.
*/
add(id: BinaryTreeNodeId, val: N['val'], count?: number): N | null | undefined {
add(id: BinaryTreeNodeId, val?: N['val'], count?: number): N | null | undefined {
count = count ?? 1;
const _bfs = (root: N, newNode: N | null): N | undefined | null => {
@ -314,12 +302,12 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode<n
};
let inserted: N | null | undefined;
const needInsert = val !== null ? this._createNode(id, val, count) : null;
const needInsert = val !== null ? this._createNode(id, val ?? id, count) : null;
const existNode = val !== null ? this.get(id, 'id') : null;
if (this.root) {
if (existNode) {
existNode.count += count;
existNode.val = val;
existNode.val = val ?? id;
if (needInsert !== null) {
this._setCount(this.count + count);
inserted = existNode;
@ -328,7 +316,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode<n
inserted = _bfs(this.root, needInsert);
}
} else {
this._setRoot(val !== null ? this._createNode(id, val, count) : null);
this._setRoot(val !== null ? this._createNode(id, val ?? id, count) : null);
if (needInsert !== null) {
this._setSize(1);
this._setCount(count);
@ -395,32 +383,46 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode<n
}
for (const item of data) {
// TODO will this cause an issue?
const count = this._isDuplicatedVal ? 1 : map.get(item);
if (item instanceof BinaryTreeNode) {
inserted.push(this.add(item.id, item.val, item.count));
} else if (typeof item === 'number' && !this._autoIncrementId) {
if (!this._isDuplicatedVal) {
if (map.get(item) !== undefined) {
inserted.push(this.add(item, item, count));
map.delete(item);
}
} else {
inserted.push(this.add(item, item, 1));
}
} else {
if (item !== null) {
} else if (typeof item === 'number') {
if (!this._autoIncrementId) {
if (!this._isDuplicatedVal) {
if (map.get(item) !== undefined) {
inserted.push(this.add(++this._maxId, item, count));
inserted.push(this.add(item, item, count));
map.delete(item);
}
} else {
inserted.push(this.add(++this._maxId, item, 1));
inserted.push(this.add(item, item, 1));
}
}
} else if (item instanceof Object) {
if (!this._isDuplicatedVal) {
if (map.has(item)) {
let newId: number;
if (!this._autoIncrementId) {
if ((item as KeyValObject).hasOwnProperty('id')) {
newId = (item as KeyValObject).id;
} else {
console.warn('Object value must has an id property when the autoIncrementId is false');
break;
}
} else {
newId = this.maxId + 1;
this._setMaxId(newId);
}
inserted.push(this.add(newId, item, count));
map.delete(item);
}
} else {
inserted.push(this.add(Number.MAX_SAFE_INTEGER, item, 0));
inserted.push(this.add(++this._maxId, item, 1));
}
} else if (item === null) {
inserted.push(this.add(Number.MAX_SAFE_INTEGER, item, 0));
}
}
return inserted;
@ -851,7 +853,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode<n
* @returns a number, which is the sum of the values of the nodes in the subtree rooted at `subTreeRoot`.
*/
subTreeSum(subTreeRoot: N, propertyName ?: BinaryTreeNodePropertyName): number {
propertyName = propertyName ?? 'val';
propertyName = propertyName ?? 'id';
if (!subTreeRoot) return 0;
let sum = 0;

View file

@ -12,11 +12,10 @@ import type {
BSTDeletedResult,
RecursiveBSTNode
} from '../types';
import {BinaryTree, BinaryTreeNode, FamilyPosition, LoopType,} from './binary-tree';
import {BSTOptions, CP, FamilyPosition, LoopType} from '../types';
import {BinaryTree, BinaryTreeNode} from './binary-tree';
import {IBinaryTree, IBinaryTreeNode} from '../interfaces';
export enum CP {lt = -1, eq = 0, gt = 1}
export class BSTNode<T, FAMILY extends BSTNode<T, FAMILY> = RecursiveBSTNode<T>> extends BinaryTreeNode<T, FAMILY> implements IBinaryTreeNode<T, FAMILY> {
}
@ -26,10 +25,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode<number>> extends Binar
* The constructor function accepts an optional options object and sets the comparator property if provided.
* @param [options] - An optional object that can contain the following properties:
*/
constructor(options?: {
comparator?: BSTComparator,
loopType?: LoopType
}) {
constructor(options?: BSTOptions) {
super(options);
if (options !== undefined) {
const {comparator} = options;

View file

@ -1,3 +1,4 @@
// export * from './abstract-binary-tree';
export * from './binary-tree';
export * from './bst';
export * from './binary-indexed-tree';

View file

@ -1,5 +1,6 @@
import {BinaryTree, BinaryTreeNode, LoopType} from './binary-tree';
import {BinaryTree, BinaryTreeNode} from './binary-tree';
import {IBinaryTree, IBinaryTreeNode} from '../interfaces';
import {LoopType} from '../types';
enum RBColor { Red, Black }

View file

@ -74,14 +74,10 @@ export class DirectedEdge<T = number> extends AbstractEdge<T> {
}
// Strongly connected, One direction connected, Weakly connected
export class DirectedGraph<V extends DirectedVertex<any>, E extends DirectedEdge<any>> extends AbstractGraph<V, E> implements IDirectedGraph<V, E> {
private readonly _vertexConstructor: new (id: VertexId, val?: V['val']) => V;
private readonly _edgeConstructor: new (src: VertexId, dest: VertexId, weight?: number, val?: E['val']) => E;
export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E extends DirectedEdge<any> = DirectedEdge> extends AbstractGraph<V, E> implements IDirectedGraph<V, E> {
constructor(vertexConstructor: new (id: VertexId, val?: V['val']) => V, edgeConstructor: new (src: VertexId, dest: VertexId, weight?: number, val?: E['val']) => E) {
constructor() {
super();
this._vertexConstructor = vertexConstructor;
this._edgeConstructor = edgeConstructor;
}
private _outEdgeMap: Map<V, E[]> = new Map<V, E[]>();
@ -103,7 +99,7 @@ export class DirectedGraph<V extends DirectedVertex<any>, E extends DirectedEdge
* @param val
*/
_createVertex(id: VertexId, val?: V['val']): V {
return new this._vertexConstructor(id, val);
return new DirectedVertex(id, val ?? id) as V;
}
/**
@ -115,8 +111,7 @@ export class DirectedGraph<V extends DirectedVertex<any>, E extends DirectedEdge
* @param val
*/
_createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E['val']): E {
if (weight === undefined || weight === null) weight = 1;
return new this._edgeConstructor(src, dest, weight, val);
return new DirectedEdge(src, dest, weight ?? 1, val) as E;
}
/**

View file

@ -58,15 +58,10 @@ export class UndirectedEdge<T = number> extends AbstractEdge<T> {
// }
}
export class UndirectedGraph<V extends UndirectedVertex<any>, E extends UndirectedEdge<any>> extends AbstractGraph<V, E> {
export class UndirectedGraph<V extends UndirectedVertex<any> = UndirectedVertex, E extends UndirectedEdge<any> = UndirectedEdge> extends AbstractGraph<V, E> {
private readonly _vertexConstructor: new (id: VertexId, val?: V['val']) => V;
private readonly _edgeConstructor: new (src: VertexId, dest: VertexId, weight?: number, val?: E['val']) => E;
constructor(vertexConstructor: new (id: VertexId, val?: V['val']) => V, edgeConstructor: new (src: VertexId, dest: VertexId, weight?: number, val?: E['val']) => E) {
constructor() {
super();
this._vertexConstructor = vertexConstructor;
this._edgeConstructor = edgeConstructor;
this._edges = new Map<V, E[]>();
}
@ -83,20 +78,24 @@ export class UndirectedGraph<V extends UndirectedVertex<any>, E extends Undirect
* @param val
*/
_createVertex(id: VertexId, val?: V['val']): V {
return new this._vertexConstructor(id, val);
return new UndirectedVertex(id, val ?? id) as V;
}
/**
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
* This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
* @param src
* @param dest
* @param weight
* @param val
* The function _createEdge creates an undirected edge between two vertices with an optional weight and value.
* @param {VertexId} v1 - The parameter `v1` represents the first vertex of the edge. It is of type `VertexId`, which
* could be a unique identifier or label for the vertex.
* @param {VertexId} v2 - The parameter `v2` represents the second vertex of the edge. It is of type `VertexId`, which
* is typically a unique identifier for a vertex in a graph.
* @param {number} [weight] - The weight parameter is an optional number that represents the weight of the edge. If no
* weight is provided, the default value is 1.
* @param [val] - The `val` parameter is an optional value that can be assigned to the edge. It can be of any type and
* is used to store additional information or data associated with the edge.
* @returns an instance of the UndirectedEdge class, casted as type E.
*/
_createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E['val']): E {
if (weight === undefined || weight === null) weight = 1;
return new this._edgeConstructor(src, dest, weight, val);
_createEdge(v1: VertexId, v2: VertexId, weight?: number, val?: E['val']): E {
return new UndirectedEdge(v1, v2, weight ?? 1, val) as E;
}
/**

View file

@ -1,5 +1,4 @@
import {BinaryTreeNodeId} from '../types';
import {FamilyPosition} from '../binary-tree';
import {BinaryTreeNodeId, FamilyPosition} from '../types';
export interface IBinaryTreeNode<T, FAMILY extends IBinaryTreeNode<T, FAMILY>> {
_createNode(id: BinaryTreeNodeId, val: T | null, count?: number): FAMILY | null;

View file

@ -0,0 +1,14 @@
import {AbstractBinaryTreeNode} from '../binary-tree/abstract-binary-tree';
/**
* Enum representing different loop types.
*
* - `iterative`: Indicates the iterative loop type (with loops that use iterations).
* - `recursive`: Indicates the recursive loop type (with loops that call themselves).
*/
export enum LoopType { iterative = 1, recursive = 2}
/* This enumeration defines the position of a node within a family tree composed of three associated nodes, where 'root' represents the root node of the family tree, 'left' represents the left child node, and 'right' represents the right child node. */
export enum FamilyPosition {root, left, right}
export type RecursiveAbstractBinaryTreeNode<T> = AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, AbstractBinaryTreeNode<T, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View file

@ -1,4 +1,5 @@
import {AVLTreeNode} from '../binary-tree';
import {BSTOptions} from './bst';
export type AVLTreeDeleted<N> = {
deleted: N | null;
@ -6,3 +7,4 @@ export type AVLTreeDeleted<N> = {
}
export type RecursiveAVLTreeNode<T> = AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type AVLTreeOptions = BSTOptions & {};

View file

@ -1,4 +1,5 @@
import {BinaryTreeNode} from '../binary-tree';
import {LoopType} from './abstract-binary-tree';
export type BinaryTreeNodePropertyName = 'id' | 'val' | 'count';
export type NodeOrPropertyName = 'node' | BinaryTreeNodePropertyName;
@ -8,3 +9,8 @@ export type BinaryTreeDeleted<N> = { deleted: N | null | undefined, needBalanced
export type ResultByProperty<N extends BinaryTreeNode<N['val'], N>> = N['val'] | N | number | BinaryTreeNodeId;
export type ResultsByProperty<N extends BinaryTreeNode<N['val'], N>> = ResultByProperty<N>[];
export type RecursiveBinaryTreeNode<T> = BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type BinaryTreeOptions = {
loopType?: LoopType,
autoIncrementId?: boolean,
isDuplicatedVal?: boolean
}

View file

@ -1,6 +1,11 @@
import {BSTNode} from '../binary-tree';
import type {BinaryTreeNodeId} from './binary-tree';
import type {BinaryTreeNodeId, BinaryTreeOptions} from './binary-tree';
export type BSTComparator = (a: BinaryTreeNodeId, b: BinaryTreeNodeId) => number;
export type BSTDeletedResult<N extends BSTNode<N['val'], N>> = { deleted: N | null, needBalanced: N | null };
export type RecursiveBSTNode<T> = BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type BSTOptions = BinaryTreeOptions & {
comparator?: BSTComparator,
}
export enum CP {lt = 'lt', eq = 'eq', gt = 'gt'}

View file

@ -0,0 +1,2 @@
export type IdObject = { id: number; } & { [key: string]: any; };
export type KeyValObject = { [key: string]: any };

View file

@ -4,9 +4,11 @@ export * from './avl-tree';
export * from './segment-tree';
export * from './tree-multiset';
export * from './abstract-graph';
export * from './abstract-binary-tree';
export * from './directed-graph';
export * from './priority-queue';
export * from './heap';
export * from './singly-linked-list';
export * from './doubly-linked-list';
export * from './navigator';
export * from './navigator';
export * from './helpers';

View file

@ -1,7 +1,7 @@
import {BST, BSTNode} from '../../../../src';
describe('BST Case6', () => {
it('should perform various operations on a Binary Search Tree', () => {
describe('BST operations test', () => {
it('should perform various operations on a Binary Search Tree with numeric values', () => {
const tree = new BST();
expect(tree).toBeInstanceOf(BST);
@ -190,4 +190,203 @@ describe('BST Case6', () => {
expect(bfsNodes[1].id).toBe(12);
expect(bfsNodes[2].id).toBe(16);
});
it('should perform various operations on a Binary Search Tree with object values', () => {
const objBST = new BST<BSTNode<{ id: number, keyA: number }>>({autoIncrementId: false});
expect(objBST).toBeInstanceOf(BST);
const values = [{id: 11, keyA: 11}, {id: 3, keyA: 3}, {id: 15, keyA: 15}, {id: 1, keyA: 1}, {
id: 8,
keyA: 8
}, {id: 13, keyA: 13}, {id: 16, keyA: 16}, {id: 2, keyA: 2}, {id: 6, keyA: 6}, {id: 9, keyA: 9}, {
id: 12,
keyA: 12
}, {id: 14, keyA: 14}, {id: 4, keyA: 4}, {id: 7, keyA: 7}, {id: 10, keyA: 10}, {id: 5, keyA: 5}];
objBST.addMany(values);
expect(objBST.root).toBeInstanceOf(BSTNode);
if (objBST.root) expect(objBST.root.id).toBe(11);
expect(objBST.count).toBe(16);
expect(objBST.has(6)).toBe(true);
const node6 = objBST.get(6);
expect(node6 && objBST.getHeight(node6)).toBe(2);
expect(node6 && objBST.getDepth(node6)).toBe(3);
const nodeId10 = objBST.get(10, 'id');
expect(nodeId10?.id).toBe(10);
const nodeVal9 = objBST.get(9, 'id');
expect(nodeVal9?.id).toBe(9);
const nodesByCount1 = objBST.getNodes(1, 'count');
expect(nodesByCount1.length).toBe(16);
const leftMost = objBST.getLeftMost();
expect(leftMost?.id).toBe(1);
const node15 = objBST.get(15);
expect(node15?.val).toEqual({id: 15, keyA: 15});
const minNodeBySpecificNode = node15 && objBST.getLeftMost(node15);
expect(minNodeBySpecificNode?.id).toBe(12);
const subTreeSum = node15 && objBST.subTreeSum(node15);
expect(subTreeSum).toBe(70);
const lesserSum = objBST.lesserSum(10);
expect(lesserSum).toBe(45);
expect(node15).toBeInstanceOf(BSTNode);
if (node15 instanceof BSTNode) {
const subTreeAdd = objBST.subTreeAdd(node15, 1, 'count');
expect(subTreeAdd).toBeDefined();
}
const node11 = objBST.get(11);
expect(node11).toBeInstanceOf(BSTNode);
if (node11 instanceof BSTNode) {
const allGreaterNodesAdded = objBST.allGreaterNodesAdd(node11, 2, 'count');
expect(allGreaterNodesAdded).toBeDefined();
}
const dfsInorderNodes = objBST.DFS('in', 'node');
expect(dfsInorderNodes[0].id).toBe(1);
expect(dfsInorderNodes[dfsInorderNodes.length - 1].id).toBe(16);
objBST.balance();
expect(objBST.isBalanced()).toBe(true);
const bfsNodesAfterBalanced = objBST.BFS('node');
expect(bfsNodesAfterBalanced[0].id).toBe(8);
expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].id).toBe(16);
const removed11 = objBST.remove(11, true);
expect(removed11).toBeInstanceOf(Array);
expect(removed11[0]).toBeDefined();
expect(removed11[0].deleted).toBeDefined();
if (removed11[0].deleted) expect(removed11[0].deleted.id).toBe(11);
expect(objBST.isAVLBalanced()).toBe(true);
expect(node15 && objBST.getHeight(node15)).toBe(2);
const removed1 = objBST.remove(1, true);
expect(removed1).toBeInstanceOf(Array);
expect(removed1[0]).toBeDefined();
expect(removed1[0].deleted).toBeDefined();
if (removed1[0].deleted) expect(removed1[0].deleted.id).toBe(1);
expect(objBST.isAVLBalanced()).toBe(true);
expect(objBST.getHeight()).toBe(4);
const removed4 = objBST.remove(4, true);
expect(removed4).toBeInstanceOf(Array);
expect(removed4[0]).toBeDefined();
expect(removed4[0].deleted).toBeDefined();
if (removed4[0].deleted) expect(removed4[0].deleted.id).toBe(4);
expect(objBST.isAVLBalanced()).toBe(true);
expect(objBST.getHeight()).toBe(4);
const removed10 = objBST.remove(10, true);
expect(removed10).toBeInstanceOf(Array);
expect(removed10[0]).toBeDefined();
expect(removed10[0].deleted).toBeDefined();
if (removed10[0].deleted) expect(removed10[0].deleted.id).toBe(10);
expect(objBST.isAVLBalanced()).toBe(false);
expect(objBST.getHeight()).toBe(4);
const removed15 = objBST.remove(15, true);
expect(removed15).toBeInstanceOf(Array);
expect(removed15[0]).toBeDefined();
expect(removed15[0].deleted).toBeDefined();
if (removed15[0].deleted) expect(removed15[0].deleted.id).toBe(15);
expect(objBST.isAVLBalanced()).toBe(true);
expect(objBST.getHeight()).toBe(3);
const removed5 = objBST.remove(5, true);
expect(removed5).toBeInstanceOf(Array);
expect(removed5[0]).toBeDefined();
expect(removed5[0].deleted).toBeDefined();
if (removed5[0].deleted) expect(removed5[0].deleted.id).toBe(5);
expect(objBST.isAVLBalanced()).toBe(true);
expect(objBST.getHeight()).toBe(3);
const removed13 = objBST.remove(13, true);
expect(removed13).toBeInstanceOf(Array);
expect(removed13[0]).toBeDefined();
expect(removed13[0].deleted).toBeDefined();
if (removed13[0].deleted) expect(removed13[0].deleted.id).toBe(13);
expect(objBST.isAVLBalanced()).toBe(true);
expect(objBST.getHeight()).toBe(3);
const removed3 = objBST.remove(3, true);
expect(removed3).toBeInstanceOf(Array);
expect(removed3[0]).toBeDefined();
expect(removed3[0].deleted).toBeDefined();
if (removed3[0].deleted) expect(removed3[0].deleted.id).toBe(3);
expect(objBST.isAVLBalanced()).toBe(false);
expect(objBST.getHeight()).toBe(3);
const removed8 = objBST.remove(8, true);
expect(removed8).toBeInstanceOf(Array);
expect(removed8[0]).toBeDefined();
expect(removed8[0].deleted).toBeDefined();
if (removed8[0].deleted) expect(removed8[0].deleted.id).toBe(8);
expect(objBST.isAVLBalanced()).toBe(true);
expect(objBST.getHeight()).toBe(3);
const removed6 = objBST.remove(6, true);
expect(removed6).toBeInstanceOf(Array);
expect(removed6[0]).toBeDefined();
expect(removed6[0].deleted).toBeDefined();
if (removed6[0].deleted) expect(removed6[0].deleted.id).toBe(6);
expect(objBST.remove(6, true).length).toBe(0);
expect(objBST.isAVLBalanced()).toBe(false);
expect(objBST.getHeight()).toBe(3);
const removed7 = objBST.remove(7, true);
expect(removed7).toBeInstanceOf(Array);
expect(removed7[0]).toBeDefined();
expect(removed7[0].deleted).toBeDefined();
if (removed7[0].deleted) expect(removed7[0].deleted.id).toBe(7);
expect(objBST.isAVLBalanced()).toBe(false);
expect(objBST.getHeight()).toBe(3);
const removed9 = objBST.remove(9, true);
expect(removed9).toBeInstanceOf(Array);
expect(removed9[0]).toBeDefined();
expect(removed9[0].deleted).toBeDefined();
if (removed9[0].deleted) expect(removed9[0].deleted.id).toBe(9);
expect(objBST.isAVLBalanced()).toBe(false);
expect(objBST.getHeight()).toBe(3);
const removed14 = objBST.remove(14, true);
expect(removed14).toBeInstanceOf(Array);
expect(removed14[0]).toBeDefined();
expect(removed14[0].deleted).toBeDefined();
if (removed14[0].deleted) expect(removed14[0].deleted.id).toBe(14);
expect(objBST.isAVLBalanced()).toBe(false);
expect(objBST.getHeight()).toBe(2);
expect(objBST.isAVLBalanced()).toBe(false);
const bfsIDs = objBST.BFS();
expect(bfsIDs[0]).toBe(2);
expect(bfsIDs[1]).toBe(12);
expect(bfsIDs[2]).toBe(16);
const bfsNodes = objBST.BFS('node');
expect(bfsNodes[0].id).toBe(2);
expect(bfsNodes[1].id).toBe(12);
expect(bfsNodes[2].id).toBe(16);
});
});

View file

@ -1,10 +1,10 @@
import {DirectedEdge, DirectedGraph, DirectedVertex, VertexId} from '../../../../src';
describe('DirectedGraph Operation Test', () => {
let graph: DirectedGraph<DirectedVertex, DirectedEdge>;
let graph: DirectedGraph;
beforeEach(() => {
graph = new DirectedGraph(DirectedVertex, DirectedEdge);
graph = new DirectedGraph();
});
@ -100,13 +100,19 @@ class MyEdge<E extends string> extends DirectedEdge<E> {
}
class MyDirectedGraph<V extends MyVertex<string>, E extends MyEdge<string>> extends DirectedGraph<V, E> {
_createVertex(id: VertexId, val: V['val']): V {
return new MyVertex(id, val) as V;
}
_createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E['val']): E {
return new MyEdge(src, dest, weight ?? 1, val) as E;
}
}
describe('Inherit from DirectedGraph and perform operations', () => {
let myGraph = new MyDirectedGraph<MyVertex<string>, MyEdge<string>>(MyVertex, MyEdge);
let myGraph = new MyDirectedGraph<MyVertex<string>, MyEdge<string>>();
beforeEach(() => {
myGraph = new MyDirectedGraph(MyVertex, MyEdge);
myGraph = new MyDirectedGraph();
});
it('Add vertices', () => {
@ -211,7 +217,7 @@ describe('Inherit from DirectedGraph and perform operations', () => {
});
describe('Inherit from DirectedGraph and perform operations test2.', () => {
const myGraph = new MyDirectedGraph<MyVertex<string>, MyEdge<string>>(MyVertex, MyEdge);
const myGraph = new MyDirectedGraph<MyVertex<string>, MyEdge<string>>();
it('should test graph operations', () => {
const vertex1 = new MyVertex(1, 'data1');

View file

@ -1,5 +1,64 @@
describe('UndirectedGraph Operation Test', () => {
it('should xxx', function () {
import {UndirectedEdge, UndirectedGraph, UndirectedVertex} from '../../../../src';
describe('UndirectedGraph Operation Test', () => {
let graph: UndirectedGraph;
beforeEach(() => {
graph = new UndirectedGraph();
});
it('should add vertices', () => {
const vertex1 = new UndirectedVertex('A');
const vertex2 = new UndirectedVertex('B');
graph.addVertex(vertex1);
graph.addVertex(vertex2);
expect(graph.hasVertex(vertex1)).toBe(true);
expect(graph.hasVertex(vertex2)).toBe(true);
});
it('should add edges', () => {
const vertex1 = new UndirectedVertex('A');
const vertex2 = new UndirectedVertex('B');
const edge = new UndirectedEdge('A', 'B');
graph.addVertex(vertex1);
graph.addVertex(vertex2);
graph.addEdge(edge);
expect(graph.hasEdge('A', 'B')).toBe(true);
expect(graph.hasEdge('B', 'A')).toBe(true);
});
it('should remove edges', () => {
const vertex1 = new UndirectedVertex('A');
const vertex2 = new UndirectedVertex('B');
const edge = new UndirectedEdge('A', 'B');
graph.addVertex(vertex1);
graph.addVertex(vertex2);
graph.addEdge(edge);
expect(graph.removeEdge(edge)).toBe(edge);
expect(graph.hasEdge('A', 'B')).toBe(false);
});
it('should perform topological sort', () => {
const vertexA = new UndirectedVertex('A');
const vertexB = new UndirectedVertex('B');
const vertexC = new UndirectedVertex('C');
const edgeAB = new UndirectedEdge('A', 'B');
const edgeBC = new UndirectedEdge('B', 'C');
graph.addVertex(vertexA);
graph.addVertex(vertexB);
graph.addVertex(vertexC);
graph.addEdge(edgeAB);
graph.addEdge(edgeBC);
const dijkstraResult = graph.dijkstra('A');
// TODO to be tested
});
});