力扣刷题1-区间、括号、差分

68 阅读5分钟

1 区间问题

1288. 删除被覆盖区间

题目描述:给你一个区间列表,请你删除列表中被其他区间所覆盖的区间。返回剩余区间数目。
区间 [a,b) 被区间 [c,d) 覆盖,必须满足:c <= a 且 b <= d
输入:intervals = [[1,4],[3,6],[2,8]] 输出:2

思路:
1 排序intvs,注意按照start升序、end降序
2 l、r表示上个区间边界,遍历统计覆盖区间,3种情况

int[] cur = intervals[i];
if (r >= cur[1]) {
    res++; //覆盖区间数++
} else if (r < cur[1] && r >= cur[0]) {
    l = cur[0];
    r = cur[1];
} else {
    l = cur[0];
    r = cur[1];
}

数组可以直接拷贝

tmp = intvs[i]
intvs[i] = intvs[j]
intvs[j] = tmp

56. 合并区间

题目描述:请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]

思路:
1 排序intvs,按照start升序
2 遍历区间数组,重叠区域 处理成1个,非重叠区域 2个

for (int i = 1; i < n; i++) {
    int[] last = res[k];
    int[] cur = intervals[i];
    if (last[1] >= cur[1]) {
        continue;
    } else if (last[1] >= cur[0] && last[1] < cur[1]) {
        last[1] = cur[1];
        res[k] = last;
    } else {
        k++;
        res[k] = cur;
    }
}
return Arrays.copyOf(res, k + 1);

拷贝数组前k个作为新数组

Arrays.copyOf(res, k)

986. 区间列表的交集

题目描述:两个由一些 闭区间 组成的列表,firstList 和 secondList,每个区间列表都是成对 不相交 的,并且已经排序 。返回这两个区间列表的交集 。 输入:firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]]
输出:[[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]

思路: 双指针 1 梳理不相交条件(a2<b1 || a1 > b2) => 相交条件 (a2>=b1 && a1<=b2)
交集区间c1,c2,c1=max(a1,b1), c2=min(a2,b2)

2 指针i、j 增长

while (i < m && j < n) {
    int[] a = firstList[i];
    int[] b = secondList[j];
    // 相交情况
    if (a[1] >= b[0] && b[1] >= a[0]) {
        int[] c = new int[2];
        c[0] = Math.max(a[0], b[0]);
        c[1] = Math.min(a[1], b[1]);
        res[k++] = c;
    }
    // 根据右边界判断指针增长
    if (a[1] < b[1]) {
        i++;
    } else {
        j++;
    }
}
return Arrays.copyOf(res, k);

注意:结果数组长度 需要设置最大m+n int[][] res = new int[m + n][2]

2 括号相关问题

20 有效括号

基本问题 输入:s = "()[]{}" 输出:true

思路:stack,遍历s,遇到左括号即入栈,遇到右括号即判断匹配

921 使括号有效的最少添加

题目描述:有效括号递归定义,给括号字符串s一次添加操作 输入:s = "())" 输出:1

思路:遍历,res、need分别记录插入左括次数和右括号需求数。判断当前括号,遇到左括号need++,遇到右括号need--,判断当前后,检查need是否为-1,如果是说明需要插入左括号。

public int minAddToMakeValid(String s) {
    int n = s.length();
    int res = 0;  //left
    int need = 0;  //right
    for (int i = 0; i < n; i++) {
        if (s.charAt(i) == '(') {
            need++;
        }
        if (s.charAt(i) == ')') {
            need--;
        }
        if (need == -1) {
            res++;
            need = 0;
        }
    }
    return res + need;
}

1541 平衡括号字符串最小插入次数

题目较难

思路:遍历,res、need分别记录 插入左括次数和 右括号需求数,1个左括号匹配2个右括号。参照921题目写法,不同的是括号匹配规则, 难点:当遇到左括号时,若对右括号需求数为奇数,需要插1个右括号

if (s.charAt(i) == '(') {
    need += 2;
    if (need % 2 == 1) {
        System.out.println(i+ ": insert )");
        res++; // 插入右括号
        need--;
    }
}

3 前缀和、差分

前缀和

// preSum[i] 表示[0, i-1]元素和
preSum[0] = 0;
// 前缀和
for (int i = 0; i < n; i++) {
    preSum[i + 1] = preSum[i] + nums[i];
}

差分

diff[0] = num[0]

for (int i = 1; i < n; i++) {
  diff[i] = num[i] - num[i-1];
}

差分反推原始数组nums

int[] res = new int[diff.len];
res[0] = diff[0]
for (int i = 1; i < n; i++) {
  res[i] = res[i-1] + diff[i];
}

560.和为 K 的子数组

题目描述:给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。
输入:nums = [1,1,1], k = 2
输出:2

思路:
解法1:暴力解法遍历,超时

for (int i = 1; i <= n; i++) {
    for (int j = 0; j < i; j++) {
        // [j, i-1] 区间
        if (sum[i] - sum[j] == k) {
            res++;
        }
    }
}

解法2:前缀和+哈希表优化,动态规划
使用哈希表preSum记录前缀和,key:sum_i value: 前缀和为sum_i的次数。
sum[i] - sum[j] = k 等价于 sum[j] = sum[i] - k sum[j] 在计算sum[i] 前已计算得到j<i

int sum_i = 0;
// 前缀和
for (int i = 0; i < n; i++) {
    sum_i += nums[i];
    int sum_j = sum_i - k;
    if (preSum.containsKey(sum_j)) {
        res += preSum.get(sum_j);
    }
    preSum.put(sum_i, preSum.getOrDefault(sum_i, 0) + 1);
}
return res;

1109 航班预订

题目描述:n 个航班,它们分别从 1 到 n 进行编号,bookings bookings[i] = [firsti, lasti, seatsi] 表示从 firsti 到 lasti(包含 firsti 和 lasti )的 每个航班 上预订了 seatsi 个座位。
请你返回一个长度为 n 的数组 answer,里面的元素是每个航班预定的座位总数。
input: bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
output: [10,55,45,25,25]

思路:

思路: 对区间元素执行加法,等价于对差分数组操作,利用差分数组反推原始数组。
差分数组diff[i]=nums[i]-nums[i-1], i从1开始,diff[0]=nums[0]
对区间[l, r] (l、r都从0开始)所有元素加val,等价于 diff[l]+=val, diff[r+1] -=val,特别注意r<n1r<n-1才需要执行第2步。

for(int[] booking : bookings) {
    // 对区间[l,r] +val
    int l = booking[0] - 1;
    diff[l] += booking[2];
    int r = booking[1] - 1;
    if (r < n - 1) {
        diff[r+1] -= booking[2];
    }
}
int[] res = new int[n];
res[0] = diff[0];
for (int i=1; i< n; i++) {
    res[i] = diff[i] + res[i-1];
}