2747. Count Zero Request Servers
LeetCode 每日一题:Count Zero Request Servers
本页目录
You are given an integer n denoting the total number of servers and a 2D 0-indexed integer array logs, where logs[i] = [server_id, time] denotes that the server with id server_id received a request at time time.
You are also given an integer x and a 0-indexed integer array queries.
Return a 0-indexed integer array arr of length queries.length where arr[i] represents the number of servers that did not receive any requests during the time interval [queries[i] - x, queries[i]].
Note that the time intervals are inclusive.
Constraints:
1 <= n <= 1051 <= logs.length <= 1051 <= queries.length <= 105logs[i].length == 21 <= logs[i][0] <= n1 <= logs[i][1] <= 1061 <= x <= 105x < queries[i] <= 106
思路:排序+滑动窗口
首先很容易想到此题的暴力解法,即对每一个查询的时间窗口,依次检测在窗口内没有收到请求的服务器的数量,然后返回。然而,看数据量,这需要我们至多次操作,显然超时了。看数据量,我们大概率要将复杂度里面塞入一个log
其实,想到本题题眼“查询时间窗口内数据个数”就已经暗示了滑动窗口的解法。既然滑动的是时间窗口,那必然要保证数据在时间上是有序的。另外,我们要查询多个时间窗口,因此,时间窗口也应该有序
于是Log的位置确定下来了:排序。滑动窗口仅仅是常数级别遍历,因此时间复杂度在Llog(L)+Qlog(Q),完全符合题目的数据量
唯一需要注意的一点是,需要保留queries 的原序号,以便查询后及时还原。答案采用了一种更好的方式。这里仅仅是笔者不成熟的第一直觉。
logs.sort(key=lambda x:x[-1])
ordered_q = sorted(list(enumerate(queries)), key=lambda x:x[-1])然后,考虑滑动窗口的实现。笔者习惯使用左闭右开的窗口表示,这样,左右指针的更新全都可以统一放置在窗口更新的后面,比较无脑。
假设当前查询的时间是q, 那么对应的时间窗口就是[q-x, q]。我们对不在窗口内的服务器数量感兴趣,因此窗口内需要统计其包含的服务器数量,然后用n去减。自然,我们想到了字典。字典内值非零的键的数量就是服务器的数量,而每次窗口的移动,都可以增减进入/离开窗口的服务器对应的值
据此,可以写出左右指针移动的条件:
- 但凡
right处于的时间≤q,right就右移一步 - 但凡
left处于的时间<q-x,left就右移一步
注意不等号是否取等,以确保[q-x, q]窗口内的log全部被囊括在窗口内
这样,每次左右指针移动完毕,就可以知道q对应的答案
left, right = 0, 0
q_idx = 0
ans = [0]*len(queries)
server = {}
while left < len(logs) and q_idx < len(queries):
while right < len(logs) and logs[right][-1] <= ordered_q[q_idx][-1]:
server[logs[right][0]] = server.get(logs[right][0], 0) + 1
right += 1
while left < len(logs) and logs[left][-1] < ordered_q[q_idx][-1] - x:
server[logs[left][0]] -= 1
if server[logs[left][0]] == 0:
server.pop(logs[left][0], None) # 答案写的更好,单独拿了一个变量来记录非零数
left += 1
ans[ordered_q[q_idx][0]] = n-len(server)
q_idx += 1循环结束有两种情况:
left指针走到头,这意味着对于剩余的queries, 没有任何的log与之对应,因此他们的答案就是nq_idx指针走到头,这意味着所有的查询全部完毕,不必在乎窗口滑动到了哪里
对于第一种情况,需要额外补充这几个没来的及查询的答案。循环的条件恰好避开了第二种情况。
while q_idx < len(queries):
ans[ordered_q[q_idx][0]] = n
q_idx += 1把代码合到一起,就是最终的答案了
代码
class Solution:
def countServers(self, n: int, logs: List[List[int]], x: int, queries: List[int]) -> List[int]:
logs.sort(key=lambda x:x[-1])
ordered_q = sorted(list(enumerate(queries)), key=lambda x:x[-1])
left, right = 0, 0
q_idx = 0
ans = [0]*len(queries)
server = {}
while left < len(logs) and q_idx < len(queries):
while right < len(logs) and logs[right][-1] <= ordered_q[q_idx][-1]:
server[logs[right][0]] = server.get(logs[right][0], 0) + 1
right += 1
while left < len(logs) and logs[left][-1] < ordered_q[q_idx][-1] - x:
server[logs[left][0]] -= 1
if server[logs[left][0]] == 0:
server.pop(logs[left][0], None)
left += 1
ans[ordered_q[q_idx][0]] = n-len(server)
q_idx += 1
while q_idx < len(queries):
ans[ordered_q[q_idx][0]] = n
q_idx += 1
return ans
复杂度
- 时间:如前面分析,复杂度全在排序上,为
- 空间:对于长度为
n的列表,Timsort消耗的空间为级别;最坏的情况下n台服务器全在窗口内。因此,总的空间复杂度为