找到N个最受欢迎的元素

时间:2015-11-05 16:15:28

标签: java algorithm sorting

我有一个TrackDay对象列表,供跑步者在不同的日子里绕着田径跑。每对开始/结束时间表示跑步者单圈运行。我们保证有匹配的开始/结束日期(按照它们出现在相应列表中的顺序):

TrackDay() {
    List<DateTime> startTimes
    List<DateTime> finishTimes
}

我想找到跑步者跑得最多的前N天(比方说3)。这转化为找到每个TrackDay对象的N个最长的总开始/结束时间。天真的方式是做以下事情:

for (TrackDay td : listOftrackDays) {
    // loop through each start/finish lists and find out the finish-start time for each pair.
    // Add the delta times (finish-start) up for each pair of start/finish objects.
    // Create a map to store the time for each TrackDay
    // sort the map and get the first N entries
}

是否有更好,更干净/更有效的方法来完成上述工作?

2 个答案:

答案 0 :(得分:1)

您尝试解决的问题众所周知为Selection algorithm,尤其是Quick select。虽然排序通常很好,但对于大型集合,最好考虑这种方法,因为它会给你线性时间而不是N * log(N)。

答案 1 :(得分:0)

此解决方案应为线性时间。我假设startTimes和finishTimes支持随机访问。我不知道你的DateTime是什么API,所以使用了java.time.LocalDateTime。

public List<TrackDay> findTop(List<TrackDay> trackDays, int limit) {
    limit = Math.min(limit, trackDays.size());
    List<Duration> durations = new ArrayList<>(Collections.nCopies(limit, Duration.ZERO));
    List<TrackDay> result = new ArrayList<>(Collections.nCopies(limit, null));
    int lastIndex = limit - 1;
    for (TrackDay trackDay : trackDays) {
        Duration duration = Duration.ZERO;
        for (int i = 0, n = trackDay.startTimes.size(); i < n; i++) {
            duration = duration.plus(Duration.between(trackDay.startTimes.get(i), trackDay.finishTimes.get(i)));
        }
        Integer destinationIndex = null;
        for (int i = lastIndex; i >= 0; i--) {
            if (durations.get(i).compareTo(duration) >= 0) {
                break;
            }
            destinationIndex = i;
        }
        if (destinationIndex != null) {
            durations.remove(lastIndex);
            result.remove(lastIndex);
            durations.add(destinationIndex, duration);
            result.add(destinationIndex, trackDay);
        }
    }
    return result;
}