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,特别注意才需要执行第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];
}