2021.8.31 每日一题,不知不觉,8月都到最后一天了,又一个打卡徽章
这里有 n 个航班,它们分别从 1 到 n 进行编号。
有一份航班预订表 bookings ,表中第 i 条预订记录 bookings[i] = [firsti, lasti, seatsi] 意味着在从 firsti 到 lasti (包含 firsti 和 lasti )的 每个航班 上预订了 seatsi 个座位。
请你返回一个长度为 n 的数组 answer,其中 answer[i] 是航班 i 上预订的座位总数。
示例 1:
输入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
输出:[10,55,45,25,25]
解释:
航班编号 1 2 3 4 5
预订记录 1 : 10 10
预订记录 2 : 20 20
预订记录 3 : 25 25 25 25
总座位数: 10 55 45 25 25
因此,answer = [10,55,45,25,25]
示例 2:
输入:bookings = [[1,2,10],[2,2,15]], n = 2
输出:[10,25]
解释:
航班编号 1 2
预订记录 1 : 10 10
预订记录 2 : 15
总座位数: 10 25
因此,answer = [10,25]
提示:
1 <= n <= 2 * 10^4
1 <= bookings.length <= 2 * 10^4
bookings[i].length == 3
1 <= firsti <= lasti <= n
1 <= seatsi <= 10^4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/corporate-flight-bookings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
差分数组,用diff表示到每一站,航班的人数变化,
然后求前缀和,就是最后的结果
class Solution {
public int[] corpFlightBookings(int[][] bookings, int n) {
//一看做过,再看差分数组,在每次变化的位置改变数字
int l = bookings.length;
int[] diff = new int[n + 1];
for(int i = 0; i < l; i++){
int[] temp = bookings[i];
diff[temp[0] - 1] += temp[2];
diff[temp[1]] -= temp[2];
}
int[] res = new int[n];
res[0] = diff[0];
for(int i = 1; i < n; i++){
res[i] += res[i - 1] + diff[i];
}
return res;
}
}
再次回顾线段树和树状数组,看了一下三叶姐的线段树,因为是区间修改,所以加入了懒标记,瞬间变复杂了,没时间研究了,看了一下别人的题解,发现树状数组也能做
树状数组的写法一点没变,变的是在添加节点的时候,也是和差分数组一样添加节点的值,这为啥是正确的呢?
先自己举了个例子,发现和差分有点像
然后去看了一波解释:
用树状数组维护一个差分数组的前缀和,因为可推得若b[i]=a[i]-a[i-1],则a[i]=b[1]+…+b[i] (b[1]=a[1]-a[0],a[0]=0) 。 可发现a[i]只与b[j] (j<=i)有关,若将b[j]加上delta,其后所有值都将加dlt,因此只需改变b[i]就可实现b[i]到b[n]的区间修改。而将b[j+1]减去dlt,对a[j]无影响,其后所有值也减去dlt,恢复原值,即实现了区间修改操作。 因为求取a值用到的是前缀和,因此设t[i]为b[1]到b[i]的前缀和,a[i]=t[i]=b[1]+…b[i],即可大大降低时间复杂度。
自己理解了一下,在树状数组的某一点加一个值,相当于后面的所有点都加了这个值;而某一点减去一个值,相当于后面所有点都减去了这个值;而查询的时候,相当于求前缀和;树状数组中节点的值,其实相当于差分数组中,每个节点的变化量,只不过把变化量,变成了树状的形式;查询的时候,是不会重复查到相同的变化的
class Solution {
int[] tree;
int l;
public int lowbit(int x){
return x & (-x);
}
public void add(int index, int u){
while(index < l){
tree[index] += u;
index += lowbit(index);
}
}
public int query(int i){
int res = 0;
while(i > 0){
res += tree[i];
i -= lowbit(i);
}
return res;
}
public int[] corpFlightBookings(int[][] bookings, int n) {
l = n + 1;
tree = new int[l];
for(int[] temp : bookings){
add(temp[0], temp[2]);
add(temp[1] + 1, -temp[2]);
}
int[] res = new int[n];
for(int i = 0; i < n; i++){
res[i] = query(i + 1);
}
return res;
}
}
2021.9.1 每日一题,这就九月了
给你两个版本号 version1 和 version2 ,请你比较它们。
版本号由一个或多个修订号组成,各修订号由一个 ‘.’ 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。
比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。
返回规则如下:
如果 version1 > version2 返回 1,
如果 version1 < version2 返回 -1,
除此之外返回 0。
示例 1:
输入:version1 = “1.01”, version2 = “1.001”
输出:0
解释:忽略前导零,“01” 和 “001” 都表示相同的整数 “1”
示例 2:
输入:version1 = “1.0”, version2 = “1.0.0”
输出:0
解释:version1 没有指定下标为 2 的修订号,即视为 “0”
示例 3:
输入:version1 = “0.1”, version2 = “1.1”
输出:-1
解释:version1 中下标为 0 的修订号是 “0”,version2 中下标为 0 的修订号是 “1” 。0 < 1,所以 version1 < version2
示例 4:
输入:version1 = “1.0.1”, version2 = “1”
输出:1
示例 5:
输入:version1 = “7.5.2.4”, version2 = “7.5.3”
输出:-1
提示:
1 <= version1.length, version2.length <= 500
version1 和 version2 仅包含数字和 ‘.’
version1 和 version2 都是 有效版本号
version1 和 version2 的所有修订号都可以存储在 32 位整数 中
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/compare-version-numbers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
模拟,注意转移字符是两个
我一般转整型用的是valueOf,我是为了好记,但是一般别人写都是parseInt,有啥区别呢?学到了,看来以后得用parseInt了
valueOf方法是得到的Integer包装类,而parseInt得到的是int基本类型,valueOf调用了parseInt方法
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}
class Solution {
public int compareVersion(String version1, String version2) {
String[] ss1 = version1.split("\\.");
String[] ss2 = version2.split("\\.");
int l1 = ss1.length;
int l2 = ss2.length;
int idx1 = 0;
int idx2 = 0;
while(idx1 < l1 && idx2 < l2){
String s1 = ss1[idx1];
String s2 = ss2[idx2];
int i = 0;
for(; i < s1.length(); i++){
if(s1.charAt(i) != '0')
break;
}
int j = 0;
for(; j < s2.length(); j++){
if(s2.charAt(j) != '0')
break;
}
int n1 = 0;
int n2 = 0;
if(i != s1.length()){
s1 = s1.substring(i, s1.length());
n1 = Integer.valueOf(s1);
}
if(j != s2.length()){
s2 = s2.substring(j, s2.length());
n2 = Integer.valueOf(s2);
}
if(n1 < n2){
return -1;
}else if(n1 > n2)
return 1;
idx1++;
idx2++;
}
if(idx1 == l1){
while(idx2 < l2){
String s2 = ss2[idx2];
for(int i = 0; i < s2.length(); i++){
if(s2.charAt(i) != '0')
return -1;
}
idx2++;
}
}else if(idx2 == l2){
while(idx1 < l1){
String s1 = ss1[idx1];
for(int i = 0; i < s1.length(); i++){
if(s1.charAt(i) != '0')
return 1;
}
idx1++;
}
}
return 0;
}
}
看了题解。。这,原来字符串转成整数会自动去掉前导零
class Solution {
public int compareVersion(String version1, String version2) {
String[] ss1 = version1.split("\\.");
String[] ss2 = version2.split("\\.");
int l1 = ss1.length;
int l2 = ss2.length;
int idx1 = 0;
int idx2 = 0;
while(idx1 < l1 || idx2 < l2){
int n1 = 0;
int n2 = 0;
if(idx1 < l1){
String s1 = ss1[idx1];
n1 = Integer.valueOf(s1);
}
if(idx2 < l2){
String s2 = ss2[idx2];
n2 = Integer.valueOf(s2);
}
if(n1 < n2){
return -1;
}else if(n1 > n2)
return 1;
idx1++;
idx2++;
}
return 0;
}
}
2021.9.2 每日一题
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
双指针,设链表长度为n,要找倒数第k个,让第一个指针先走k步,达到第k + 1个节点;然后两个指针同时走,直到第一个指针到头,也就是第一个指针又走了n - k步,所以第二个指针也走了n - k步,也就是走到了n-k+1个节点,即倒数第k个节点
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
//双指针,第一个先走k步
ListNode index1 = head;
ListNode index2 = head;
for(int i = 0; i < k; i++){
index2 = index2.next;
}
//然后同时走到末尾
while(index2 != null){
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}