LeetCode 1109. 航班预订统计(差分数组、基于差分的树状数组)/ 165. 比较版本号 / 剑指 Offer 22. 链表中倒数第k个节点

1109. 航班预订统计

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;
    }
}

165. 比较版本号

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;
    }
}

剑指 Offer 22. 链表中倒数第k个节点

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;
    }
}

你可能感兴趣的:(LeetCode,leetcode,java)