Skip to content

Commit

Permalink
Refactor tree struct (#43)
Browse files Browse the repository at this point in the history
* feat: update tsc prettier to fit override modifier

* refactor: udpate ds override & isEmpty

* refactor(tree): update bst adt & treap now extends bst

* test: update tree test
  • Loading branch information
kscarrot committed Jun 13, 2021
1 parent f571380 commit 17e521b
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 127 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"husky": "^4.2.5",
"jest": "^26.6.3",
"lint-staged": "^10.2.9",
"prettier": "^2.0.5",
"prettier": "^2.3.1",
"typescript": "^4.3.2"
},
"husky": {
Expand Down
9 changes: 7 additions & 2 deletions src/datastructure/ADT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,12 @@ export interface HashTableADT<T> {
get: (key: any) => T | null
set: (key: any, value: T) => void
delete: (key: string) => void
clear: () => void
}
export interface HashSetADT<T> {
size: number
add: (value: T) => void
delete: (value: T) => boolean
has: (value: T) => boolean
clear: () => void
}

export interface HeapADT<T> {
Expand All @@ -62,8 +60,15 @@ export interface PriorityQueueADT<T> extends QueueADT<T> {

export interface SearchTree<T> {
size: number
isEmpty: boolean
insert: (value: T) => void
delete: (value: T) => void
getRank: (value: T) => number
getKth: (index: number) => T | null
max: T | null
min: T | null
getPrev: (value: T) => T | null
getNext: (value: T) => T | null
}

export interface TreapADT<T> extends SearchTree<T>, HeapADT<T> {}
21 changes: 11 additions & 10 deletions src/datastructure/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class SinglyLinkedNode<T> extends BaseNode<T> {

export class DoublyLinkedNode<T> extends SinglyLinkedNode<T> {
public prev: DoublyLinkedNode<T> | null
public next: DoublyLinkedNode<T> | null
public override next: DoublyLinkedNode<T> | null
constructor(value: T) {
super(value)
this.next = null
Expand All @@ -24,8 +24,8 @@ export class DoublyLinkedNode<T> extends SinglyLinkedNode<T> {
}

export class CircularLinkedNode<T> extends DoublyLinkedNode<T> {
public prev: CircularLinkedNode<T>
public next: CircularLinkedNode<T>
public override prev: CircularLinkedNode<T>
public override next: CircularLinkedNode<T>
constructor(value: T, prev?: CircularLinkedNode<T>, next?: CircularLinkedNode<T>) {
super(value)
this.prev = prev ?? this
Expand All @@ -42,11 +42,15 @@ export class TreeNode<T> {
this.value = value
if (parent) this.parent = parent
}

get isLeaf() {
return this.right === null && this.left === null
}
}

export class TreapNode<T> extends TreeNode<T> {
left: TreapNode<T> | null = null
right: TreapNode<T> | null = null
override left: TreapNode<T> | null = null
override right: TreapNode<T> | null = null
size: number = 1
key: number
constructor(value: T) {
Expand All @@ -63,15 +67,12 @@ export class TreapNode<T> extends TreeNode<T> {
const inverse = (side: 'right' | 'left') => (side === 'right' ? 'left' : 'right')

export class TreapRotateNode<T> extends TreapNode<T> {
left: TreapRotateNode<T> | null
right: TreapRotateNode<T> | null
size: number = 1
key: number
override left: TreapRotateNode<T> | null
override right: TreapRotateNode<T> | null
constructor(value: T, left?: TreapRotateNode<T>, right?: TreapRotateNode<T>) {
super(value)
this.left = left ?? null
this.right = right ?? null
this.key = Math.random()
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/datastructure/heap/Heap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class MaxHeap<T> extends MinHeap<T> {
this.cmp.reverse()
}

[Symbol.iterator] = super.traverse
override [Symbol.iterator] = super.traverse
}

export { MinHeap, MaxHeap }
29 changes: 27 additions & 2 deletions src/datastructure/tree/BinarySearchTree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import BinarySearchTree from './BinarySearchTree'

test('test binary search tree', () => {
const bst = new BinarySearchTree()
expect(bst.isEmpty()).toBe(true)
expect(bst.isEmpty).toBe(true)
expect(bst.max).toBe(null)
expect(bst.min).toBe(null)
expect(bst.getKth(1)).toBe(null)
bst.insert(10).insert(12).insert(3).insert(4).insert(13).insert(9).insert(11)
/**
* 10
Expand All @@ -11,11 +14,33 @@ test('test binary search tree', () => {
* 9
*/
expect(bst.size).toBe(7)
expect(bst.getKth(1)).toBe(3)
expect([...bst]).toStrictEqual([3, 4, 9, 10, 11, 12, 13])
bst.delete(12)
expect([...bst]).toStrictEqual([3, 4, 9, 10, 11, 13])
expect(bst.search(4)).toBe(4)
expect(bst.search(12)).toBeNull()
expect([bst.getKth(1), bst.getKth(2), bst.getKth(3), bst.getKth(4), bst.getKth(5)]).toStrictEqual([3, 4, 9, 10, 11])
expect([bst.getRank(3), bst.getRank(4), bst.getRank(9)]).toStrictEqual([1, 2, 3])
expect(bst.getNext(9)).toBe(10)
expect(bst.getPrev(9)).toBe(4)
expect(bst.min).toBe(3)
expect(bst.max).toBe(13)
})

test('test bst protect methods ', () => {
const bst = new BinarySearchTree()
bst.insert(10).insert(12).insert(3).insert(4).insert(13).insert(9).insert(11).insert(2).insert(1)
/**
* 10
* 3 12
* 2 4 11 13
* 1 9
*/
// eslint-disable-next-line @typescript-eslint/dot-notation
expect(bst['getNodeSize'](bst.root)).toBe(9)
// eslint-disable-next-line @typescript-eslint/dot-notation
expect(bst['getNodeSize'](bst.root?.left?.left ?? null)).toBe(2)
})

test('testing bst boundary case', () => {
Expand All @@ -27,7 +52,7 @@ test('testing bst boundary case', () => {
bst.delete(4)
expect([...bst]).toStrictEqual([1, 2])
bst.clear()
expect(bst.isEmpty()).toBe(true)
expect(bst.isEmpty).toBe(true)
})

test('test private method findmin', () => {
Expand Down
99 changes: 91 additions & 8 deletions src/datastructure/tree/BinarySearchTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,56 @@ import { TreeNode } from '../Node'
import { Comparator, compareFunction } from '../../util'

class BinarySearchTree<T> implements SearchTree<T> {
private root: TreeNode<T> | null = null
root: TreeNode<T> | null = null
protected cmp: Comparator<T>
size: number = 0
constructor(cmpFn?: compareFunction<T>) {
this.cmp = new Comparator(cmpFn)
}

isEmpty() {
get isEmpty() {
return this.size === 0
}

private find(value: T, curRoot: TreeNode<T> | null): TreeNode<T> | null {
private searchNode(value: T, curRoot: TreeNode<T> | null): TreeNode<T> | null {
if (curRoot === null) return null
if (curRoot.value === value) return curRoot
const child = this.cmp.lt(value, curRoot.value) ? 'left' : 'right'
return curRoot[child] ? this.find(value, curRoot[child]) : null
return curRoot[child] ? this.searchNode(value, curRoot[child]) : null
}

private findMin(node: TreeNode<T>) {
protected searchMinNode(node: TreeNode<T>) {
let minNode = node
while (minNode?.left) {
minNode = minNode.left
}
return minNode
}

get min() {
if (this.root === null) {
return null
} else {
return this.searchMinNode(this.root).value
}
}

protected searchMaxNode(node: TreeNode<T>) {
let maxNode = node
while (maxNode?.right) {
maxNode = maxNode.right
}
return maxNode
}

get max() {
if (this.root === null) {
return null
} else {
return this.searchMaxNode(this.root).value
}
}

private replaceNodeInParent(curNode: TreeNode<T> | null, newNode: TreeNode<T> | null) {
const parent = curNode?.parent
if (parent) {
Expand Down Expand Up @@ -59,11 +83,11 @@ class BinarySearchTree<T> implements SearchTree<T> {
}

delete(value: T) {
const node = this.find(value, this.root)
const node = this.searchNode(value, this.root)
if (node == null) return null
const temp = node.value
if (node.left && node.right) {
const result = this.findMin(node.right)
const result = this.searchMinNode(node.right)
this.delete(result.value)
node.value = result.value
} else {
Expand All @@ -74,10 +98,69 @@ class BinarySearchTree<T> implements SearchTree<T> {
}

search(value: T) {
const node = this.find(value, this.root)
const node = this.searchNode(value, this.root)
return node ? node.value : null
}

protected getNodeSize(node: TreeNode<T> | null): number {
if (node === null) {
return 0
} else if (node.isLeaf) {
return 1
} else {
if (node.left === null) {
return this.getNodeSize(node.right) + 1
}
if (node.right === null) {
return this.getNodeSize(node.left) + 1
}

return this.getNodeSize(node.right) + this.getNodeSize(node.left) + 1
}
}

protected getKthNode(index: number, node: TreeNode<T> | null): TreeNode<T> | null {
if (node === null) {
return null
}
const leftSize = this.getNodeSize(node.left)
if (leftSize > index) {
return this.getKthNode(index, node.left)
} else if (leftSize < index) {
return this.getKthNode(index - leftSize - 1, node.right)
} else {
return node
}
}

getKth(index: number) {
const result = this.getKthNode(index - 1, this.root)
if (result === null) {
return null
} else {
return result.value
}
}

protected getNodeRank(tree: TreeNode<T> | null, value: T): number {
if (tree == null) return 0
return this.cmp.lt(value, tree.value)
? this.getNodeRank(tree.left, value)
: this.getNodeRank(tree.right, value) + this.getNodeSize(tree.left) + 1
}

getRank(value: T): number {
return this.getNodeRank(this.root, value)
}

getPrev(value: T) {
return this.getKth(this.getRank(value) - 1)
}

getNext(value: T) {
return this.getKth(this.getRank(value) + 1)
}

clear() {
this.root = null
this.size = 0
Expand Down
12 changes: 6 additions & 6 deletions src/datastructure/tree/Treap.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Treap from './Treap'

test('test treap', () => {
const tp = new Treap()
expect(tp.isEmpty()).toBe(true)
const tp = new Treap<number>()
expect(tp.isEmpty).toBe(true)
expect(tp.getKth(1)).toBeNull()
expect(tp.deleteKth(1)).toBeNull()
expect(tp.delete(1)).toBeNull()
Expand All @@ -20,7 +20,7 @@ test('test treap', () => {
expect(tp.getRank(99)).toBe(5)
expect([...tp]).toStrictEqual([5, 10, 15, 50, 99])

expect(tp.getPre(15)).toBe(10)
expect(tp.getPrev(15)).toBe(10)
expect(tp.getNext(15)).toBe(50)
expect(tp.deleteKth(2)).toBe(10)
expect([...tp]).toStrictEqual([5, 15, 50, 99])
Expand All @@ -30,8 +30,8 @@ test('test treap', () => {
// [5, 9, 15, 50, 99, 999]
tp.insert(9).insert(999)
const sp = tp.splitV(tp.root, 99)
expect(tp.getNodeKth(sp.first, 1)).toBe(5)
expect(tp.getNodeKth(sp.second, 1)).toBe(99)
expect(tp.getNodeKth(sp.second, 2)).toBe(999)
expect(tp.getNodeKth(sp.first, 1)?.value).toBe(5)
expect(tp.getNodeKth(sp.second, 1)?.value).toBe(99)
expect(tp.getNodeKth(sp.second, 2)?.value).toBe(999)
expect(tp.getNodeKth(tp.root, 99)).toBe(null)
})

0 comments on commit 17e521b

Please sign in to comment.