代码随想录算法训练营第三十六天|435. 无重叠区间、763.划分字母区间、56. 合并区间

70 阅读1分钟

435. 无重叠区间

题目链接:435. 无重叠区间

思路:按照左边界从小到大进行排序,定义currRight记录当前覆盖范围的右边界,从第1个元素开始遍历数组,如果当前元素发生重叠,就让删除的值加一,并且更新覆盖范围的最小右边界(这里要表示要删除右边界更大的元素),否则只更新覆盖范围的右边界。

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        int count = 0;
        // 按照起始位置从小到大进行排序
        Arrays.sort(intervals, (a, b) -> {
            return Integer.compare(a[0], b[0]);
        });
        // 当前覆盖范围的最小右边界
        int minRight = intervals[0][1];
        for (int i = 1; i < intervals.length; i++) {
            if (intervals[i][0] < minRight) {
                count++;
                minRight = Math.min(minRight, intervals[i][1]);
            } else {
                minRight = intervals[i][1];
            }
        }
        return count;
    }
}

本题也可以按照右边界来从小到大排序,但同时也要注意:删除的是右边界较大的那个元素。

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        Arrays.sort(intervals, (a, b) -> {
            // 按照区间右边界升序排序
            return a[1] - b[1];
        });

        int count = 0;
        int edge = Integer.MIN_VALUE;
        for (int i = 0; i < intervals.length; i++) {
            // 若上一个区间的右边界小于当前区间的左边界,说明无交集
            if (edge <= intervals[i][0]) {
                edge = intervals[i][1];
            } else {
                count++;
            }
        }
        return count;
    }
}

763.划分字母区间

题目链接:763. 划分字母区间

思路:如果我们能够找到之前遍历过的所有字母的最远边界,那么这个边界就是要划分的地方。首先我们要记录每一个字母最后出现的位置,遍历字符串,不断更新出现过的字母的最远边界,如果当前下标和出现过的字母的最远边界相同,则该处为分割点。

image.png

class Solution {
    public List<Integer> partitionLabels(String s) {
        int[] hash = new int[26];
        List<Integer> res = new ArrayList<>();
        char[] ch = s.toCharArray();
        // 记录每一个字母最后出现的位置。
        for (int i = 0; i < ch.length; i++) {
            hash[ch[i] - 'a'] = i;
        }
        int index = 0; // 当前片段出现过的字母的最远边界
        int last = -1; // 上一个片段结束的下标
        for (int i = 0; i < ch.length; i++) {
            // 更新最远边界
            index = Math.max(index, hash[ch[i] - 'a']);
            if (index == i) {
                res.add(i - last);
                last = i;
            }
        }
        return res;
    }
}

56. 合并区间

题目链接:56. 合并区间

思路:按照左边界从小到大排序,定义range记录需要合并的左边界和右边界,定义maxRight记录当前覆盖的最大右边界,如果发生重叠,更新最大右边界,否则收集结果并重置range和最大右边界。

class Solution {
    public int[][] merge(int[][] intervals) {
        List<int[]> res = new ArrayList<>();
        // 按照左边界从小到大排序,左边界相同时右边界小的在前
        Arrays.sort(intervals, (a, b) -> {
            if (a[0] == b[0]) return Integer.compare(a[1], b[1]);
            return Integer.compare(a[0], b[0]);
        });
        // 当前覆盖范围的最大右边界
        int maxRight = intervals[0][1];
        // 记录合并的范围
        int[] range = new int[2];
        range[0] = intervals[0][0]; 
        for (int i = 1; i < intervals.length; i++) {
            if (intervals[i][0] <= maxRight) { // 如果发生重叠,更新覆盖范围的最大右边界
                maxRight = Math.max(intervals[i][1], minLeft);
            } else { // 没有重叠,记录结果
                range[1] = maxRight;
                int[] t = {range[0], range[1]};
                res.add(t);
                // 重置记录数组和最大右边界
                range[0] = intervals[i][0];
                maxRight = intervals[i][1];
            }
        }
        // 收集最后一个结果
        range[1] = maxRight;
        res.add(range);
        return res.toArray(new int[res.size()][]);
    }
}