区间的合并

区间合并的说明

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

  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]]
相关推荐
indexsunny13 分钟前
互联网大厂Java面试实战:基于微服务与云原生的电商场景问答解析
java·数据库·spring boot·docker·微服务·云原生·kubernetes
小明的IT世界14 分钟前
编程智能体为何能让LLM在实际工作中表现更好
java·开发语言·人工智能·ai编程
下地种菜小叶16 分钟前
接口幂等怎么设计?一次讲清重复提交、支付回调、幂等键与防重落地方案
java·spring boot·spring·kafka·maven
YDS82917 分钟前
大营销平台 —— 模板方法串联前中置抽奖规则
java·spring boot·ddd
.柒宇.17 分钟前
Java八股之== 与 equals 区别
java·开发语言
时间静止不是简史18 分钟前
当MyBatis-Plus的like遇上SQL通配符:下划线翻车记
java·sql·mybatis
两年半的个人练习生^_^21 分钟前
每日一学:设计模式之建造者模式
java·开发语言·设计模式
我登哥MVP22 分钟前
【SpringMVC笔记】 - 6 - RESTFul编程风格
java·spring boot·spring·servlet·tomcat·maven·restful
yhole27 分钟前
spring security 超详细使用教程(接入springboot、前后端分离)
java·spring boot·spring