algorithm - 寻找一种算法来有效地布置日历事件横幅

标签 algorithm calendar minimize

我正在寻找一种算法来有效地放置全天/多天事件横幅,就像 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/

相关文章:

algorithm - 通过插入 11 个节点得到一棵高度为 3 的树,你可以创建多少个 BST?

java - 在自定义日历中实现 ViewPager

r - 反复从矩阵s.t.中删除列和行。行和列最小值的平均值最小

java - 全屏 java 应用程序在屏幕保护程序打开时最小化

r - 优化 r 中函数的函数

algorithm - 帕斯卡三角 Scala : Compute elements of Pascal's triangle using tail recursive approach

arrays - 找到具有给定约束的最大总和的对

c++ - 为什么我在删除 char* 时遇到内存异常

css - react js中antd日历的操作

android - 如何获得所有前几周