D36第八章 贪心算法 part05● 435. 无重叠区间 ● 763.划分字母区间 ● 56. 合并区间

第八章 贪心算法 part05

  • 435. 无重叠区间
  • 给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。问题就是要求非交叉区间的最大个数当确定区间1和区间2重叠后,如何确定是否与区间3也重叠呢,就是取区间1和区间2有边界的最小值,因为这个最小值之前的部分一定是区间1和区间2的重合部分,如果这个最小值也触达到区间3,那么说明区间123都是重合的。
  • 接下来就是找大于区间1结束位置的区间,是从区间4开始,为什么不从区间5开始,别玩那个了这已经是按照有边界排序的了。区间4结束之后,再找到区间6,所以一共记录非交叉区间的个数是三个。总共区间个数是6,减去非交叉区间个数3,移除区间的最小数量就是3.
  • class Solution {
        public int eraseOverlapIntervals(int[][] intervals) {
            Arrays.sort(intervals, (a,b)-> {
                return Integer.compare(a[0],b[0]);
            });
            int count = 1;
            for(int i = 1;i < intervals.length;i++){
                if(intervals[i][0] < intervals[i-1][1]){
                    intervals[i][1] = Math.min(intervals[i - 1][1], intervals[i][1]);
                    continue;
                }else{
                    count++;
                }    
            }
            return intervals.length - count;
        }
    }

  • 763.划分字母区间
  • 字符串s由小写字母组成我们把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
  • 思路:一想到分割字符串就想到了回溯,但本题其实不用回溯去暴力搜索。题目要求同一字母最多出现在一个片段中。那么如何把同一个字母的都圈在一个区间呢
  • 如果没有接触过这种题目的话,还挺有难度的。在遍历的过程中相当于是找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。
  • 可以分为一下两步:
  • 统计每个字符最后出现的位置
  • 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等,则找到了分割点
    class Solution {
        public List<Integer> partitionLabels(String S) {
            List<Integer> list = new LinkedList<>();
            int[] edge = new int[26];
            char[] chars = S.toCharArray();
            for (int i = 0; i < chars.length; i++) {
                edge[chars[i] - 'a'] = i;
            }
            int idx = 0;
            int last = -1;
            for (int i = 0; i < chars.length; i++) {
                idx = Math.max(idx,edge[chars[i] - 'a']);
                if (i == idx) {
                    list.add(i - last);
                    last = i;
                }
            }
            return list;
        }
    }

    这段代码是一个循环,用于遍历字符数组 chars,并将每个字符在 edge 数组中对应的位置(通过字符减去 'a' 的ASCII码值得到的索引)更新为当前字符在字符串 S 中最后一次出现的索引。

    以下是一个举例说明:

    假设输入的字符串 S 为 "abacd",那么 chars 数组的内容为 ['a', 'b', 'a', 'c', 'd']

    初始时,edge 数组的所有元素都是0。

  • i 为0时,表示遍历到 chars 数组的第一个字符 'a',则将 edge['a' - 'a'] 更新为0,即 edge[0] = 0

  • i 为1时,表示遍历到 chars 数组的第二个字符 'b',则将 edge['b' - 'a'] 更新为1,即 edge[1] = 1

  • 最终,edge 数组的内容为 [2, 1, 3, 4, 0, 0, ..., 0],表示每个字符在字符串 S 中最后一次出现的索引。

  • i 为2时,表示遍历到 chars 数组的第三个字符 'a',则将 edge['a' - 'a'] 更新为2,即 edge[0] = 2

  • i 为3时,表示遍历到 chars 数组的第四个字符 'c',则将 edge['c' - 'a'] 更新为3,即 edge[2] = 3

  • i 为4时,表示遍历到 chars 数组的第五个字符 'd',则将 edge['d' - 'a'] 更新为4,即 edge[3] = 4

  •     // 创建一个长度为26的整型数组,用于记录每个字符在字符串S中最后一次出现的索引
        int[] edge = new int[26];
        
        // 将字符串S转换为字符数组
        char[] chars = S.toCharArray();
        
        // 遍历字符数组,记录每个字符在字符串S中最后一次出现的索引
        for (int i = 0; i < chars.length; i++) {
            edge[chars[i] - 'a'] = i;
        }
        
        // 初始化两个指针
        int idx = 0; // 用于记录当前最远的字符索引
        int last = -1; // 用于记录上一个子字符串的末尾索引
        
        // 再次遍历字符数组
        for (int i = 0; i < chars.length; i++) {
            // 更新当前最远的字符索引
            idx = Math.max(idx,edge[chars[i] - 'a']);
            
            // 如果当前索引等于最远索引,说明当前字符是一个子字符串的末尾字符
            if (i == idx) {
                // 将当前子字符串的长度添加到结果链表中
                list.add(i - last);
                
                // 更新上一个子字符串的末尾索引为当前索引
                last = i;
            }
        }
        
        // 返回结果链表
        return list;
    }
    
    

  • 56. 合并区间
  • 给出一个区间的集合,请合并所有重叠的区间,本题的本质其实还是判断重叠区间的问题,这几道题都是判断区间重叠色问题,区别就是判断区间重叠后的逻辑判断,本题是判断区间重叠后要进行区间合并。所以一样的套路,先排序,让所有的相邻区间尽可能地重叠在一起,按左边界或有边界排序都可以,处理逻辑稍有不同。按照左边界从小到大拍好序之后,如果有一定的重叠,本题相邻区间也算重叠,所以是<=
  • 知道如何判断重复之后,剩下的就是合并了。如何去模拟合并区间呢?其实就是用合并区间后左边界和有边界,作为一个新的区间,加入到result数组里就可以了。如果没有合并就把元区间加入到result数组。
  • class Solution {
        public int[][] merge(int[][] intervals) {
            List<int[]> res = new LinkedList<>();
            //按照左边界排序
            Arrays.sort(intervals, (x, y) -> Integer.compare(x[0], y[0]));
            //initial start 是最小左边界
            int start = intervals[0][0];
            int rightmostRightBound = intervals[0][1];
            for (int i = 1; i < intervals.length; i++) {
                //如果左边界大于最大右边界
                if (intervals[i][0] > rightmostRightBound) {
                    //加入区间 并且更新start
                    res.add(new int[]{start, rightmostRightBound});
                    start = intervals[i][0];
                    rightmostRightBound = intervals[i][1];
                } else {
                    //更新最大右边界
                    rightmostRightBound = Math.max(rightmostRightBound, intervals[i][1]);
                }
            }
            res.add(new int[]{start, rightmostRightBound});
            return res.toArray(new int[res.size()][]);
        }
    }
    
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值