【问题标题】:TreeSet to find k most frequent words in a book?TreeSet 在书中找到 k 个最常见的单词?
【发布时间】:2018-09-10 06:14:42
【问题描述】:

在书中找到k个最常用的单词(单词可以动态添加)的常见问题通常使用trie和heap的组合来解决。

但是,我认为即使使用 TreeSet 也应该足够了,并且在插入和检索的 log(n) 性能方面更简洁。

树集将包含一个自定义对象:

class MyObj implements Comparable{
  String value;
  int count;

 public int incrementCount(){count++;}

 //override equals and hashcode to make this object unique by string 'value'

 //override compareTo to compare count
}

每当我们在树集中插入对象时,我们首先检查该元素是否已经存在于树集中,如果是,则我们获取 obj 并增加该对象的计数变量。

每当我们想要找到最大的 k 个单词时,我们只需遍历树集的前 k 个元素

您对上述方法有何看法?我觉得这种方法更容易编码和理解,并且与 trie 和 heap 方法的时间复杂度相匹配,以获得 k 个最大元素

编辑:如答案之一所述,插入 myobj 后递增计数变量不会重新排序树集/树图。因此,在增加计数后,我还需要删除并重新插入树集/树图中的对象

【问题讨论】:

    标签: java data-structures time-complexity trie treeset


    【解决方案1】:

    一旦将对象输入TreeSet,如果compareTo方法比较中使用的属性发生变化,TreeSet(或底层TreeMap不会重新排列元素。因此,这种方法无法按您的预期工作。

    这里有一个简单的例子来演示它

    public static class MyObj implements Comparable<MyObj> {
        String value;
        int count;
    
        MyObj(String v, int c) {
            this.value = v;
            this.count = c;
        }
        public void incrementCount(){
            count++;
        }
    
        @Override
        public int compareTo(MyObj o) {
            return Integer.compare(this.count, o.count); //This does the reverse. Orders by freqency
        }
    }
     public static void main(String[] args) {
        Set<MyObj> set = new TreeSet<>();
        MyObj o1 = new MyObj("a", 1);
        MyObj o2 = new MyObj("b", 4);
        MyObj o3 = new MyObj("c", 2);
        set.add(o1);
        set.add(o2);
        set.add(o3);
        System.out.println(set);
       //The above prints [a-1, c-2, b-4]
    
       //Increment the count of c 4 times
        o3.incrementCount();
        o3.incrementCount();
        o3.incrementCount();
        o3.incrementCount();
        System.out.println(set);
       //The above prints [a-1, c-6, b-4]
    

    正如我们所见,c-6 对应的对象并没有被推到最后。

       //Insert a new object
        set.add(new MyObj("d", 3));
        System.out.println(set);
       //this prints [a-1, d-3, c-6, b-4] 
    }
    

    编辑:
    注意事项/问题:

    • 在比较两个词时使用count 如果两个词的频率相同,则会删除一个词。因此,如果频率相同,您需要比较实际单词。
    • 如果我们以更新的频率移除并重新插入对象,它将起作用。但为此,您需要从TreeSet 获取该对象(指定valueMyObj 实例以了解到目前为止的频率)。 Set 没有get 方法。它的contains 方法只是委托给底层的TreeMap's containsKey 方法,该方法使用compareTo 逻辑(而不是equals)来标识对象。 compareTo 函数还考虑了单词的频率,因此我们无法识别集合中的单词以将其删除(除非我们在每次添加时迭代整个集合)

    【讨论】:

    • 如果我重新插入对象呢? (删除和插入)
    【解决方案2】:

    如果删除和插入对象,TreeMap 应该可以工作,整数键作为频率,MyObj 列表作为值,键按频率排序。上述代码的更新证明了这一点:

    public class MyObj  {
    String value;
    int count;
    
    MyObj(String v, int c) {
        this.value = v;
        this.count = c;
    }
    
    public int getCount() {
        return count;
    }
    
    public void incrementCount() {
        count++;
    }
    
    
    
    @Override
    public String toString() {
        return value + " " + count;
    }
    
    public static void put(Map<Integer, List<MyObj>> map, MyObj value) {
        List<MyObj> myObjs = map.get(value.getCount());
        if (myObjs == null) {
            myObjs = new ArrayList<>();
            map.put(value.getCount(),myObjs);
        }
        myObjs.add(value);
    }
    
    public static void main(String[] args) {
        TreeMap<Integer, List<MyObj>> set = new TreeMap<>();
        MyObj o1 = new MyObj("a", 1);
        MyObj o2 = new MyObj("b", 4);
        MyObj o3 = new MyObj("c", 2);
        MyObj o4 = new MyObj("f", 4);
    
        put(set,o1);
        put(set,o2);
        put(set,o3);
        System.out.println(set);
    
        put(set,o4);
        System.out.println(set);
    }
    

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-14
      • 1970-01-01
      • 2021-07-29
      • 1970-01-01
      • 2016-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多