前言
前几天,有一哥们发我一个LeetCode题目链接,紧跟着附上了自己的提交记录,一个2ms
,另一个1451ms
…
我一看,这题有点意思啊,不同的思路竟然时间差这么多。搞它。
题目描述
这里有n
个航班,它们分别从1
到n
进行编号。
我们这儿有一份航班预订表,表中第i
条预订记录bookings[i] = [i, j, k]
意味着我们在从i
到j
的每个航班上预订了k
个座位。
请你返回一个长度为n
的数组answer
,按航班编号顺序返回每个航班上预订的座位数。
示例:
输入:bookings = [ [1,2,10], [2,3,20], [2,5,25] ], n = 5
输出:[10,55,45,25,25]
O(m*n)解法
根据题意初始化长度为n
的answer
数组,代表1
到n
号航班预订的座位数量,外层遍历 bookings
,内层遍历bookings[i] = [i, j, k]
,计算航班号i
到j
的座位数量,即当前座位数量加k
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public int[] corpFlightBookings(
int[][] bookings, int n) {
int[] answer = new int[n];
// 遍历整个bookings数组
for (int[] b : bookings) {
// 内层循环把每个航班预订数加上
for (int i = b[0] - 1;
i <= b[1] - 1; i++) {
answer[i] += b[2];
}
}
return answer;
}
O(n)解法
思考
O(m*n)
解法中关键一点,内层循环我们一直重复的在[i, j]
之间加上k
,怎么将这循环变成O(1)
,成为问题的关键!
[i, j]
之间加上k
,这让我想到了等差数列,这不就是公差为k
的等差数列吗?然后呢?
分析
设answer[i]
表示第i
个航班预订的座位数。定义一个差分数组d[]
,d[i]
表示第i
个航班与第i-1
个航班预订座位的差值,即d[i] = answer[i] - answer[i - 1]
。
这样,我们每次遍历到bookings[i] = [i, j, k]
,就只需要将d[i]
增加k
,d[j + 1]
减少k
即可,因为i
到j
之间,航班预订数量是没有变化的。
最后,计算answer[i] = answer[i - 1] + d[i]
,返回answer
即可。
推演
好吧,这样说可能有人没懂,我们按照题目的例子推演一次:
- 初始航班预订数量数组
answer = [0,0,0,0,0]
,差分数组d = [0,0,0,0,0]
- 当遍历到
bookings[0] = [1,2,10]
的时候,差分数组第1位加10,第3位减10,变成d = [10,0,-10,0,0]
- 同理,当遍历到
bookings[1] = [2,3,20]
的时候,差分数组变成d = [10,20,-10,-20,0]
- 当遍历到
bookings[2] = [2,5,25]
的时候,差分数组变成d = [10,45,-10,-20,0]
,第6位要减25,我们也不需要了 - 最后计算
answer
数组的值,answer[0] = d[0] = 10
,answer[1] = d[1] + answer[0] = 45 + 10 = 55
,answer[2] = d[2] + answer[1] = -10 + 55 = 45
… - 最最后发现,只申请一个数组表示
d[]
和answer[]
就可以了,over!
代码
1 | public int[] corpFlightBookings( |
拼车
你以为这就完了吗?不要太天真。再来看一下这个题,或许会给你带来新思路。
题目描述
假设你是一位顺风车司机,车上最初有 capacity
个空座位可以用来载客。由于道路的限制,车 只能 向一个方向行驶(也就是说,不允许掉头或改变方向,你可以将其想象为一个向量)。
这儿有一份行程计划表 trips[][]
,其中 trips[i] = [num_passengers, start_location, end_location]
包含了你的第 i
次行程信息:
必须接送的乘客数量;
乘客的上车地点;
以及乘客的下车地点。
这些给出的地点位置是从你的 初始 出发位置向前行驶到这些地点所需的距离(它们一定在你的行驶方向上)。
请你根据给出的行程计划表和车子的座位数,来判断你的车是否可以顺利完成接送所用乘客的任务(当且仅当你可以在所有给定的行程中接送所有乘客时,返回 true
,否则请返回 false
)。
示例 1:
输入:trips = [[2,1,5],[3,3,7]], capacity = 4
输出:false
示例 2:
输入:trips = [[2,1,5],[3,3,7]], capacity = 5
输出:true
提示:
- 你可以假设乘客会自觉遵守 “先下后上” 的良好素质
trips.length <= 1000
1 <= trips[i][0] <= 100
题目分析
这道题实际上就是问,车的座位数量是否能满足每个行程i
的乘客,即每个乘客都坐上座位,能则返回true
,否则返回false
。
如果我们能计算出,行程i
,要乘车的乘客的数量,然后跟capacity
对比一下,就能得到答案了。
很显然,要乘车的乘客的数量 = 车上原来乘客的数量 - 下车乘客数量 + 上车乘客数量。
思路
我们可以用数组或者Map记录,行程i
,下车乘客的数量和上车乘客的数量,然后行程开始到结束,计算要乘车的乘客数量,并与capacity
比较。
代码实现Map版
1 | public boolean carPooling( |
代码实现数组版
1 | public boolean carPooling( |
回头
拼车的代码与场景感觉好理解一些,因为生活中我们就是这样,先下后上,能不能坐上座,就看车上原来有多少人,还有下车多少人。
我们再回来来看航班预订统计这题,实际上跟拼车是完全一样的题目。
我看到有人问,计算bookings[i] = [i, j, k]
预订变化数量的时候,为啥是第j + 1
的位置要减k
,而不是j
的位置呢?因为,j - 1
的位置,航班预订座位数量应该加k
,而j
的位置,航班预订座位数量也加k
,所以j
和j - 1
之间数量是没有变化的。但是,j + 1
的位置航班数量不再加k
了,所以j + 1
相对于j
位置航班预订数量是减少k
的。
而拼车这道题,trips[i][j]
,在j
位置,车到站了,乘客就下车了,再坐一站就过站了…
总之,两道题本质是完全一样的,只不过略微有些细节不同。