最佳数据结构,用于快速检索,更新和保持订购

时间:2017-09-11 17:22:25

标签: java algorithm dictionary data-structures

问题如下

  • 我需要跟踪网址+点击次数。
  • 当用户点击该网址时,我需要能够通过点击次数快速更新网址。
  • 我需要能够快速检索前10个点击次数网址。

注意:假设您无法使用数据库。

实现结果的最佳数据结构是什么?

我之前考虑过使用地图,但地图并没有跟踪前10次点击的排序。

5 个答案:

答案 0 :(得分:1)

你需要一个额外的List<Map.Entry<URL,Integer>>来保持前十名,其中T是最下面的点击次数。

  • 如果计算另一次点击,此计数仍然不大于T:什么都不做。
  • 如果增加的计数大于T,请检查URL是否在列表中。如果是的话,什么都不做。如果不是,请将此条目添加到列表中,如果列表包含10个以上的条目,则排序并删除最后一个条目。更新T.

答案 1 :(得分:1)

我能想到的最好的数据结构是使用TreeSet。

  

TreeSet的元素已排序,因此您可以轻松找到热门项目。   还要确保URL保持一个单独的比较器类来实现   比较器,所以你可以把你的逻辑保持元素排序   基于计数的时间。在创建TreeSet时使用此比较器。插入/更新/删除/获取所有操作都在O(logn)

中进行

以下是代码,您应该如何定义结构。

 TreeSet<URL> treeSet = new TreeSet<URL>(new URLComparator());


class URL {
    private String url;
    int count;

    public URL(String string, int i) {
        url = string;
        count = i;
    }

    @Override
    public int hashCode() {
        return url.hashCode();
    }

    @Override // No need to write this method. Just used it for testing 
    public String toString() {
        return "url : " + url + " ,count : " + count+"\n";
    }

}

还有一个信息 - 使用您的URL类的哈希码方法作为您网址的哈希码。

这是您定义URLComparator类的方法。比较逻辑基于URL计数。

class URLComparator implements Comparator<URL> {

    @Override
    public int compare(URL o1, URL o2) {
        return new Integer(o2.count).compareTo(o1.count);
    }

}

<强>测试

TreeSet<URL> treeSet = new TreeSet<URL>(new URLComparator());

treeSet.add(new URL("url1", 12));
treeSet.add(new URL("url2", 0));
treeSet.add(new URL("url3", 5));

System.out.println(treeSet);

输出: -

[url : url1 ,count : 12
, url : url3 ,count : 5
, url : url2 ,count : 0
]

要打印前10个元素,请使用以下代码。

Iterator<URL> iterator = treeSet.iterator();
int count = 0;
while(count < 10 && iterator.hasNext() ){
    System.out.println(iterator.next());
    count++;
}

答案 2 :(得分:0)

您可以将Map<String, Integer>用作用例:

  1. 跟踪key(网址)和value(点击次数)

  2. 当用户点击该网址时,您可put向地图添加带有映射点击次数的更新网址。

  3. 根据entryset

    排序地图后,您可以检索前10个点击次数
    // create a list out of the entryset of your map
    Set<Map.Entry<String, Integer>> set = map.entrySet();
    List<Map.Entry<String, Integer>> list = new ArrayList<>(set);
    
    // this can be clubbed in another stub to act on top 'N' click counts
    list.sort((o1, o2) -> (o2.getValue()).compareTo(o1.getValue()));
    list.stream().limit(10).forEach(entry -> 
    System.out.println(entry.getKey() + " ==== " + entry.getValue()));
    

答案 3 :(得分:0)

使用Map,您必须对前10个网址的值进行排序。 这会使用比较器按值进行排序,从而使你(ologn)的复杂性降低。

另一种方式是:

将双向链表(大小为10)与HashMap 一起使用(并以LRU缓存方式进行) 检索/更新将是o(1)。 排名前10的结果将是列表中的项目。

双重列表的结构:

class UrlAndCountNode{
    String url;
    int count;
    UrlAndCountNode next;
    UrlAndCountNode prev;   
}

地图结构:

Map<String, UrlAndCountNode>

答案 4 :(得分:0)

这是一个有趣的问题IMO。您似乎需要点击sorted的内容,但同时您需要更改这些值,使用数据结构执行此操作的唯一方法是删除该条目(您要更新)和把更新后的那个放回去。简单地更新clicks将无效。因此,我认为保持按点击排序是一个更好的选择。

缺点是,如果有相同点击次数的条目,它们将被覆盖,因为像guava multiset这样的东西将是一个更好的选择。

因此我会这样做:

static class Holder {
    private final String name;

    private final int clicks;

    public Holder(String name, int clicks) {
        super();
        this.name = name;
        this.clicks = clicks;
    }

    public String getName() {
        return name;
    }

    public int getClicks() {
        return clicks;
    }

    @Override
    public String toString() {
        return "name = " + name + " clicks = " + clicks;
    }
}

方法看起来像这样:

private static List<Holder> firstN(Multiset<Holder> set, int n) {
    return set.stream().limit(n).collect(Collectors.toList());
}

private static void updateOne(Multiset<Holder> set, String urlName, int more) {
    Iterator<Holder> iter = set.iterator();

    int currentClicks = 0;
    boolean found = false;

    while (iter.hasNext()) {
        Holder h = iter.next();
        if (h.getName().equals(urlName)) {
            currentClicks = h.getClicks();
            iter.remove();
            found = true;
        }
    }

    if (found) {
        set.add(new Holder(urlName, currentClicks + more));
    }

}