Easy
题目描述
有 n 个人在排队买票,其中第 0 个人在队列的最前面,第 (n - 1) 个人在队列的最后面。
给你一个下标从 0 开始、长度为 n 的整数数组 tickets,其中 tickets[i] 表示第 i 个人想要购买的票数。
每个人买一张票恰好需要 1 秒。一个人一次只能买一张票,如果需要购买更多票,他必须走到队列的末尾重新排队(瞬间发生)。如果一个人没有剩余需要买的票,那个人将会离开队列。
返回位置为 k(下标从 0 开始)的人完成买票需要的时间。
示例 1:
输入:tickets = [2,3,2], k = 2
输出:6
解释:
- 第 1 轮,队列中的每个人都买一张票,队列变为 [1,2,1]。
- 第 2 轮,队列中的每个人都买一张票,队列变为 [0,1,0]。
- 第 3 轮,队列中的每个人都买一张票,第 k 个人买完票。
示例 2:
输入:tickets = [5,1,1,1], k = 0
输出:8
解释:
- 第 1 轮,队列中的每个人都买一张票,队列变为 [4,0,0,0]。
- 现在,只有第 0 个人需要继续买票。
- 第 0 个人买了 4 张票,总共花费 8 秒。
提示:
n == tickets.length1 <= n <= 1001 <= tickets[i] <= 1000 <= k < n
解题思路
这道题有两种解法思路:
方法一:队列模拟 最直观的方法是直接模拟购票过程。使用队列存储每个人的信息(包括需要买的票数和原始位置),然后逐秒模拟:每次队首的人买一张票,如果还需要买票就重新入队,直到位置k的人买完所有票为止。
方法二:数学优化(推荐) 观察发现,我们不需要真正模拟整个过程。对于位置k的人要买完tickets[k]张票,我们可以计算:
- 对于位置在k之前的人:他们最多能买min(tickets[i], tickets[k])张票
- 对于位置k的人:买tickets[k]张票
- 对于位置在k之后的人:他们最多能买min(tickets[i], tickets[k]-1)张票(因为当k买完最后一张票时就结束了)
这样直接计算总时间,时间复杂度更优。
代码实现
class Solution {
public:
int timeRequiredToBuy(vector<int>& tickets, int k) {
int time = 0;
int target = tickets[k];
for (int i = 0; i < tickets.size(); i++) {
if (i <= k) {
// 位置在k之前或就是k的人,最多买min(tickets[i], target)张票
time += min(tickets[i], target);
} else {
// 位置在k之后的人,最多买min(tickets[i], target-1)张票
time += min(tickets[i], target - 1);
}
}
return time;
}
};
class Solution:
def timeRequiredToBuy(self, tickets: List[int], k: int) -> int:
time = 0
target = tickets[k]
for i in range(len(tickets)):
if i <= k:
# 位置在k之前或就是k的人,最多买min(tickets[i], target)张票
time += min(tickets[i], target)
else:
# 位置在k之后的人,最多买min(tickets[i], target-1)张票
time += min(tickets[i], target - 1)
return time
public class Solution {
public int TimeRequiredToBuy(int[] tickets, int k) {
int time = 0;
int target = tickets[k];
for (int i = 0; i < tickets.Length; i++) {
if (i <= k) {
// 位置在k之前或就是k的人,最多买min(tickets[i], target)张票
time += Math.Min(tickets[i], target);
} else {
// 位置在k之后的人,最多买min(tickets[i], target-1)张票
time += Math.Min(tickets[i], target - 1);
}
}
return time;
}
}
/**
* @param {number[]} tickets
* @param {number} k
* @return {number}
*/
var timeRequiredToBuy = function(tickets, k) {
let time = 0;
const target = tickets[k];
for (let i = 0; i < tickets.length; i++) {
if (i <= k) {
// 位置在k之前或就是k的人,最多买min(tickets[i], target)张票
time += Math.min(tickets[i], target);
} else {
// 位置在k之后的人,最多买min(tickets[i], target-1)张票
time += Math.min(tickets[i], target - 1);
}
}
return time;
};
复杂度分析
| 复杂度类型 | 队列模拟 | 数学优化 |
|---|---|---|
| 时间复杂度 | O(sum(tickets)) | O(n) |
| 空间复杂度 | O(n) | O(1) |
说明:
- 队列模拟:最坏情况下需要模拟所有人买完所有票的过程
- 数学优化:只需要遍历一次数组,直接计算结果