day31 56. 合并区间&&763. 划分字母区间&&435. 无重叠区间

    1. 合并区间
      问题描述
      给定一个区间的集合,合并所有重叠的区间。
      代码逻辑
      排序:首先按照区间的起始位置对所有区间进行升序排序。
      合并:
      初始化一个当前区间 cur 为第一个区间。
      遍历所有区间,对于每个区间:
      如果当前区间的起始位置小于等于 cur 的结束位置,说明它们重叠,更新 cur 的结束位置为两者结束位置的最大值。
      如果不重叠,则将 cur 添加到结果列表中,并将当前区间设为新的 cur。
      结果:将最终的 cur 添加到结果列表中,并将其转换为数组返回。
      关键点
      排序:按区间起始位置排序是合并区间的前提。
      贪心思想:通过比较当前区间的起始位置和已合并区间的结束位置,决定是否合并。
      时间复杂度
      排序:O(nlogn)
      遍历:O(n)
      总时间复杂度:O(nlogn)
      空间复杂度
      O(n):用于存储结果的列表。
点击查看代码
//56. 合并区间
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals,new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0] - o2[0];
            }
        });
        ArrayList<int[]> res = new ArrayList<>();
        int[] cur = intervals[0];
        for (int[] interval : intervals) {
            if (interval[0]<=cur[1]) {
                cur[1]=Math.max(interval[1],cur[1]);
            }else {
                res.add(cur);
                cur=interval;
            }
        }
        res.add(cur);
        return res.toArray(new int[res.size()][]);
    }

    1. 划分字母区间
      问题描述
      给定一个字符串,将字符串划分为若干个子串,使得每个子串内的字符互不相同。
      代码逻辑
      记录字符的最后出现位置:
      使用一个数组 arr,记录每个字符在字符串中最后出现的索引。
      划分区间:
      遍历字符串,对于每个字符:
      找到当前字符及其后续字符的最大右边界。
      如果当前右边界已经是最大右边界,则将当前区间长度加入结果列表,并移动到下一个区间。
      如果有更大的右边界,则更新右边界并重新验证。
      优化版本:
      使用一个变量 start 记录当前区间的起始位置,end 记录当前区间的最大右边界。
      遍历字符串时,更新 end 为当前字符的最后出现位置。
      当遍历到 end 时,说明当前区间结束,将区间长度加入结果列表,并更新 start。
      关键点
      字符的最后出现位置:通过数组快速查询每个字符的最后出现位置。
      贪心思想:每次找到当前区间的最大右边界,确保区间内的字符互不相同。
      时间复杂度
      遍历字符串:O(n)
      查询字符最后出现位置:O(1)
      总时间复杂度:O(n)
      空间复杂度
      O(1):数组 arr 的大小固定为 26,不随输入规模变化。
点击查看代码
//763. 划分字母区间
    public List<Integer> partitionLabels(String s) {
        int[] arr = new int[26];
        for (int i = 0; i < s.length(); i++) {
            arr[s.charAt(i) - 'a']=i;
        }
        List<Integer> res = new ArrayList<>();
        for (int i = 0; i < s.length();) {
            int curRight = arr[s.charAt(i) - 'a'];//初始右边界
            while (true){
                int maxRight = maxRight(s,arr,i,curRight);//找最大右边界
                if (curRight==maxRight){//当前边界就是最大右边界
                    res.add(curRight+1-i);
                    i=curRight+1;
                    break;
                }
                curRight=maxRight;//有更大的右边界就更新,重新验证该边界是不是最大右边界
            }
        }
        return res;
        /*效率一样,但这个看起来循环少,更好理解
        // 记录每个字符最后一次出现的位置
        int[] lastPositions = new int[26];
        char[] chars = s.toCharArray();//String拆成char数组
        for (int i = 0; i < chars.length; i++) {
            lastPositions[chars[i] - 'a'] = i;
        }

        // 初始化当前区间的起始位置和上一个区间的结束位置
        int start = 0, end = 0;
        List<Integer> result = new ArrayList<>();

        for (int i = 0; i < chars.length; i++) {
            // 更新当前字符最后一次出现的位置
            int endi = lastPositions[chars[i] - 'a'];
            if(endi < end) continue;
            end = endi;
            // end = Math.max(end, lastPositions[chars[i] - 'a']);

            // 如果当前字符是区间的结束字符,那么就可以分割出一个子串
            if (i == end) {
                result.add(end - start + 1);
                start = end + 1;
            }
        }

        return result;*/
    }
    private int maxRight(String s,int[] arr,int start,int end) {
        int max = arr[s.charAt(start) - 'a'];
        for (int i = start+1; i < end; i++) {
            max = Math.max(max,arr[s.charAt(i) - 'a']);
        }
        return max;
    }

    1. 无重叠区间
      问题描述
      给定一个区间的集合,删除最少数量的区间,使得剩下的区间互不重叠。
      代码逻辑
      排序:按照区间的结束位置对所有区间进行升序排序。
      贪心选择:
      初始化一个变量 end 为第一个区间的结束位置,计数器 count 为 1。
      遍历所有区间,对于每个区间:
      如果当前区间的起始位置大于等于 end,说明它们不重叠,更新 end 为当前区间的结束位置,并增加计数器。
      如果重叠,则跳过当前区间。
      结果:返回需要删除的区间数量,即 intervals.length - count。
      关键点
      排序:按区间结束位置排序是贪心选择的前提。
      贪心思想:优先选择结束位置早的区间,减少后续区间的重叠。
      时间复杂度
      排序:O(nlogn)
      遍历:O(n)
      总时间复杂度:O(nlogn)
      空间复杂度
      O(1):仅使用了常数级别的额外空间。
点击查看代码
//435. 无重叠区间
    public int eraseOverlapIntervals(int[][] intervals) {
        Arrays.sort(intervals,new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[1]<o2[1]){
                    return -1;
                }else if (o1[1]>o2[1]){
                    return 1;
                }else {
                    return 0;
                }
            }
        });
        int count = 1;
        int end = intervals[0][1];
        for (int i = 1; i < intervals.length; i++) {
            if (intervals[i][0]<end){
                continue;
            }else {
                end = intervals[i][1];
                count++;
            }
        }
        return intervals.length-count;
    }

posted @ 2025-02-25 02:44  123木头人-10086  阅读(17)  评论(0)    收藏  举报