区间的合并

区间合并的说明

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

  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]]
相关推荐
e***74953 分钟前
SpringBoot项目集成ONLYOFFICE
java·spring boot·后端
qq_3363139311 分钟前
java基础-常用的API
java·开发语言
百锦再15 分钟前
第21章 构建命令行工具
android·java·图像处理·python·计算机视觉·rust·django
极光代码工作室21 分钟前
基于SpringBoot的校园招聘信息管理系统的设计与实现
java·前端·spring
未若君雅裁31 分钟前
斐波那契数列 - 动态规划实现 详解笔记
java·数据结构·笔记·算法·动态规划·代理模式
断剑zou天涯32 分钟前
【算法笔记】从暴力递归到动态规划(三)
java·算法·动态规划
断剑zou天涯35 分钟前
【算法笔记】从暴力递归到动态规划(一)
java·算法·动态规划
Ace_317508877640 分钟前
微店平台关键字搜索接口深度解析:从 Token 动态生成到多维度数据挖掘
java·前端·javascript
yyt36304584144 分钟前
Maven 命令构建成功但 IDEA 构建失败原因解析
java·maven·intellij-idea
krafft1 小时前
从零入门 Spring AI,详细拆解 ChatClient 调用流程和 Advisor 底层原理,小白可入!
java·spring·ai