LCR 041-数据流中的移动平均值

Raphael Liu Lv10

给定一个窗口大小和一个整数数据流,根据该滑动窗口的大小,计算滑动窗口里所有数字的平均值。

实现 MovingAverage 类:

  • MovingAverage(int size) 用窗口大小 size 初始化对象。
  • double next(int val) 成员函数 next 每次调用的时候都会往滑动窗口增加一个整数,请计算并返回数据流中最后 size 个值的移动平均值,即滑动窗口里所有数字的平均值。

示例:

**输入:**
inputs = ["MovingAverage", "next", "next", "next", "next"]
inputs = [[3], [1], [10], [3], [5]]
**输出:**
[null, 1.0, 5.5, 4.66667, 6.0]

**解释:**
MovingAverage movingAverage = new MovingAverage(3);
movingAverage.next(1); // 返回 1.0 = 1 / 1
movingAverage.next(10); // 返回 5.5 = (1 + 10) / 2
movingAverage.next(3); // 返回 4.66667 = (1 + 10 + 3) / 3
movingAverage.next(5); // 返回 6.0 = (10 + 3 + 5) / 3

提示:

  • 1 <= size <= 1000
  • -105 <= val <= 105
  • 最多调用 next 方法 104

注意:本题与主站 346 题相同: <https://leetcode-cn.com/problems/moving-average-from-data-
stream/>

方法一:队列

这道题要求根据给定的数据流计算滑动窗口中所有数字的平均值,滑动窗口的大小为给定的参数 size。当数据流中的数字个数不超过滑动窗口的大小时,计算数据流中的所有数字的平均值;当数据流中的数字个数超过滑动窗口的大小时,只计算滑动窗口中的数字的平均值,数据流中更早的数字被移出滑动窗口。

由于数字进入滑动窗口和移出滑动窗口的规则符合先进先出,因此可以使用队列存储滑动窗口中的数字,同时维护滑动窗口的大小以及滑动窗口的数字之和。

初始时,队列为空,滑动窗口的大小设为给定的参数 size,滑动窗口的数字之和为 0。

每次调用 next 时,需要将 val 添加到滑动窗口中,同时确保滑动窗口中的数字个数不超过 size,如果数字个数超过 size 则需要将多余的数字移除,在添加和移除数字的同时需要更新滑动窗口的数字之和。由于每次调用只会将一个数字添加到滑动窗口中,因此每次调用最多只需要将一个多余的数字移除。具体操作如下。

  1. 如果队列中的数字个数等于滑动窗口的大小,则移除队首的数字,将移除的数字从滑动窗口的数字之和中减去。如果队列中的数字个数小于滑动窗口的大小,则不移除队首的数字。

  2. 将 val 添加到队列中,并加到滑动窗口的数字之和中。

  3. 计算滑动窗口的数字之和与队列中的数字个数之商,即为滑动窗口中所有数字的平均值。

[sol1-Python3]
1
2
3
4
5
6
7
8
9
10
11
12
class MovingAverage:
def __init__(self, size: int):
self.size = size
self.sum = 0
self.q = deque()

def next(self, val: int) -> float:
if len(self.q) == self.size:
self.sum -= self.q.popleft()
self.sum += val
self.q.append(val)
return self.sum / len(self.q)
[sol1-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MovingAverage {
Queue<Integer> queue;
int size;
double sum;

public MovingAverage(int size) {
queue = new ArrayDeque<Integer>();
this.size = size;
sum = 0;
}

public double next(int val) {
if (queue.size() == size) {
sum -= queue.poll();
}
queue.offer(val);
sum += val;
return sum / queue.size();
}
}
[sol1-C#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MovingAverage {
Queue<int> queue;
int size;
double sum;

public MovingAverage(int size) {
queue = new Queue<int>();
this.size = size;
sum = 0;
}

public double Next(int val) {
if (queue.Count == size) {
sum -= queue.Dequeue();
}
queue.Enqueue(val);
sum += val;
return sum / queue.Count;
}
}
[sol1-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MovingAverage {
public:
MovingAverage(int size) {
this->size = size;
this->sum = 0.0;
}

double next(int val) {
if (qu.size() == size) {
sum -= qu.front();
qu.pop();
}
qu.emplace(val);
sum += val;
return sum / qu.size();
}
private:
int size;
double sum;
queue<int> qu;
};
[sol1-C]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
typedef struct {
int size;
double sum;
int *queue;
int front;
int rear;
} MovingAverage;


MovingAverage* movingAverageCreate(int size) {
MovingAverage *obj = (MovingAverage *)malloc(sizeof(MovingAverage));
obj->size = size;
obj->sum = 0;
obj->queue = (int *)malloc(sizeof(int) * (size + 1));
obj->front = 0;
obj->rear = 0;
return obj;
}

double movingAverageNext(MovingAverage* obj, int val) {
int size = (obj->rear - obj->front + obj->size + 1) % (obj->size + 1);
if (size == obj->size) {
obj->sum -= obj->queue[obj->front];
obj->front = (obj->front + 1) % (obj->size + 1);
size--;
}
obj->queue[obj->rear] = val;
obj->rear = (obj->rear + 1) % (obj->size + 1);
obj->sum += val;
size++;
return obj->sum / size;
}

void movingAverageFree(MovingAverage* obj) {
free(obj->queue);
free(obj);
}
[sol1-Golang]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type MovingAverage struct {
size, sum int
q []int
}

func Constructor(size int) MovingAverage {
return MovingAverage{size: size}
}

func (m *MovingAverage) Next(val int) float64 {
if len(m.q) == m.size {
m.sum -= m.q[0]
m.q = m.q[1:]
}
m.sum += val
m.q = append(m.q, val)
return float64(m.sum) / float64(len(m.q))
}
[sol1-JavaScript]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var MovingAverage = function(size) {
this.queue = [];
this.size = size;
this.sum = 0;
};

MovingAverage.prototype.next = function(val) {
if (this.queue.length === this.size) {
this.sum -= this.queue.shift();
}
this.queue.push(val);
this.sum += val;
return this.sum / this.queue.length;
};

复杂度分析

  • 时间复杂度:初始化和每次调用 next 的时间复杂度都是 O(1)。

  • 空间复杂度:O(\textit{size}),其中 size 是滑动窗口的大小。空间复杂度主要取决于队列,队列中的数字个数不超过 size。

 Comments
On this page
LCR 041-数据流中的移动平均值