1、题目描述
中位数 是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
例如,[2,3,4]
的中位数是 3
[2,3]
的中位数是 (2 + 3) / 2 = 2.5
示例
输入:
[5,2,3,4,1,6,7,0,8]
返回值:
"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
说明:
数据流里面不断吐出的是5,2,3...,则得到的平均数分别为5,(5+2)/2,3...
2、题解
(1)法一:插入排序法
分析:①Insert
方法:该方法首先检查数组是否为空。如果为空,则直接添加数字。如果不为空,则循环遍历数组找到合适的位置插入数字。这种方法的时间复杂度为 O(n),其中 n 是数组的大小。这是因为我们可能需要遍历整个数组来找到合适的位置。另外,这种方法会改变数组的顺序。
②GetMedian
方法:该方法首先获取数组的大小,然后根据数组的大小确定中位数的位置。这种方法的时间复杂度为 O(1),因为我们直接访问数组的特定位置。注意,这里假设数组已经按照升序排列。如果数组未排序,则需要首先对数组进行排序,这会增加时间复杂度。
代码如下:
public class MedianFinder {
// 插入排序法
private ArrayList<Integer> array = new ArrayList<Integer>();
// 插入数字到已排序的整数数组中
public void Insert(Integer num) {
// 如果数组为空,直接添加数字
if (array.isEmpty()) {
array.add(num);
} else {
// 初始化索引i为0
int i = 0;
// 遍历数组直到找到合适的位置插入数字
for (; i < array.size(); i++) {
// 如果当前数字小于等于要插入的数字,跳出循环
if (num <= array.get(i)) {
break;
}
}
// 在合适的位置插入数字
array.add(i, num);
}
}
// 获取数组的中位数
public Double GetMedian() {
// 获取数组的大小
int n = array.size();
// 如果数组大小是奇数,返回中间的数字(向下取整)
if (n % 2 == 1) {
return (double) array.get(n / 2);
} else {
// 如果数组大小是偶数,返回中间两个数字的平均值
return ((double) array.get(n / 2) + (double) array.get(n / 2 - 1)) / 2;
}
}
}
(2)法二:借助堆排序
分析:①insert方法:该方法首先将元素插入到最大堆中。然后,该方法将最大堆中的元素依次弹出,并放入最小堆中。最后,如果最大堆和最小堆的大小不相等,则将最小堆中的最小元素插入到最大堆中。
②getmedian方法:该方法首先判断最大堆的大小是否大于最小堆的大小。如果是,则中位数为最大堆中的堆顶元素。否则,中位数为最大堆和最小堆堆顶元素的平均值。
代码如下:
// 建立大根堆
private PriorityQueue<Integer> maxHeap = new PriorityQueue<>(((o1, o2) -> o2.compareTo(o1)));
// 建立小根堆
private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
// 插入元素
public void insert(Integer num) {
// 将元素插入到最大堆中
maxHeap.offer(num);
// 将最大堆中的元素依次弹出,并放入最小堆中
while (maxHeap.size() > minHeap.size()) {
minHeap.offer(maxHeap.poll());
}
// 如果最大堆和最小堆的大小不相等,则将最小堆中的最小元素插入到最大堆中
if (maxHeap.size() < minHeap.size()) {
maxHeap.offer(minHeap.poll());
}
}
// 获取中位数
public Double getMedian() {
// 如果最大堆的大小大于最小堆的大小,则中位数为最大堆中的堆顶元素
if (maxHeap.size() > minHeap.size()) {
return (double) maxHeap.peek();
}
// 否则,中位数为最大堆和最小堆堆顶元素的平均值
else {
return ((double) maxHeap.peek() + (double) minHeap.peek()) / 2;
}
}
注解:
lambda 表达式 (o1, o2) -> o2.compareTo(o1)
等价于使用匿名内部类实现 Comparator 接口的 compare 方法。在这个比较器中,它通过调用 o2.compareTo(o1)
来比较两个 Integer 对象的大小,从而实现了降序排序。
还原如下:
private PriorityQueue<Integer> min = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});