Java 找到前N个最流行的元素

Java 找到前N个最流行的元素,java,algorithm,sorting,Java,Algorithm,Sorting,我有一份田径日物品清单,供跑步者在不同的日子里在田径场上跑步。每对开始/结束时间表示跑步者跑了一圈。我们保证有一个匹配的开始/完成日期(按照它们在相应列表中出现的顺序): 有没有更好、更干净/更有效的方法来完成上述任务?您试图解决的问题是众所周知的,尤其是-。在一般工程中排序时,对于大型集合,最好考虑这种方法,因为它会给您线性时间而不是N*log(n)。我假设startTimes和finishTimes支持随机访问。我不知道DateTime是哪个API的一部分,所以我使用了java.time.L

我有一份田径日物品清单,供跑步者在不同的日子里在田径场上跑步。每对开始/结束时间表示跑步者跑了一圈。我们保证有一个匹配的开始/完成日期(按照它们在相应列表中出现的顺序):


有没有更好、更干净/更有效的方法来完成上述任务?

您试图解决的问题是众所周知的,尤其是-。在一般工程中排序时,对于大型集合,最好考虑这种方法,因为它会给您线性时间而不是N*log(n)。我假设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;
}
公共列表findTop(列表跟踪天数,整数限制){
limit=Math.min(limit,trackDays.size());
列表持续时间=新的ArrayList(Collections.nCopies(limit,Duration.ZERO));
列表结果=新的ArrayList(Collections.nCopies(limit,null));
int lastIndex=极限-1;
用于(轨道日轨道日:轨道日){
持续时间=持续时间0;
对于(int i=0,n=trackDay.startTimes.size();i=0;i--){
if(durations.get(i).compareTo(duration)>=0){
打破
}
目的指数=i;
}
if(destinationIndex!=null){
持续时间。删除(lastIndex);
结果:删除(lastIndex);
持续时间。添加(destinationIndex,持续时间);
结果.添加(destinationIndex,trackDay);
}
}
返回结果;
}

使用Collection.sort()和自定义比较器。我会按照Jawad所说的做,并向
TrackDay
添加
totalTime()class@JawadLeWywadi谢谢你的建议。如果TrackDay的定义不能改变,那么您可以稍微扩展一下这样一个比较器是如何工作的。代码片段将极大地有助于
集合。排序(…)
的唯一问题是,这将在适当的位置发生,如果列表的排序有意义,则排序操作之后,列表将丢失。@Tgsmith61591 True,我创建了一个包含所有totalTimes值的临时列表,临时列表的排序不是原始列表。快速选择似乎是为了查找列表中的第k个最大值,而不是列表中的第一个k个最大值。你能举例说明在这种情况下如何使用快速选择吗?@JohnBaum列表中第一个k最大的元素就是所有等于或大于第k个最大元素的元素。这个答案很好。OP对于创建天数和总时间的地图有正确的想法。但是如果您想要获得最佳性能,那么应该使用此算法而不是Java的sort()。后者会对整个列表进行不必要的排序,而不仅仅是选择前N个;C++标准库已经具备了。因此,在找到第k个最大(如果我想找到前3个)时,我是否只循环剩余的轨迹天来找到大于第三个最大的?不,当你发现第k个最大的(枢轴将指向它)时,你需要从0到k迭代(k是枢轴的索引)。并获得所有K最大的项目。请注意,它们可能不会以排序顺序出现-没有“合并”-只有“分区”,因此排序不会真正发生。
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
}
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;
}