【问题标题】:How to get 5 highest values from a hashmap?如何从哈希图中获得 5 个最高值?
【发布时间】:2014-02-23 07:21:35
【问题描述】:

我有一个 Hashmap,它链接存储为键的邮政编码和存储为哈希图中的值的人口。

hashmap 包含大约 33k 个条目。

我正在尝试从 5 个邮政编码中获取 5 个最高人口值,并打印出与 5 个最高人口相关的 5 个邮政编码,但我无法理解如何执行此操作的算法。

如果只有一个,那很容易,但 5 的限制给我带来了一些麻烦。

我知道将 5 个值存储在一个 int 数组中,并且我有一个计数器来确定何时存储其中的 5 个,但仅此而已。

谢谢

    int populatedCounter = 0;

    int[] populatedZip = new int[5];

    it = zipCodePop.entrySet().iterator();
    while (it.hasNext())
    {
        Map.Entry pairs = (Map.Entry)it.next();

        for (int i = 0; i < populatedZip.length; i++)
        {

        }
    }

}

【问题讨论】:

  • Guava 这样的第三方库是公平游戏吗?这可能只是一行Ordering.natural().greatestOf(map.values(), 5)
  • 如何创建一个 max_heap 值;删除最大值然后调整大小;获取下一个最大值,依此类推。
  • 什么是邮编和人口(地图的参数是什么)? Map&lt;Integer, Integer&gt;?
  • 这是作业吗?顺便说一句,另请参阅Finding the second highest number in array,它可以概括为找到 k 个最高数字。
  • 算法:将前 5 个值添加到数组中,对数组进行排序,遍历映射,直到找到高于数组中第一个值(最低)的值,替换最低值,使用数组(如果新值高于其他值),继续。保留键/值对会花费更多精力,但应该足够简单。

标签: java


【解决方案1】:

将此类集合的条目放入列表并对其进行排序是一种选择。但是 33k 个元素是一个数字,其中 O(n*log(n)) 的排序复杂度可能已经对性能产生了显着影响。

一种方法是使用 nr4bt 已经提到的 PriorityQueue(我在他回答时写了这个 sn-p)。它基本上将所有元素插入到根据映射条目的值排序的 PriorityQueue 中。

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.PriorityQueue;

public class GreatestOfMap
{
    public static void main(String[] args)
    {
        Map<String, Integer> map = new HashMap<String, Integer>();

        map.put("zip000", 1234);
        map.put("zip001", 2345);
        map.put("zip002", 3456);
        map.put("zip003", 4567);
        map.put("zip004", 5678);
        map.put("zip005", 6789);
        map.put("zip006", 123);
        map.put("zip007", 234);
        map.put("zip008", 456);
        map.put("zip009", 567);
        map.put("zip010", 7890);
        map.put("zip011", 678);
        map.put("zip012", 789);
        map.put("zip013", 890);

        int n = 5;
        List<Entry<String, Integer>> greatest = findGreatest(map, 5);
        System.out.println("Top "+n+" entries:");
        for (Entry<String, Integer> entry : greatest)
        {
            System.out.println(entry);
        }
    }

    private static <K, V extends Comparable<? super V>> List<Entry<K, V>> 
        findGreatest(Map<K, V> map, int n)
    {
        Comparator<? super Entry<K, V>> comparator = 
            new Comparator<Entry<K, V>>()
        {
            @Override
            public int compare(Entry<K, V> e0, Entry<K, V> e1)
            {
                V v0 = e0.getValue();
                V v1 = e1.getValue();
                return v0.compareTo(v1);
            }
        };
        PriorityQueue<Entry<K, V>> highest = 
            new PriorityQueue<Entry<K,V>>(n, comparator);
        for (Entry<K, V> entry : map.entrySet())
        {
            highest.offer(entry);
            while (highest.size() > n)
            {
                highest.poll();
            }
        }

        List<Entry<K, V>> result = new ArrayList<Map.Entry<K,V>>();
        while (highest.size() > 0)
        {
            result.add(highest.poll());
        }
        return result;
    }
}

【讨论】:

  • @TokugawaIeysu 此解决方案 not 比添加然后排序所有值更好。看看PriorityQueuedocumentationoffer()add()O(log(n)),在这里你称它们为n 次......导致我的O(n log(n)) 复杂性完全相同解决方案,但更难理解。
  • 对于 PriorityQueue,文档引用的n 永远不会大于它包含的(恒定)最大元素数(在本例中为 5)。所以总的运行时间是 O(nlog(5)) = O(n)。与此相反,当 *sorting n 元素时,对于可能较大的 n(在本例中为 33k),运行时间为 O(n*log(n))。但是,我没有做一个基准来查看哪个n 确实存在显着差异,只是渐近复杂度较低。
  • 感谢 Marco,我能够得到人口!...我认为?...有太多条目无法确认它是否正确,但我相信它是正确的,所以再次感谢!
【解决方案2】:

试试这个,使用标准方法并假设人口计数在HashMap 中存储为Integers:

List<Integer> list = new ArrayList<Integer>(zipCodePop.values());
Collections.sort(list, Collections.reverseOrder());
List<Integer> top5 = list.subList(0, 5);

【讨论】:

  • @KevinWorkman 只是好奇是什么让你认为这是家庭作业?只是猜测?
  • @Radiodef “在此数据结构中查找 X 最大值”是一项非常标准的家庭作业,再加上 OP 似乎甚至不知道如何开始解决问题的事实使得我高度怀疑这是作业。我可能是错的,但无论哪种方式,我都会争辩说,“我不知道该怎么做”的答案是向人们展示如何自己解决问题,而不是给他们 20 种他们可以复制的不同可能的解决方案- 在不理解的情况下粘贴。
  • 我了解如何获取“最大”填充 zip,但每次都会更新以获取最大值
【解决方案3】:

公共类 CheckHighiestValue { public static void main(String... s) {

    HashMap<String, Integer> map = new HashMap<String, Integer>();

    map.put("first", 10000);
    map.put("second", 20000);
    map.put("third", 300);
    map.put("fourth", 800012);
    map.put("fifth", 5000);
    map.put("sixth", 30012);
    map.put("seventh", 1234);
    map.put("eighth", 45321);
    map.put("nineth", 5678);

    Set<Entry<String, Integer>> set = map.entrySet();

    List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(
            set);

    Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {

        @Override
        public int compare(Entry<String, Integer> o1,
                Entry<String, Integer> o2) {

            return o2.getValue().compareTo(o1.getValue());
        }

    });
    System.out.println(list.subList(0, 5));
}

}

【讨论】:

  • 我们可以使用list.subList(index,index);的比较器来得到列表中最后5个五位数的值;
【解决方案4】:

PriorityQueue 也会有所帮助,也是一个关于如何从列表中获取前 k 名的好话题,您可以查看this link

PriorityQueue<Integer> p = new PriorityQueue<Integer>(5);

int[] a = new int[]{3,5,10,1,23,42,66,1333,545,110};

for (int i : a){
    p.add(i);
    if (p.size() > 5){
        p.poll();
    }
}

//output will be highest 5, [42, 66, 110, 1333, 545]

你可以有 O(n log(k)) 时间复杂度 // k 是你的最高值。

【讨论】:

  • 感谢您提供的信息,但我不得不更改我原来的描述。我实际上需要找到 5 个最高值(人口),但我需要打印出与这 5 个最高值相关联的邮政编码,这就是我将它们存储在 hashmap 中的原因
  • 只需通过与人口比较获得前 5 名,然后通过使用您的 hashmap 获得邮政编码
【解决方案5】:

这是我制作的,希望能为您提供您想要使用的东西。

public class TopsCollection { 

private static Map<String, Integer> collectors = new HashMap<>();

public TopsCollection() {
}

public void add(String playerName, int score) {
    collectors.put(playerName, score);
}

public void clearCollectors() {
    synchronized (collectors) {
        collectors.clear();
    }
}

public List<Map.Entry<String, Integer>> getTops() {
    return collectors.entrySet().stream().sorted(comparing(Map.Entry::getValue, reverseOrder())).limit(5).collect(toList());
}

public int getTopByName(String name) {
    for (int i = 0; i < getTops().size(); i++) {
        if (getTops().get(i).getKey().contains(name)) {
            return i;
        }
    }
    return 0;
}

getTopByName 允许您获取指定名称的顶部位置。

【讨论】:

    【解决方案6】:

    如果没有电脑,只有一张纸和一支铅笔,您将如何做到这一点?假设您有一叠带有数字的索引卡,而您的工作是找到 5 个最高的数字。你会怎么做?写下其他人可以遵循以实现目标的步骤,当你写出这些步骤时,你就会有一个算法,你可以开始考虑用代码来实现。

    您说单个最大值很容易,所以完全按照您对单个最大值的做法进行操作,但要跟踪五个最大值。一个最大值数组在这里可能会有所帮助。

    【讨论】:

    • 在实现软件时模拟纸和铅笔并不是一个好主意。这是一个微不足道的排序问题,按照您的方法,Tokugawa 将实施排序算法。既然有这么多方法使用标准 API 对数据结构进行排序,他为什么要这样做?
    • 因为我敢打赌这是家庭作业,专门设计使 OP 能够理解通过基于先前工作的算法工作(找到单个最大值)。是什么让您认为“我的方法”鼓励排序算法?您可以使用排序算法以获得最大效率,但基本的蛮力方法可能是教师正在寻找的。其他复制粘贴解决方案剥夺了 OP 解决问题的学习经验,而这正是此类作业的重点。
    • 有关在 StackOverflow 上回答家庭作业问题的更多信息,请参阅:meta.stackexchange.com/questions/10811/…
    【解决方案7】:

    使用流

    int[] populatedZip = map.entrySet().parallelStream()
                .sorted(Map.Entry.<String, Integer>comparingByValue())
                .limit(5)
                .mapToInt(entry -> entry.getValue())
                .toArray();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-03
      • 2017-02-16
      • 1970-01-01
      • 2014-07-25
      • 2018-01-01
      • 2021-10-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多