【问题标题】:Find the top N most popular elements查找前 N 个最流行的元素
【发布时间】:2023-03-09 03:37:02
【问题描述】:

我有一个 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
}

有没有更好、更干净/高效的方法来完成上述操作?

【问题讨论】:

  • 将 Collection.sort() 与自定义比较器一起使用。
  • 我会照 Jawad 所说的去做,并在 TrackDay 类中添加一个 totalTime() 方法
  • @JawadLeWywadi 感谢您的建议。您能否在假设 TrackDay 的定义不能更改的情况下,稍微扩展一下这种比较器的工作原理。一个代码 sn-p 会有很大帮助
  • Collections.sort(...) 的唯一问题是这将发生在适当的位置,如果他的列表排序有意义,那么排序操作后它将随后丢失。
  • @Tgsmith61591 没错,我创建了一个包含所有 totalTimes 值的临时列表,并且临时列表不是原始列表排序的。

标签: java algorithm sorting


【解决方案1】:

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

【讨论】:

  • 似乎快速选择是用于查找列表中第 k 个最大的,而不是列表中第一个 k 大的。您能否举例说明在这种情况下如何使用快速选择?
  • @JohnBaum 列表中前 k 个最大的元素就是所有等于或大于第 k 个最大元素的元素。
  • 这个答案很好。 OP 对创建天和总时间的地图有正确的想法。但是如果你想要最好的性能,你应该使用这个算法而不是 Java 的 sort()。后者会不必要地对整个列表进行排序,而不是只选择前 N 个。遗憾的是 Java API 没有部分排序功能。 C++ 标准库有。
  • 那么,在找到第 k 个最大的(如果我想找到前 3 个,则说第 3 个最大)后,我是否只需循环剩余 trackDays 以找到大于第 3 个最大的那些?
  • 不,当您找到最大的第 k 个最大(枢轴将指向它)时 - 您将需要从 0 迭代到 K(K 是枢轴的索引)并获得所有最大的 K项目。请注意,thay 可能不会出现在排序顺序中 - 没有“合并” - 只是“分区”,因此不会真正发生排序。
【解决方案2】:

这个解决方案应该是线性时间。我假设 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-24
    • 2017-06-29
    • 1970-01-01
    • 1970-01-01
    • 2011-10-24
    • 2017-08-04
    • 2013-12-13
    • 2011-03-03
    相关资源
    最近更新 更多