[Algorithm] Median Maintenance algorithm implementation using TypeScript / JavaScript

The median maintenance problem is a common programming challenge presented in software engineering job interviews.

In this lesson we cover an example of how this problem might be presented and what your chain of thought should be to tackle this problem efficiently.

Lets first refresh what is a median

  • The median is the middle element in the sorted list
  • Given a list of numbers
`
The median is the middle element in the sorted list.

Given
13, 23, 11, 16, 15, 10, 26

Sort them
10, 11, 13, 15, 16, 23, 26
            ↑
         Median

If we have an even number of elements we average

E.g.
10, 11, 13, 15, 16, 23, 26, 32
            \    /
             15.5

They way we solve the problem is by using two heaps (Low & High) to divide the array into tow parts.

Low                 |                      High

Max Heap          |                    Min Heap

Low part is a max heap, high part is a min heap.

`
(n/2 ± 1) smallest items in a low MaxHeap       (n/2 ± 1) biggest items in a high MinHeap

        peek => n/2th smallest                     peek => n/2th smallest
                           \                        /
                                    MEDIAN!
`

If low part size is equals to high part size, then we get avg value, otherwise, we get from larger size heap.

function MedianMaintaince() {
  let lowMaxHeap = new Heap((b, a) => a - b);
  let highMinHeap = new Heap((a, b) => a - b);

  return {
    add(value) {
      // For the first element, we add to lowMaxHeap by default
      if (lowMaxHeap.size() === 0 || value < lowMaxHeap.peek()) {
        lowMaxHeap.add(value);
      } else {
        highMinHeap.add(value);
      }

      /**
       * Reblance:
       *
       * If low.size = 2; high.size = 4, then we move the root of high to the low part
       * so that low.size = 3, high.size = 3
       */
      let smallerHeap =
        lowMaxHeap.size() > highMinHeap.size() ? highMinHeap : lowMaxHeap;
      let biggerHeap = smallerHeap === lowMaxHeap ? highMinHeap : lowMaxHeap;
      if (biggerHeap.size() - smallerHeap.size() > 1) {
        smallerHeap.add(biggerHeap.extractRoot());
      }

      /**
       * If low.szie === high.size, extract root for both and calculate the average value
       */
      if (lowMaxHeap.size() === highMinHeap.size()) {
        return (lowMaxHeap.peek() + highMinHeap.peek()) / 2;
      } else {
        // get peak value from the bigger size of heap
        return lowMaxHeap.size() > highMinHeap.size()
          ? lowMaxHeap.peek()
          : highMinHeap.peek();
      }
    }
  };
}

const mm = new MedianMaintaince();
console.log(mm.add(4)); // 4
console.log(mm.add(2)); // 3
console.log(mm.add(5)); // 4
console.log(mm.add(3)); // 3.5

We have heap data structure:

function printArray(ary) {
  console.log(JSON.stringify(ary, null, 2));
}

function Heap(cmpFn = () => {}) {
  let data = [];
  return {
    data,
    // 2n+1
    leftInx(index) {
      return 2 * index + 1;
    },
    //2n + 2
    rightInx(index) {
      return 2 * index + 2;
    },
    // left: (n - 1) / 2, left index is always odd number
    // right: (n - 2) / 2, right index is always even number
    parentInx(index) {
      return index % 2 === 0 ? (index - 2) / 2 : (index - 1) / 2;
    },
    add(val) {
      this.data.push(val);
      this.siftUp(this.data.length - 1);
    },
    extractRoot() {
      if (this.data.length > 0) {
        const root = this.data[0];
        const last = this.data.pop();
        if (this.data.length > 0) {
          // move last element to the root
          this.data[0] = last;
          // move last elemment from top to bottom
          this.siftDown(0);
        }

        return root;
      }
    },
    siftUp(index) {
      // find parent index
      let parentInx = this.parentInx(index);
      // compare
      while (index > 0 && cmpFn(this.data[index], this.data[parentInx]) < 0) {
        //swap parent and current node value
        [this.data[index], this.data[parentInx]] = [
          this.data[parentInx],
          this.data[index]
        ];
        //swap index
        index = parentInx;
        //move to next parent
        parentInx = this.parentInx(index);
      }
    },
    siftDown(index) {
      const minIndex = (leftInx, rightInx) => {
        if (cmpFn(this.data[leftInx], this.data[rightInx]) <= 0) {
          return leftInx;
        } else {
          return rightInx;
        }
      };
      let min = minIndex(this.leftInx(index), this.rightInx(index));
      while (min >= 0 && cmpFn(this.data[index], this.data[min]) > 0) {
        [this.data[index], this.data[min]] = [this.data[min], this.data[index]];
        index = min;
        min = minIndex(this.leftInx(index), this.rightInx(index));
      }
    },
    peek() {
      return this.data[0];
    },
    print() {
      printArray(this.data);
    },
    size() {
      return this.data.length;
    }
  };
}

  

原文地址:https://www.cnblogs.com/Answer1215/p/10222226.html

时间: 2024-11-05 13:34:05

[Algorithm] Median Maintenance algorithm implementation using TypeScript / JavaScript的相关文章

[Algorithm] Maximum Contiguous Subarray algorithm implementation using TypeScript / JavaScript

The maximum subarray problem is one of the nicest examples of dynamic programming application. In this lesson we cover an example of how this problem might be presented and what your chain of thought should be to tackle this problem efficiently. /**

Method for finding shortest path to destination in traffic network using Dijkstra algorithm or Floyd-warshall algorithm

A method is presented for finding a shortest path from a starting place to a destination place in a traffic network including one or more turn restrictions, one or more U-turns and one or more P-turns using a Dijkstra algorithm. The method as sets a

2018.2.15 algo 红黑树和Median Maintenance

先是把昨天没看完的红黑树给看了,红黑树本身还挺复杂的,光头哥讲的就比较浅,就把红黑树的作用以及红黑规则的证明给讲了下.红黑树就是符合一种规则的树,这种树的好处就是它的高度可以确保是对数级的.证明也不算难.难点在于insert,delete这些操作,光头哥就讲了一点点,我也没咋细看... 然后是作业,Median Maintenance:求一个动态数组每一次数据变化时的中位数(这个动态数组就是一次添加一个数据,还算简单的),算法原理不难,其实就是用两个堆来一直把中位数保存在堆的root上面.看着挺

一个&quot;Median Maintenance&quot;问题

题目要求: Download the text file here. (Right click and save link as). The goal of this problem is to implement a variant of the 2-SUM algorithm (covered in the Week 6 lecture on hash table applications). The file contains 1 million integers, both positi

[Algorithm] A* Search Algorithm Basic

A* is a best-first search, meaning that it solves problems by searching amoung all possible paths to the solution(goal) for the one that incurs the smallest cost(least distance, shortest time, etc.), and among these paths it first considers the one t

[Algorithm][Greedy]Dijsktra Algorithm

面试折在了dijsktra algorithm上,我一个暴风哭泣-- 本来是想复习一下Dijsktra算法,又看到了Prim's algorithm 等系列,今天就着这个机会都好好复习一下. ---------------------------正文分割线------------------------------------------ Dijsktra Algorithm 给出一个图以及一个起点,找到从起点到其他所有点的最短路径. Dijsktra 与 Prim 最小生成树算法类似, 也生成

TypeScript -- JavaScript的救赎

TypeScript的设计目的应该是解决JavaScript的"痛点":弱类型和没有命名空间,导致很难模块化,不适合开发大型程序.另外它还提供了一些语法糖来帮助大家更方便地实践面向对象的编程. 那先来看看TypeScript是如何解决这两个问题的. 一. 编译时的强类型 TypeScript设计了一套类型机制来保证编译时的强类型判断. 最简单的,你可以申明变量的类型,那么任何其他类型的赋值将会引起编译错误. 例如 var foo: string; foo = true; //error

Programming Question - 6

Question 1 Download the text file here. (Right click and save link as). The goal of this problem is to implement a variant of the 2-SUM algorithm (covered in the Week 6 lecture on hash table applications). The file contains 1 million integers, both p

[Algorithms] Binary Search Algorithm using TypeScript

(binary search trees) which form the basis of modern databases and immutable data structures. Binary search works very much the way humans intuitively search for a name in a yellow pages directory (if you have ever seen one) or the dictionary. In thi