用于查找任务以给定开始时间和持续时间结束的顺序的算法

时间:2016-05-19 07:40:22

标签: java algorithm data-structures

我最近参加了一次采访,其中有人问我这个问题:

给定数组中的开始时间:[1, 2, 3, 2] 及其持续时间[3, 4, 4, 3]

查找并返回任务完成的顺序。对于此示例,结束时间为:[4, 6, 7, 5],因此返回的值应为[1, 3, 4, 2]

我的方法/解决方案:

创建一个数据结构,将每个类表示为具有以下属性的任务对象:

  1. 开始时间
  2. 结束时间
  3. 持续时间
  4. 完成索引
  5. 然后在每个对象的结束时间对数组进行排序。将每个元素与原始数组索引进行比较,并以原始任务顺序返回结束索引。

    请建议任何更好的解决方案。在面试中使用这种方法很难实现(使用白板和标记)。如果没有更好的解决方案,最简单的方法是什么呢?更好的解决方案=更好的时间复杂性。

6 个答案:

答案 0 :(得分:1)

让我们假设你有如下创建的原始集合(如数组)(显然是伪代码):

create collection first[] with elements (starttime, duration)
first.append (1, 3)
first.append (2, 4)
first.append (3, 4)
first.append (2, 3)

这为您提供了(starttime, duration)元组first = [(1,3), (2,4), (3,4), (2,3)]的集合。

然后,您可以使用仅包含两项内容的单独结构执行您想要的操作:

  • 当前结构的索引。
  • 计算的结束时间。

最初,填充此新结构second的数组和数组,以使索引与原始数组first匹配,如下所示:

create array second[] with elements (firstindex, endtime)
for each index in first.indexes:
    second.append (index, first[index].starttime + first[index].duration)

这为您提供了(firstindex, endtime)元组second = [(1,4), (2,6), (3,7), (4,5)]的集合(我们假设这些集合是基于一个而不是从零开始)。

然后你继续根据结束时间排序 second数组,给你second = [(1,4), (4,5), (2,6), (3,7)]

然后,您可以使用以下代码完成任务:

for each index in second.indexes:
    output "task # ", second[index].firstindex
    output " starts at, " first[second[index].firstindex].starttime
    output " duration, " first[second[index].firstindex].duration
    output " ends at ", second[index].endtime, new-line

输出结果为:

task # 1 starts at 1 duration 3 ends at 4
task # 4 starts at 2 duration 3 ends at 5
task # 2 starts at 2 duration 4 ends at 6
task # 3 starts at 3 duration 4 ends at 7

答案 1 :(得分:1)

免责声明:我的答案基于这样的想法,即如果没有任何排序就无法解决这个问题

诚实地说,您解决问题的方法听起来不错。该问题描述了结果应该是某种顺序,这意味着找不到比O(n log n)更快的解决方案是有保证的。

这是因为众所周知,不存在可以对复杂度小于O(n log n)的可排序元素列表进行排序的算法或程序。

这意味着如果您的解决方案在O(n log n)中运行,那么您的解决方案就是最佳选择。如果我是你,我会在你的面试中提到你关于你的解决方案的这个属性,因为这表明你不仅明白你已经解决了问题,而且还知道不可能有一个更好的解决方案。

如果你必须实际执行此操作,只需练习几次。

答案 2 :(得分:0)

最简单,最快速的解决方案>

创建两个数组:

finaltime数组:[4, 6, 7, 5]

索引数组:[0,1,2,3]

使用quicksort对finaltime数组进行排序(或插入排序,如果可以肯定的话,数组将非常小=小于10个数字),每当切换最终时间数组元素时,也要切换索引数组。

当订购finaltime时,索引数组保存结果。

答案 3 :(得分:0)

您的解决方案听起来不错,但实际上您并不需要对象上的某些字段,也不需要自定义对象。

因为您已经专门为Java标记了它:

4294967264

答案 4 :(得分:0)

我创建了一个包含两个成员的任务对象,即开始时间和持续时间。任务对象有一个方法,根据开始时间和持续时间计算结束时间。

此外,我创建了一个任务比较器(见下文),它按结束时间比较两个任务。

然后主类创建这四个任务,将它们放入一个数组列表中,该列表使用上面创建的比较器进行排序。

课程任务:

public class Task {

      private String m_name;
      private double m_startTime;
      private double m_duration;

      ...

      public double calculateEndTime() {
            return m_startTime + m_duration;
      }
}

Class TaskComparator:

public class TaskComparator implements Comparator<Task>{
    @Override
    public int compare(Task t1, Task t2) {
        if (t1 == null || t2 == null) {
            return 0;
        }
    return Double.compare(t1.calculateEndTime(), t2.calculateEndTime());
}

主要方法:

public void go() {
    List<Task> tasks = new ArrayList<Task>();
    tasks.add(new Task("First Task", 1, 3));
    tasks.add(new Task("Second Task", 2, 4));
    tasks.add(new Task("Third Task", 3, 4));
    tasks.add(new Task("Fourth Task", 2, 3));
    Collections.sort(tasks, new TaskComparator());
    for (Task task : tasks) {
        System.out.println(task.toString());
    }
}

答案 5 :(得分:0)

新版本 在Glubus评论之后,我明白我以前的解决方案并不像我想象的那么好。但为了精神错乱,我提出了一个新版本:

  • 不需要排序
  • 将相同位置分配给具有相同结束时间的任务

    int[] calculateCompletionsOrder(int[] starttimes, int[] durations) {
    
        // the key is the endtime (sorted ascending), the value is a list of the taskid with corresponding endtime  
        TreeMap<Integer, List<Integer>> set = new TreeMap<Integer, List<Integer>>();
    
        // first iteration is needed to calucate endtimes
        for (int taskId = 0; taskId < starttimes.length; taskId++) { // time complexity O(nlogn)
            // calculate end time for the current task
            int endtime = starttimes[taskId] + durations[taskId];
            // add the task to the list of task with same end time
            List<Integer> taskIds = set.get(endtime); // time complexity O(logn)
            if (taskIds == null) {
                taskIds = new ArrayList<Integer>();
                set.put(endtime, taskIds); // time complexity O(logn)
            }
            taskIds.add(taskId); // time complexity O(1)
        }
    
        // now calculate the order each task get completed
        int[] completionorders = new int[starttimes.length];
        int order = 1;
        // iterate over end times in ascending order
        for (List<Integer> taskIds : set.values()) { // time complexity O(n)
            for (int taskId : taskIds) {
                // set the completion order for the selected task
                completionorders[taskId] = order;
            }
            // increment order for next group of tasks with next end time 
            order++;
        }
    
        return completionorders;
    }
    

每个插页都会分配排序成本。最终的复杂性是针对最佳,平均,最差的情况O(nlogn)。

旧版

问题是“查找并返回任务完成的顺序”。

这个问题没有说明如何表示订单。我的意思是:“如果你有四个元素,则应该从1到4之间的自然数表示”。

所以在我的解决方案中,顺序用整数表示(...,-3,-2,-1,0,1,2,3,...)。 “订单”也并不意味着两个数字是连续的。

  • 输入:

    • 开始时间:[1,2,3,2]
    • 持续时间:[3,4,4,3]
  • 算法(第一阶段):计算结束时间

    max-end-time = 0;
    for (int i = 0; i < start-time.length; ++i) {
       end-time[i] = start-time[i] + durations[i];
       max-end-time = max-end-time > end-time[i] ? max-end-time : end-time[i];
    }
    
  • 在第一阶段结束时,您的输出是:

    • 结束时间:[4,6,7,5]
    • max-end-time:7
  • 算法(第二阶段):计算完成顺序

    for (int i = 0; i < end-time.length; ++i) {
       order[i] = end-time[i] - max-end-time;           
    }
    

在第二阶段结束时,完成顺序数组为:[ - 3,-1,0,-2]。

因此任务0是第一个,任务2是第三个,任务3是第四个,任务4是第二个,或者是另一个形式:[1,3,4,2]。

复杂性:如果n是算法执行的任务数n + n = 2n次迭代。所以这个算法是O(n)。