【问题标题】:Extracting a given number of the highest values in a List提取列表中给定数量的最大值
【发布时间】:2011-02-07 04:31:16
【问题描述】:

我正在寻求根据各自的权重在网页上显示固定数量的项目(由 Integer 表示)。找到这些项目的列表几乎可以是任何大小。

想到的第一个解决方案是执行Collections.sort() 并通过List 逐个获取项目。有没有更优雅的解决方案可以用来准备前八项?

【问题讨论】:

  • 你还在坚持使用列表吗?如果没有,还有更好的数据结构,例如优先级队列、映射和集合。
  • 并非如此,但列表在使用 Hibernate 返回结果时更实用。

标签: java algorithm sorting highest


【解决方案1】:

Collections.sort(..)。足够高效。

此算法提供有保证的 n log(n) 性能。

如果您知道列表的一些独特属性,您可以尝试为您的具体案例实施更有效的方法,但这是不合理的。此外,如果您的列表来自数据库,例如,您可以LIMIT 它并在那里订购它而不是在代码中。

【讨论】:

  • 注明。使用LIMIT 是个好主意。 List 没有什么特别之处,只是需要一次根据一个标准(人气和日期)进行排序。
  • +1 表示数据库的想法——在给定问题的参数之外思考。让我想起了Programming Pearls(或者可能是More Programming Pearls)中的邮局解决方案。
  • O(n log(k)) 的堆比 O(n log(n)) 的一般排序要好得多。
【解决方案2】:

您的选择:

  1. 进行linear 搜索,保持沿途找到的前 N ​​个权重。 如果由于某种原因,您不能在显示页面之间重复使用排序结果(例如列表正在快速变化),这应该比对冗长列表进行排序更快。

    更新:我认为线性搜索肯定比排序更好。有关更好的选择算法,请参阅 Wikipedia 文章“Selection_algorithm - Selecting k smallest or largest elements”。

  2. 手动维护一个按重量顺序排序的List(原始的或平行的)。您可以使用Collections.binarySearch() 之类的方法来确定每个新项目的插入位置。

  3. 通过在每次修改、批量修改或显示之前调用Collections.sort(),维护一个按重量顺序排序的List(可能维护一个修改标志以避免对一个已排序的列表)。

  4. 使用为您维护排序权重顺序的数据结构:priority queuetree set 等。您也可以创建自己的数据结构。

  5. 手动维护前 N 个项目的第二个(可能是按权重排序的)数据结构。每当修改原始数据结构时,都会更新此数据结构。您可以创建自己的数据结构来将原始列表和这个“前 N 个缓存”包装在一起。

【讨论】:

  • 感谢 Bert F。​​您不厌其烦地写了一个详尽的答案。
  • 谢谢。只是按照乔尔所说的去做:“想知道一种赢得声誉的简单方法吗?在某个地方找到一个有几个好的但不完整的答案的问题。偷走所有的答案,然后写一个长的、完整的、详细的答案,这比不完整的答案要好. 当人们投票支持你的综合答案时,坐下来赚取积分。” [joelonsoftware.com/items/2008/09/15.html]
【解决方案3】:

您可以使用max-heap

如果您的数据来自数据库,请在该列上放置索引并使用 ORDER BY 和 TOP 或 LIMIT 仅获取您需要显示的记录。

【讨论】:

  • Java 的 PriorityQueue 使用最大堆作为实现。
【解决方案4】:

【讨论】:

    【解决方案5】:

    使用dollar:

    List<Integer> topTen = $(list).sort().slice(10).toList();
    

    不使用美元你应该使用Collections.sort()sort() 它,然后使用list.sublist(0, n) 获取前n 个项目。

    【讨论】:

    • 我喜欢 slice() 方法。看到 Java 的 jQuery 等价物很有趣。
    【解决方案6】:

    既然您说要从中提取这些前 N 项的项目列表可能是任何大小,因此我认为可能很大,我会增加上面简单的sort() 答案(这完全适合合理地-大小的输入)通过建议这里的大部分工作是找到前 N 个 - 然后对这些 N 进行排序是微不足道的。那就是:

    Queue<Integer> topN = new PriorityQueue<Integer>(n);
    for (Integer item : input) {
      if (topN.size() < n) {
        topN.add(item);        
      } else if (item > topN.peek()) {
        topN.add(item);          
        topN.poll();
      }
    }
    
    List<Integer> result = new ArrayList<Integer>(n);
    result.addAll(topN);
    Collections.sort(result, Collections.reverseOrder());
    

    这里的堆(最小堆)至少是有界的。没有必要将所有物品堆成一堆。

    【讨论】:

      【解决方案7】:

      不,不是真的。至少不使用 Java 的内置方法。

      有一些聪明的方法可以比O(n*log(n)) 操作更快地从列表中获取最高(或最低)N 个项目,但这需要您手动编写此解决方案。如果项目的数量保持相对较低(不超过几百个),则使用 Collections.sort() 对其进行排序,然后获取前 N 个数字是 IMO 的方法。

      【讨论】:

        【解决方案8】:

        取决于有多少。让我们将 n 定义为键的总数,将 m 定义为您希望显示的数字。
        对整个事情进行排序:O(nlogn)
        每次扫描数组以查找下一个最大数字:O(n*m)
        所以问题是 - n 与 m 之间的关系是什么?
        如果m &lt; log n,扫描效率会更高。
        否则m &gt;= log n,表示排序会更好。 (因为对于m = log n 的边缘情况,这实际上并不重要,但是排序也会给你带来好处,嗯,对数组进行排序,这总是很好的。

        【讨论】:

          【解决方案9】:

          如果列表的大小为N,要检索的项目数为K,则需要对列表调用Heapify,它将列表(必须是可索引的,例如数组)转换为优先级队列。 (见http://en.wikipedia.org/wiki/Heapsort中的heapify函数)

          检索堆顶部的项目(最大项目)需要 O (lg N) 时间。所以你的总时间是:

          O(N + k lg N)

          假设 k 远小于 N,则优于 O (N lg N)。

          【讨论】:

            【解决方案10】:

            如果保留排序数组或使用不同的数据结构不是一种选择,您可以尝试以下方法。 O 时间类似于对大数组进行排序,但实际上这应该更有效。

            small_array = big_array.slice( number_of_items_to_find );
            small_array.sort();
            least_found_value = small_array.get(0).value;
            
            for ( item in big_array ) {  // needs to skip first few items
              if ( item.value > least_found_value ) {
                small_array.remove(0);
                small_array.insert_sorted(item);
                least_found_value = small_array.get(0).value;
              }
            }
            

            small_array 可以是 Object[],内部循环可以通过交换来完成,而不是实际删除和插入到数组中。

            【讨论】:

              猜你喜欢
              • 2015-10-20
              • 1970-01-01
              • 1970-01-01
              • 2023-03-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-04-04
              • 2019-02-17
              相关资源
              最近更新 更多