这是我参与8月更文挑战的第20天,活动详情查看:8月更文挑战
剑指 Offer 41. 数据流中的中位数
题目
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
- void addNum(int num) - 从数据流中添加一个整数到数据结构中。
- double findMedian() - 返回目前所有元素的中位数。
示例 1:
输入:
["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
示例 2:
输入:
["MedianFinder","addNum","findMedian","addNum","findMedian"]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]
限制:
- 最多会对
addNum、findMedian进行50000次调用。
方法一
暴力做法:维护一个集合,添加操作时向集合中添加;求中位数时,先排序,再按题意返回中位数;
class MedianFinder {
List<Integer> note;
/** initialize your data structure here. */
public MedianFinder() {
note = new ArrayList<>();
}
public void addNum(int num) {
note.add(num);
}
public double findMedian() {
int n = note.size();
Collections.sort(note);
if (n % 2 == 0) {
double x = note.get(n / 2), y = note.get(n / 2 - 1);
return (x + y) / 2;
}else {
return (double)note.get(n / 2);
}
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
方法二
大根堆+小根堆:
-
大根堆中存小于中位数的数,小根堆中存大于中位数的数
-
添加元素时,
- 先往大根堆中添加,若发现大根堆的堆顶元素大于小根堆的堆顶元素时,交换两个堆顶元素;
- 若大根堆的数量比小根堆的数量大2,说明不平衡,大根堆分一个给小根堆维持平衡;
-
返回中位数时,
- 若总数为奇数,返回大根堆堆顶元素
- 否则,返回两个堆堆顶元素的平均值
class MedianFinder {
PriorityQueue<Integer> maxHeap, minHeap;
/** initialize your data structure here. */
public MedianFinder() {
minHeap = new PriorityQueue<>();
maxHeap = new PriorityQueue(new Comparator<Integer>() {
public int compare(Integer a, Integer b) {
return b - a;
}
});
}
public void addNum(int num) {
maxHeap.offer(num);
if (minHeap.size() > 0 && maxHeap.peek() > minHeap.peek()) {
int t = minHeap.poll(), u = maxHeap.poll();
minHeap.offer(u); maxHeap.offer(t);
}
if (maxHeap.size() > minHeap.size() + 1) {
int t = maxHeap.poll();
minHeap.offer(t);
}
}
public double findMedian() {
if (((minHeap.size() + maxHeap.size()) & 1) == 1) return maxHeap.peek();
else return (minHeap.peek() + maxHeap.peek()) / 2.0;
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
注意: 大根堆元素个数始终和小根堆元素个数相等或者大1
剑指 Offer 42. 连续子数组的最大和
题目
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
提示:
1 <= arr.length <= 10^5-100 <= arr[i] <= 100
方法一
朴素做法:利用前缀和,首先固定子数组的右端点,然后从起点开始枚举,一直到右端点,在枚举的过程中更新答案,如此一遍操作时间复杂度为O(n),有n个点,总共时间复杂度为O(n*n),不符合题意
进阶做法:在枚举右端点的同时,记录下右端点左边前缀和的最小值,每次只要用右端点减去该最小值即可,省去了从起点再重新遍历的过程,时间复杂度为O(n)
class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;
int[] sum = new int[n + 1];
for (int i = 1; i <= n; i ++ ) sum[i] = sum[i - 1] + nums[i - 1];
int minv = 0;
int res = -0x3f3f3f3f;
for (int i = 1; i <= n; i ++ ) {
res = Math.max(res, sum[i] - sum[minv]);
if (sum[minv] > sum[i]) minv = i;
}
return res;
}
}
时间复杂度: O(n)
空间复杂度: O(1)