class MaxHeap {
private heap: number [ ] ;
constructor ( ) {
this . heap = [ ] ;
}
push ( num: number ) : void {
this . heap. push ( num) ;
this . siftUp ( this . heap. length - 1 ) ;
}
pop ( ) : number {
const top = this . heap[ 0 ] ;
const last = this . heap. pop ( ) ! ;
if ( this . heap. length > 0 ) {
this . heap[ 0 ] = last;
this . siftDown ( 0 ) ;
}
return top;
}
peek ( ) : number {
return this . heap[ 0 ] ;
}
size ( ) : number {
return this . heap. length;
}
private siftUp ( index: number ) : void {
while ( index > 0 ) {
const parent = Math. floor ( ( index - 1 ) / 2 ) ;
if ( this . heap[ parent] >= this . heap[ index] ) break ;
[ this . heap[ parent] , this . heap[ index] ] = [
this . heap[ index] ,
this . heap[ parent] ,
] ;
index = parent;
}
}
private siftDown ( index: number ) : void {
const size = this . size ( ) ;
while ( index < size) {
let left = 2 * index + 1 ;
let right = 2 * index + 2 ;
let largest = index;
if ( left < size && this . heap[ left] > this . heap[ largest] ) largest = left;
if ( right < size && this . heap[ right] > this . heap[ largest] )
largest = right;
if ( largest === index) break ;
[ this . heap[ index] , this . heap[ largest] ] = [
this . heap[ largest] ,
this . heap[ index] ,
] ;
index = largest;
}
}
}
class MinHeap {
private heap: number [ ] ;
constructor ( ) {
this . heap = [ ] ;
}
push ( num: number ) : void {
this . heap. push ( num) ;
this . siftUp ( this . heap. length - 1 ) ;
}
pop ( ) : number {
const top = this . heap[ 0 ] ;
const last = this . heap. pop ( ) ! ;
if ( this . heap. length > 0 ) {
this . heap[ 0 ] = last;
this . siftDown ( 0 ) ;
}
return top;
}
peek ( ) : number {
return this . heap[ 0 ] ;
}
size ( ) : number {
return this . heap. length;
}
private siftUp ( index: number ) : void {
while ( index > 0 ) {
const parent = Math. floor ( ( index - 1 ) / 2 ) ;
if ( this . heap[ parent] <= this . heap[ index] ) break ;
[ this . heap[ parent] , this . heap[ index] ] = [
this . heap[ index] ,
this . heap[ parent] ,
] ;
index = parent;
}
}
private siftDown ( index: number ) : void {
const size = this . size ( ) ;
while ( index < size) {
let left = 2 * index + 1 ;
let right = 2 * index + 2 ;
let smallest = index;
if ( left < size && this . heap[ left] < this . heap[ smallest] ) smallest = left;
if ( right < size && this . heap[ right] < this . heap[ smallest] )
smallest = right;
if ( smallest === index) break ;
[ this . heap[ index] , this . heap[ smallest] ] = [
this . heap[ smallest] ,
this . heap[ index] ,
] ;
index = smallest;
}
}
}
class MedianFinder {
private maxHeap: MaxHeap;
private minHeap: MinHeap;
constructor ( ) {
this . maxHeap = new MaxHeap ( ) ;
this . minHeap = new MinHeap ( ) ;
}
addNum ( num: number ) : void {
this . maxHeap. push ( num) ;
this . minHeap. push ( this . maxHeap. pop ( ) ) ;
if ( this . maxHeap. size ( ) < this . minHeap. size ( ) ) {
this . maxHeap. push ( this . minHeap. pop ( ) ) ;
}
}
findMedian ( ) : number {
if ( this . maxHeap. size ( ) === this . minHeap. size ( ) ) {
return ( this . maxHeap. peek ( ) + this . minHeap. peek ( ) ) / 2 ;
} else {
return this . maxHeap. peek ( ) ;
}
}
}
function calculateExactMedian ( data: number [ ] , chunkSize: number ) {
const chunks = [ ] ;
for ( let i = 0 ; i < data. length; i += chunkSize) {
const chunk = data. slice ( i, i + chunkSize) ;
chunks. push ( chunk) ;
}
const sortedChunks = chunks. map ( ( chunk) => chunk. sort ( ( a, b) => a - b) ) ;
const mergedArray = [ ] ;
sortedChunks. forEach ( ( chunk) => {
mergedArray. push ( ... chunk) ;
} ) ;
mergedArray. sort ( ( a, b) => a - b) ;
const length = mergedArray. length;
if ( length % 2 === 0 ) {
return ( mergedArray[ length / 2 - 1 ] + mergedArray[ length / 2 ] ) / 2 ;
} else {
return mergedArray[ Math. floor ( length / 2 ) ] ;
}
}
const nums: number [ ] = [ ] ;
const len = 10 ** 7 ;
for ( let i = 0 ; i < len; i++ ) {
nums. push ( Math. floor ( Math. random ( ) * 100 ) ) ;
}
const $s = performance. now ( ) ;
const medianFinder = new MedianFinder ( ) ;
for ( let i = 0 ; i < nums. length; i++ ) {
medianFinder. addNum ( nums[ i] ) ;
}
console . log ( medianFinder. findMedian ( ) ) ;
const $e = performance. now ( ) ;
console . log ( $e - $s, '@heap_sort' ) ;
const $s1 = performance. now ( ) ;
nums. sort ( ( a, b) => a - b) ;
const n = nums. length;
if ( n % 2 === 0 ) {
console . log ( ( nums[ n / 2 - 1 ] + nums[ n / 2 ] ) / 2 ) ;
} else {
console . log ( nums[ Math. floor ( n / 2 ) ] ) ;
}
const $e1 = performance. now ( ) ;
console . log ( $e1 - $s1, '@arr_sort' ) ;
const $s2 = performance. now ( ) ;
const chunkSize = 10000 ;
console . log ( calculateExactMedian ( nums, chunkSize) ) ;
const $e2 = performance. now ( ) ;
console . log ( $e2 - $s2, '@chunk_sort' ) ;
output: