区间的合并

区间合并的说明

业务中的区间合并是比较常见的需求,区间合并的核心有两点:

  1. 合并前排序,后面处理起来可以简单很多;
  2. 两个区间合并,这是多个区间合并的基础。

完整代码

区间类

java 复制代码
/**
 * 区间类(left、right可以根据需要改为BigDecimal或其它类型)
 */
public class Section {
    private int left;
    private boolean isLeftClose;
    private int right;
    private boolean isRightClose;

    public Section(int left, boolean isLeftClose, int right, boolean isRightClose) {
        this.left = left;
        this.isLeftClose = isLeftClose;
        this.right = right;
        this.isRightClose = isRightClose;
        checkParam();
    }

    public Section(int left, int right) {
        this.left = left;
        this.isLeftClose = true;
        this.right = right;
        this.isRightClose = true;
        checkParam();
    }

    private void checkParam() {
        if (left == right && !(isLeftClose && isRightClose)) {
            throw new RuntimeException("If left and right are the same, both sides must be closed intervals");
        }
        if (left > right) {
            throw new RuntimeException("left is greater than the right");
        }
    }

    public int getLeft() {
        return left;
    }

    public void setLeft(int left) {
        this.left = left;
    }

    public boolean isLeftClose() {
        return isLeftClose;
    }

    public void setLeftClose(boolean leftClose) {
        isLeftClose = leftClose;
    }

    public int getRight() {
        return right;
    }

    public void setRight(int right) {
        this.right = right;
    }

    public boolean isRightClose() {
        return isRightClose;
    }

    public void setRightClose(boolean rightClose) {
        isRightClose = rightClose;
    }

    @Override
    public String toString() {
        return (isLeftClose ? "[" : "(") + left + ", " + right + (isRightClose ? "]" : ")");
    }
}

区间合并类

java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * 区间合并类
 */
public class MergeSection {
    /**
     * 合并区间
     *
     * @param sections 待合并区间
     * @return 合并后的区间
     */
    public List<Section> mergeSections(List<Section> sections) {
        List<Section> merged = new ArrayList<>();
        if (sections == null || sections.size() == 0) {
            return merged;
        }
        sections.sort((o1, o2) -> o1.getLeft() - o2.getLeft());
        merged = doMerge(sections);
        return merged;
    }

    /**
     * 真正合并区间的函数
     * <p>
     * 由于已经排过序,所以新加入的区间只会跟最后一个区间产生关联。
     * 用last表示最后一个区间,section表示新加入的区间。可能有以下几种情况:
     * 1 section.getLeft() < last.getLeft() -->由于排过序了,所以不会出现这种情况
     * 2 section.getLeft() < last.getRight()
     * 2.1 section.getRight() < last.getRight() -->不用处理
     * 2.2 section.getRight() == last.getRight() -->处理开闭区间
     * 2.3 section.getRight() > last.getRight() -->更新last.getRight()
     * 3 section.getLeft() == last.getRight() -->处理开闭区间
     * 4 section.getLeft() > last.getRight() -->添加区间
     *
     * @param sections 待合并区间
     * @return 合并后的区间
     */
    private List<Section> doMerge(List<Section> sections) {
        List<Section> merged = new ArrayList<>();
        if (sections == null || sections.size() == 0) {
            return merged;
        }
        merged.add(sections.get(0));
        for (int index = 1; index < sections.size(); index++) {
            Section section = sections.get(index);
            Section last = merged.get(merged.size() - 1);
            if (section.getLeft() < last.getRight()) {
                if (last.getLeft() == section.getLeft()) {
                    last.setLeftClose(last.isLeftClose() || section.isLeftClose());
                }
                if (section.getRight() == last.getRight()) {
                    last.setRightClose(last.isRightClose() || section.isRightClose());
                } else if (section.getRight() > last.getRight()) {
                    last.setRight(section.getRight());
                    last.setRightClose(section.isRightClose());
                }
            } else if (section.getLeft() == last.getRight()) {
                if (section.isLeftClose() || last.isRightClose()) {
                    last.setRight(section.getRight());
                    last.setRightClose(section.isRightClose());
                } else {
                    merged.add(section);
                }
            } else {
                merged.add(section);
            }
        }
        return merged;
    }
}

测试代码

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<Section> list = new ArrayList<>();
        list.add(new Section(1, 2));
        list.add(new Section(3, 4));
        list.add(new Section(5, false, 6, false));
        list.add(new Section(6, false, 7, false));
        list.add(new Section(7, true, 8, true));
        list.add(new Section(8, false, 9, false));
        list.add(new Section(10, false, 11, true));
        list.add(new Section(10, true, 11, false));
        list.add(new Section(12, false, 14, false));
        list.add(new Section(12, true, 15, false));
        list.add(new Section(16, true, 18, false));
        list.add(new Section(16, false, 19, true));
        System.out.println(list);
        List<Section> merged = new MergeSection().mergeSections(list);
        System.out.println(merged);
    }
}

运行结果:

复制代码
[[1, 2], [3, 4], (5, 6), (6, 7), [7, 8], (8, 9), (10, 11], [10, 11), (12, 14), [12, 15), [16, 18), (16, 19]]
[[1, 2], [3, 4], (5, 6), (6, 9), [10, 11], [12, 15), [16, 19]]
相关推荐
Nuanyt2 分钟前
LeetCode Hot 100 · 双指针 3 题 Java
java·笔记·算法·leetcode·双指针
Wang's Blog8 分钟前
Java 项目实战: 交友项目 - 圈子互动
java·开发语言·交友
Rain的Java大神之路10 分钟前
北漂 13 年,我把 Java 家底钉成了这张知识图谱(全站免费)
java·后端·程序员
wuminyu33 分钟前
JVM规范学习总结-Class文件结构
java·linux·c语言·jvm·c++
七夜zippoe44 分钟前
Spring AI + Ollama 本地模型:数据不出内网的合规方案(落地全流程)
java·人工智能·spring·ai·ollama
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 查询单条动态:动态详情页的接口与实现
java·开发语言·交友
yuniko-n1 小时前
【JUC】CAS
java·开发语言
captain3761 小时前
Spring Web MVC(2)
java·前端·spring boot·后端·spring·mvc
代码山河1 小时前
调试技巧:IDEA Debug模式入门与使用
java·学习·架构·教程·面向对象·项目
文丑颜不良啊1 小时前
LeetCode851~875题目解析
java·leetcode