返回轨道
算法 · 2026-05-14 · 23 阅读

P160 0 到 100 的数据流中查找中位数

算法 23 阅读

信号摘要

难度:Hard · 时长:20:56 · leetcode 295

P160 0 到 100 的数据流中查找中位数

未对外公开播放权限
点击查看说明

描述

这个题目说的是,你要设计一个数据结构用于查找数据流中的中位数。它要支持以下两个操作,addNum 用于向数据结构中添加一个整数。findMedian 用于查找该数据结构中目前所有整数的中位数。

注意,这个题目要求你在两种情况下设计这个数据结构。第一种情况是,数据流中的数字全部都大于等于 0 并且小于等于 100。第二种情况是,数据流中 99% 的数字大于等于 0 并且小于等于 100。

其中,中位数的定义是将序列排序后正中间的数字,如果序列的长度是偶数,则取正中间两个数字的平均数。

代码

public class AlgoCasts {

  public class MedianFinderBetweenZeroAndOneHundred {

    private final int LENGTH = 101;
    private final int[] dataCount = new int[LENGTH];
    private int n = 0;

    // k starts from 1
    // suppose that k is always valid
    // Time: O(1)
    private int findKth(int k) {
      int cnt = 0;
      for (int i = 0; i < LENGTH; ++i) {
        cnt += dataCount[i];
        if (cnt >= k) return i;
      }
      return -1;
    }

    // Time: O(1)
    public void addNum(int num) {
      ++dataCount[num];
      ++n;
    }

    // Time: O(1)
    public double findMedian() {
      if ((n & 1) == 1) return findKth(n/2 + 1);
      else return (findKth(n/2) + findKth(n/2 + 1)) / 2.0;
    }
  }

  public class MedianFinderBetweenZeroAndOneHundred99Percent {

    class MedianFinderArray {
      private final List<Integer> data = new ArrayList<>();

      public void addNum(int num) {
        int idx = data.size() - 1;
        while (idx >= 0 && data.get(idx) > num) --idx;
        data.add(idx+1, num);
      }

      // k starts from 1
      public int findKth(int k) {
        return data.get(k-1);
      }

      public int size() {
        return data.size();
      }
    }

    class MedianFinderBetweenZeroAndOneHundred {
      private final int LENGTH = 101;
      private final int[] dataCount = new int[LENGTH];
      private int n = 0;

      // k starts from 1
      public int findKth(int k) {
        int cnt = 0;
        for (int i = 0; i < LENGTH; ++i) {
          cnt += dataCount[i];
          if (cnt >= k) return i;
        }
        return -1;
      }

      public void addNum(int num) {
        ++dataCount[num];
        ++n;
      }

      public int size() {
        return n;
      }
    }

    private final MedianFinderArray leftData = new MedianFinderArray();
    private final MedianFinderArray rightData = new MedianFinderArray();
    private final MedianFinderBetweenZeroAndOneHundred middleData = new MedianFinderBetweenZeroAndOneHundred();

    // Time(avg): O(1), Time(worst): O(n)
    public void addNum(int num) {
      if (num >= 0 && num <= 100) middleData.addNum(num);
      else if (num < 0) leftData.addNum(num);
      else rightData.addNum(num);
    }

    // Time: O(1)
    public double findMedian() {
      int middleSize = middleData.size();
      int leftSize = leftData.size();
      int rightSize = rightData.size();
      int n = middleSize + leftSize + rightSize;
      if ((n & 1) == 1) {
        int k = n/2 + 1;
        if (k > leftSize && k <= leftSize+middleSize) {
          return middleData.findKth(k - leftSize);
        } else if (k <= leftSize) {
          return leftData.findKth(k);
        } else {
          return rightData.findKth(k - leftSize - middleSize);
        }
      } else {
        int k = n/2;
        if (k > leftSize && k+1 <= leftSize+middleSize) {
          k = k - leftSize;
          return (middleData.findKth(k) + middleData.findKth(k+1)) / 2.0;
        } else if (k+1 <= leftSize) {
          return (leftData.findKth(k) + leftData.findKth(k+1)) / 2.0;
        } else if (k == leftSize && middleSize > 0) {
          return (leftData.findKth(k) + middleData.findKth(1)) / 2.0;
        } else if (k == leftSize && middleSize == 0) {
          return (leftData.findKth(k) + rightData.findKth(1)) / 2.0;
        } else if (k == leftSize + middleSize) {
          return (middleData.findKth(k-leftSize) + rightData.findKth(1)) / 2.0;
        } else {
          k = k - leftSize - middleSize;
          return (rightData.findKth(k) + rightData.findKth(k+1)) / 2.0;
        }
      }
    }

  }

}

📡 通讯频道