我正在寻找一种算法来有效地放置全天/多天事件横幅,就像 Outlook 或 Google 日历中的月 View 一样。我有许多具有开始和结束日期的事件,按增加的开始(然后结束)日期排序(或任何其他顺序,我正在从数据库表中收集事件)。我想尽量减少垂直空间的平均用量,因为在事件横幅之后我需要放置当天的其他事件(这些总是在给定日期的横幅之后)。因此,例如,如果我有两个事件,一个 1/10-1/11 和一个 1/11-1/15,我宁愿这样安排它们(每一列都是一天):
bbbbb
aa
而不是:
aa
bbbbb
因为当我只添加当天的事件(x、y 和 z)时,我可以这样做(我更喜欢第一个,不想要第二个):
bbbbb vs. aa
aa xyz bbbbb
xyz
但这并不像将较长的事件放在第一位那么简单,因为对于 1/10-1/11、1/13-1/14 和 1/11-1/13,我想要:
aa cc
bbb
相对于:
bbb
aa cc
因为这将允许事件 x 和 y:
aa cc vs. bbb
xbbby aa cc
x y
当然,我更愿意一次完成。对于数据结构,我目前使用的是从日期到列表的映射,对于事件的每一天,我都会将事件添加到相应的列表中。因此,一个为期三天的事件出现在三个列表中,每个列表都在 map 中的某一天之下。这是将结果转换为可视化输出的便捷结构,但我也对其他数据结构持开放态度。我目前正在使用贪婪算法,我只是按顺序添加每个事件,但这会产生不需要的工件,例如:
aa ccc
bbbbb
dd
eeeeeeeeeeeeeeeee
对于大多数“e”事件日,这会浪费很多空间。
有什么想法吗?
最佳答案
这是一个可能解决方案的高级草图(使用星期几整数而不是完整的日期)。这个界面:
public interface IEvent {
public abstract int getFirst(); // first day of event
public abstract int getLast(); // last day of event
public abstract int getLength(); // total number of days
public abstract char getLabel(); // one-char identifier
// true if this and that have NO days in common
public abstract boolean isCompatible(IEvent that);
// true if this is is compatible with all events
public abstract boolean isCompatibleWith(Collection<IEvent> events);
}
必须实现才能使用下面的 layout
方法中表达的算法。
此外,具体类必须实现 Comparable
以创建一个自然顺序,即较长的事件在较短的事件之前。 (我下面演示的示例实现使用了长度降序、开始日期升序、标签升序的顺序。)
layout
方法获取一组 IEvent
实例并返回一个 Map
,它为演示文稿中的每一行分配一组事件可以显示在该行中。
public Map<Integer,Set<IEvent>> layout(Collection<IEvent> events) {
Set<IEvent> remainingEvents = new TreeSet<IEvent>(events);
Map<Integer,Set<IEvent>> result = new TreeMap<Integer,Set<IEvent>>();
int day = 0;
while (0 < remainingEvents.size()) {
Set<IEvent> dayEvents = new TreeSet<IEvent>();
for(IEvent e : remainingEvents) {
if (e.isCompatibleWith(dayEvents)) {
dayEvents.add(e);
}
}
remainingEvents.removeAll(dayEvents);
result.put(day, dayEvents);
++day;
}
return result;
}
每一行都是通过选择最长的剩余事件并逐步选择与当前行先前选择的事件兼容的所有其他事件(按上述顺序)组成的。效果是所有事件尽可能向上“漂浮”而不会发生碰撞。
以下演示展示了您问题中的两个场景,以及一组随机创建的事件。
Event collection:
x(1):4
b(5):2..6
y(1):5
a(2):1..2
z(1):6
Result of layout:
0 -> {b(5):2..6}
1 -> {a(2):1..2, x(1):4, y(1):5, z(1):6}
Visual presentation:
bbbbb
aa xyz
Event collection:
x(1):1
b(3):2..4
a(2):1..2
c(2):4..5
y(1):5
Result of layout:
0 -> {b(3):2..4, x(1):1, y(1):5}
1 -> {a(2):1..2, c(2):4..5}
Visual presentation:
xbbby
aa cc
Event collection:
f(2):1..2
h(2):1..2
d(4):1..4
e(4):2..5
c(1):6
a(2):5..6
g(4):2..5
b(2):0..1
Result of layout:
0 -> {d(4):1..4, a(2):5..6}
1 -> {e(4):2..5, b(2):0..1, c(1):6}
2 -> {g(4):2..5}
3 -> {f(2):1..2}
4 -> {h(2):1..2}
Visual presentation:
ddddaa
bbeeeec
gggg
ff
hh
关于algorithm - 寻找一种算法来有效地布置日历事件横幅,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/400288/