leetcode-295 Find Median from Data Stream

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:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值