zoukankan      html  css  js  c++  java
  • 算法——合并计算区间问题

    一维的区间合并计算问题种类很多,但是都是一个套路,起点排序,在这里做一个汇总。

    合并无序区间

    给出一个区间的集合,请合并所有重叠的区间。
    leetcode

    解题思路:先根据起点进行排序,再遍历数组,然后用一个指针获取当前区间:

    • 如果遇到交集,就合并区间,将指针指向合并过的区间;
    • 如果没有交集,那么就将指针所指的区间添加到答案末尾,再将指针指向后面的区间。
    class Solution {
        public int[][] merge(int[][] intervals) {
            List<int[]> res = new ArrayList<>();
            // 根据区间起点排序
            Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
    
    		// 先指向第一个区间
            int[] cur = intervals[0];
            // 往后遍历集合
            for(int i = 1; i < intervals.length; i++) {
                if(intervals[i][0] > cur[1]) {
                	// 不存在交集,存入指针区间
                    res.add(cur);
                    // 更新指针区间
                    cur = intervals[i];
                } else {
                	// 存在交集,合并两个区间
                    cur[1] = Math.max(cur[1], intervals[i][1]);
                }
            }
    		// 添加最后的答案
            res.add(cur);
    
            return res.toArray(new int[res.size()][]);
        }
    }
    

    插入有序区间

    给出一个无重叠的 ,按照区间起始端点排序的区间列表。
    在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

    leetcode

    解题思路:将插入的区间直接添加进去,就是和上一题一摸一样了。但是题目给的区间集合是有序无重叠的,如果选择添加之后再排序,其实就不符合出题者的意图,时间复杂度也将是nlogn的。
    因此我们应该好好利这个条件,即有序无重叠的。同样也可以利用上一题的思路,进行分类讨论:

    • 首先遍历集合,如果当前区间的右端点小于插入区间的左端点,因为原来的区间都是有序无重复的,那么直接添加到答案末尾即可;
    • 如果当前区间的左端点大于插入区间的右端点,那么就将插入区间直接添加到答案末尾,再将当前的指针替换为新区间;
    • 到了这种情况,就说明当前区间和插入区间有交集了,那么就将两个区间合并,再更新插入区间。

    这样做就不会浪费多余的空间,时间复杂度也是线性的。其实可以考虑二分的做法,快速获取第一个存在交集的区间。

    class Solution {
        public int[][] insert(int[][] in, int[] cur) {
            List<int[]> res = new ArrayList<>();
    
            for(int[] i : in) {
                if(i[1] < cur[0]) {
                	// 当前区间在插入区间之前
                    res.add(i);
                } else if(i[0] > cur[1]) {
                	// 当前区间在插入区间之后
                    res.add(cur);
                    // 更新插入区间
                    cur = i;
                } else {
                	// 合并存在交集的两个区间
                    cur[0] = Math.min(i[0], cur[0]);
                    cur[1] = Math.max(i[1], cur[1]);
                }
            }
    		// 添加最后的区间
            res.add(cur);
    
            return res.toArray(new int[res.size()][]);
        }
    }
    

    计算交集区间个数

    在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。
    一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
    给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。
    leetcode

    解题思路:先排序,出现相交的区间,这里需要将指针区间变成交集,而不是并,然后再将答案加一,如果出现不相交区间,就更新当前指针区间。

    class Solution {
        public int findMinArrowShots(int[][] points) {
            int n = points.length;
            if(n == 0) return 0;
    		// 根据起点来排序
            Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0]));
    
            int r = points[0][1];
            int res = 1;
    
    		// 遍历集合
            for(int i = 1; i < n; i++) {
                if(points[i][0] > r) {
                    res ++;
                    r = points[i][1];
                } else {
                	// 取交集
                    r = Math.min(points[i][1], r);
                }
            }
    
            return res;
        }
    }
    

    计算不相交的区间个数

    解题思路:先排序,出现相交的区间,就合并区间,如果出现不相交区间,就更新当前指针区间,并将答案加一。

  • 相关阅读:
    day 66 crm(3) 自创组件stark界面展示数据
    day 65 crm(2) admin源码解析,以及简单的仿造admin组件
    用 Python+nginx+django 打造在线家庭影院
    django -admin 源码解析
    day 64 crm项目(1) admin组件的初识别以及应用
    云链接 接口不允许 情况 解决方法 mysql Host is not allowed to connect to this MySQL server解决方法
    day 56 linux的安装python3 ,虚拟环境,mysql ,redis
    day55 linux 基础以及系统优化
    Codeforces 989 P循环节01构造 ABCD连通块构造 思维对云遮月参考系坐标轴转换
    Codeforces 990 调和级数路灯贪心暴力 DFS生成树两子树差调水 GCD树连通块暴力
  • 原文地址:https://www.cnblogs.com/lippon/p/14159098.html
Copyright © 2011-2022 走看看