Skip to content

Commit

Permalink
ARSN-298: add Min/Max heap data structure
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderchan-scality committed Feb 24, 2023
1 parent c1dd2e4 commit 054f61d
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 0 deletions.
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const algorithms = {
MergeStream: require('./lib/algos/stream/MergeStream'),
},
SortedSet: require('./lib/algos/set/SortedSet'),
Heap: require('./lib/algos/heap/Heap'),
};

export const policies = {
Expand Down
124 changes: 124 additions & 0 deletions lib/algos/heap/Heap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
export enum HeapOrder {
Min = -1,
Max = 1,
}

export enum CompareResult {
LT = -1,
EQ = 0,
GT = 1,
}

export type CompareFunction = (x: any, y: any) => CompareResult;

export class Heap {
size: number;
_maxSize: number;
_order: HeapOrder;
_heap: any[];
_cmpFn: CompareFunction;

constructor(size: number, order: HeapOrder, cmpFn: CompareFunction) {
this.size = 0;
this._maxSize = size;
this._order = order;
this._cmpFn = cmpFn;
this._heap = new Array<any>(this._maxSize);
}

_parent(i: number): number {
return Math.floor((i - 1) / 2);
}

_left(i: number): number {
return Math.floor((2 * i) + 1);
}

_right(i: number): number {
return Math.floor((2 * i) + 2);
}

_shouldSwap(childIdx: number, parentIdx: number): boolean {
return this._cmpFn(this._heap[childIdx], this._heap[parentIdx]) as number === this._order as number;
}

_swap(i: number, j: number) {
const tmp = this._heap[i];
this._heap[i] = this._heap[j];
this._heap[j] = tmp;
}

_heapify(i: number) {
const l = this._left(i);
const r = this._right(i);
let c = i;

if (l < this.size && this._shouldSwap(l, c)) {
c = l;
}

if (r < this.size && this._shouldSwap(r, c)) {
c = r;
}

if (c != i) {
this._swap(c, i);
this._heapify(c);
}
}

add(item: any): any {
if (this.size >= this._maxSize) {
return new Error('Max heap size reached');
}

++this.size;
let c = this.size - 1;
this._heap[c] = item;

while (c > 0) {
if (!this._shouldSwap(c, this._parent(c))) {
return null;
}

this._swap(c, this._parent(c));
c = this._parent(c);
}

return null;
};

remove(): any {
if (this.size <= 0) {
return null;
}

const ret = this._heap[0];
this._heap[0] = this._heap[this.size - 1];
this._heapify(0);
--this.size;

return ret;
};

peek(): any {
if (this.size <= 0) {
return null;
}

return this._heap[0];
};
}

export class MinHeap extends Heap {
constructor(size: number, cmpFn: CompareFunction) {
super(size, HeapOrder.Min, cmpFn);
}
}

export class MaxHeap extends Heap {
constructor(size: number, cmpFn: CompareFunction) {
super(size, HeapOrder.Max, cmpFn);
}
}

75 changes: 75 additions & 0 deletions tests/unit/algos/heap/Heap.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use strict'

import * as Heap from '../../../../lib/algos/heap/Heap';

function numberCompare(x: any, y: any): Heap.CompareResult {
if (x > y) {
return Heap.CompareResult.GT;
}

if (x < y) {
return Heap.CompareResult.LT;
}

return Heap.CompareResult.EQ;
}

describe('Heap', () => {
describe('Heap::add', () => {
it('should set max heap size and return error if max size is reached', () => {
const heap = new Heap.Heap(1, Heap.HeapOrder.Min, numberCompare);
expect(heap.add(1)).toBeNull();
expect(heap.add(2)).not.toBeNull();
});
});

describe('Heap::remove', () => {
it('should return null if heap is empty', () => {
const heap = new Heap.Heap(1, Heap.HeapOrder.Min, numberCompare);
expect(heap.remove()).toBeNull();
});
});

describe('Heap::peek', () => {
it('should return null if heap is empty', () => {
const heap = new Heap.Heap(1, Heap.HeapOrder.Min, numberCompare);
expect(heap.peek()).toBeNull();
});
});
});

describe('MinHeap', () => {
it('should maintain min heap properties', () => {
const minHeap = new Heap.MinHeap(5, numberCompare);
expect(minHeap.add(5)).toBeNull();
expect(minHeap.add(3)).toBeNull();
expect(minHeap.add(2)).toBeNull();
expect(minHeap.add(4)).toBeNull();
expect(minHeap.add(1)).toBeNull();
expect(minHeap.size).toEqual(5);
expect(minHeap.remove()).toEqual(1);
expect(minHeap.remove()).toEqual(2);
expect(minHeap.remove()).toEqual(3);
expect(minHeap.remove()).toEqual(4);
expect(minHeap.remove()).toEqual(5);
expect(minHeap.size).toEqual(0);
});
});

describe('MaxHeap', () => {
it('should maintain max heap properties', () => {
const maxHeap = new Heap.MaxHeap(5, numberCompare);
expect(maxHeap.add(5)).toBeNull();
expect(maxHeap.add(3)).toBeNull();
expect(maxHeap.add(2)).toBeNull();
expect(maxHeap.add(4)).toBeNull();
expect(maxHeap.add(1)).toBeNull();
expect(maxHeap.size).toEqual(5);
expect(maxHeap.remove()).toEqual(5);
expect(maxHeap.remove()).toEqual(4);
expect(maxHeap.remove()).toEqual(3);
expect(maxHeap.remove()).toEqual(2);
expect(maxHeap.remove()).toEqual(1);
expect(maxHeap.size).toEqual(0);
});
});

0 comments on commit 054f61d

Please sign in to comment.