区间合并的说明
业务中的区间合并是比较常见的需求,区间合并的核心有两点:
- 合并前排序,后面处理起来可以简单很多;
- 两个区间合并,这是多个区间合并的基础。
完整代码
区间类
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]]