剑指 Offer 41. 数据流中的中位数
1、题目
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
2、题解
一、使用一个 list 集合即可
这里直接根据 list 中的元素排序,然后取中间元素即可。
二、双堆
根据堆的性质,可以判断两个堆的值从下往上递增,即:
bigHeap堆底 <= bigHeap堆顶 <= smallHeap 堆顶 <= smallHeap 堆底
。
将获取中位数的情况分为两类:
- 数据流为奇数时,保证两个堆的长度相差1,那么长度较大的堆的堆顶就是中位数;
- 数据流为偶数时,保证两个堆的长度相等,两个堆的堆顶相加除二就是中位数。
我们要保证每次插入元素后,两堆维持相对长度。让smallHeap
为长度较大的堆,每次插入元素时进行判断:
- 当两堆总长度为偶数时,即两堆长度相等,将新元素插入到
smallHeap
,插入后smallHeap
比bigHeap
长度大1; - 当两堆总长度为奇数时,即两堆长度不等,将新元素插入到
bigHeap
,插入后两堆长度相等;
还要保证插入元素后,两堆仍是保证从下往上递增的顺序性。
那么,每次新元素插入时,都需要先插入到另一个堆,进行重新排序后,再将最值拿出来插入正确的堆中。
- 当两堆总大小为偶数时,即两堆大小相等,先将新元素插入
bigHeap
,重新排序后将新的最值拿出并插入到smallHeap
; - 当两堆总大小为奇数时,即两堆大小不等,先将新元素插入
smallHeap
,重新排序后将新的最值拿出并插入到bigHeap
;
3、代码
一、使用一个 list 集合即可
class MedianFinder {
List<Integer> list = null;
public MedianFinder() {
list = new ArrayList<>();
}
public void addNum(int num) {
list.add(num);
}
public double findMedian() {
Collections.sort(list);
int len = list.size();
if(len % 2 == 0){
return (double)(list.get(len / 2 - 1) + list.get(len / 2)) / 2.0;
}else{
return (double) list.get(len / 2);
}
}
}
二、双堆
class MedianFinder {
PriorityQueue<Integer> smallHeap, bigHeap;
public MedianFinder() {
smallHeap = new PriorityQueue<>(); // 小顶堆,保存较大的一半
bigHeap = new PriorityQueue<>((x, y) -> (y - x)); // 大顶堆,保存较小的一半
}
public void addNum(int num) {
// 长度为偶数时先放入大顶堆,重新排序后在插入到小顶堆
if(smallHeap.size() == bigHeap.size()){
bigHeap.add(num);
smallHeap.add(bigHeap.poll());
}else{
// 长度为奇数时先放入小顶堆,重新排序后在插入到大顶堆
smallHeap.add(num);
bigHeap.add(smallHeap.poll());
}
}
public double findMedian() {
if(smallHeap.size() != bigHeap.size()){
return smallHeap.peek();
}else{
return (smallHeap.peek() + bigHeap.peek()) / 2.0;
}
}
}
剑指 Offer 43. 1~n 整数中 1 出现的次数
1、题目
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
2、题解
一、暴力解
这题目乍一看,直接暴力遍历就好了,通过遍历每一个数出现 1 的次数,然后累加起来即可,但是立马被暴击,超时了。
二、数学规律
3、代码
一、暴力解
class Solution {
public int countDigitOne(int n) {
int res = 0;
for(int i = 1;i <= n;i++){
res += getNum1(i);
}
return res;
}
public int getNum1(int num){
int sum = 0;
while(num > 0){
if(num % 10 == 1){
sum++;
}
num /= 10;
}
return sum;
}
}