代码之家  ›  专栏  ›  技术社区  ›  Hilton Campbell

寻找一种有效布局日历事件横幅的算法

  •  0
  • Hilton Campbell  · 技术社区  · 17 年前

     bbbbb
    aa
    

    aa
     bbbbb
    

     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
    

    当然,我更愿意一次完成。对于数据结构,我目前使用的是从日期到列表的映射,其中对于事件的每一天,我都将事件添加到相应的列表中。因此,一个为期三天的活动出现在三个列表中,每个列表位于地图中某一天的下面。这是一种将结果转换为可视输出的方便结构,但我也对其他数据结构持开放态度。我目前正在使用贪婪算法,我只是按顺序添加每个事件,但这可能会产生不需要的工件,如:

    aa ccc          
     bbbbb
        dd
         eeeeeeeeeeeeeeeee
    

    这浪费了一大笔钱 大量 大多数“e”活动日的空间。

    有什么想法吗?

    2 回复  |  直到 17 年前
        1
  •  6
  •   joel.neely    17 年前

    下面是一个可能的解决方案的高级示意图(使用星期几整数而不是完整日期)。此接口:

    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 创建较长事件先于较短事件的自然顺序。(我下面演示的示例实现使用了长度递减、开始日期递增、标签递增的顺序。)

    这个 方法获取 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    
    
        2
  •  0
  •   Wes P    17 年前

    我认为在这种情况下,最好先确保数据组织正确,然后再进行渲染。我知道你想要一次传球,但我认为结果会好得多。

    例如,将数据组织到给定日期所需的行中,并以可能的最佳方式组织事件,从最长的事件开始(不需要首先显示,但需要首先组织),然后向下移动到最短的事件。这将允许您相应地呈现输出,而不会浪费任何空间,并避免那些“e”事件日。此外,然后:

     bbb
    aa cc
    

    aa cc
     bbb
    

    没关系,因为 x y 总是可以走在任何一边 bbb 甚至在两者之间 aa cc

    推荐文章