ビッグワードシーケンスで上位K個の頻繁な単語を見つける最も効率的な方法


85

入力:正の整数Kと大きなテキスト。テキストは実際には単語シーケンスとして表示できます。したがって、単語シーケンスに分解する方法について心配する必要はありません。
出力:テキスト内で最も頻繁に使用されるK語。

私の考えはこんな感じです。

  1. ハッシュテーブルを使用して、単語シーケンス全体をトラバースしながら、すべての単語の頻度を記録します。このフェーズでは、キーは「単語」であり、値は「単語頻度」です。これにはO(n)時間がかかります。

  2. (単語、単語-頻度)ペアを並べ替えます。そして鍵は「単語の頻度」です。これには、通常のソートアルゴリズムではO(n * lg(n))時間がかかります。

  3. ソート後、最初のK語を取得します。これにはO(K)時間がかかります。

要約すると、合計時間はO(n + n lg(n)+ K)です。Kは確かにNよりも小さいため、実際にはO(n lg(n))になります。

これを改善することができます。実際には、上位K語だけが必要です。言い換えれば、頻度は私たちにとって重要ではありません。したがって、「部分ヒープソート」を使用できます。ステップ2)と3)については、ソートを行うだけではありません。代わりに、次のように変更します

2 ')「word-frequency」をキーとして(word、word-frequency)ペアのヒープを構築します。ヒープを構築するにはO(n)時間がかかります。

3 ')ヒープから上位K語を抽出します。各抽出はO(lg(n))です。したがって、合計時間はO(k * lg(n))です。

要約すると、このソリューションのコストは時間O(n + k * lg(n))です。

これは私の考えです。ステップ1)を改善する方法がわかりません。
一部の情報検索の専門家がこの質問にもっと光を当てることができることを願っています。


O(n * logn)ソートにマージソートまたはクイックソートを使用しますか?
committedandroider

1
実際の使用には、サンプルを頼りにするというAaronMaenpaaの答えが最適です。最も頻繁な単語がサンプルから隠れるわけではありません。複雑なオタクにとっては、サンプルのサイズが固定されているため、O(1)です。正確な数はわかりませんが、それらも求めていません。
Nikana Reklawyks 2015

複雑さの分析のレビューが必要な場合は、言及したほうがよいでしょう。nがテキスト内の単語の数で、m異なる単語(タイプ、私たちはそれらと呼びます)の数である場合、ステップ1はO(n)ですが、ステップ2はO(m .lg(m))であり、m << nです(数十億の単語があり、数百万のタイプに達しない場合があります。試してみてください)。したがって、ダミーアルゴリズムを使用しても、O(n + m lg(m))= O(n)のままです。
Nikana Reklawyks 2015

1
plsは、大きなテキストのすべての単語を保持するのに十分なメインメモリがあるという質問に仮定を追加します。10GBのファイルからk = 100の単語を見つけるアプローチを見るのは興味深いでしょう(つまり、すべての単語が4GBのRAMに収まらない)!!
KGhatak 2016年

@KGhatak RAMサイズを超えた場合、どうすればよいですか?
user7098526

回答:


66

これはO(n)時間で実行できます

解決策1:解決策1:

手順:

  1. 単語を数えてハッシュすると、次のような構造になります。

    var hash = {
      "I" : 13,
      "like" : 3,
      "meow" : 3,
      "geek" : 3,
      "burger" : 2,
      "cat" : 1,
      "foo" : 100,
      ...
      ...
    
  2. ハッシュをトラバースして、最も頻繁に使用される単語(この場合は「foo」100)を見つけ、そのサイズの配列を作成します

  3. 次に、ハッシュを再度トラバースし、単語の出現回数を配列インデックスとして使用できます。インデックスに何もない場合は、配列を作成します。それ以外の場合は、配列に追加します。次に、次のような配列になります。

      0   1      2            3                  100
    [[ ],[cat],[burger],[like, meow, geek],[]...[foo]]
    
  4. 次に、配列を最後からトラバースして、k個の単語を収集します。

解決策2:解決策2:

手順:

  1. 同上
  2. 最小ヒープを使用し、最小ヒープのサイズをkに維持し、ハッシュ内の各単語について、単語の出現を最小と比較します。1)最小値より大きい場合は、最小を削除します(最小のサイズの場合)ヒープはk)に等しく、最小ヒープに数値を挿入します。2)単純な条件を休ませます。
  3. 配列をトラバースした後、最小ヒープを配列に変換して配列を返します。

16
ソリューション(1)は、標準のO(n lg n)比較ソートを置き換えるO(n)バケットソートです。あなたのアプローチはバケット構造のために追加のスペースを必要としますが、比較ソートはその場で行うことができます。ソリューション(2)は時間O(n lg k)で実行されます。つまり、O(n)はすべての単語を反復処理し、O(lg k)は各単語をヒープに追加します。
stackoverflowuser2010 2014

4
最初の解決策はより多くのスペースを必要としますが、実際には時間的にO(n)であることを強調することが重要です。1:単語O(n)でキー設定されたハッシュ頻度。2:頻度ハッシュをトラバースし、頻度でキー設定された2番目のハッシュを作成します。これは、ハッシュをトラバースするためのO(n)と、その頻度で単語のリストに単語を追加するためのO(1)です。3:kに達するまで、ハッシュを最大頻度から下にトラバースします。せいぜいO(n)。合計= 3 * O(n)= O(n)。
BringMyCakeBack 2014年

3
通常、単語を数えるとき、ソリューション1のバケット数は大幅に過大評価されているため(最も頻度の高い単語の数が2番目と3番目の単語よりもはるかに多いため)、配列はまばらで非効率的です。
Nikana Reklawyks 2015

k(頻繁な単語の数)が最も頻繁な単語の出現数(この場合は100)よりも少ない場合、ソリューション#1は機能しません。もちろん、実際には発生しない可能性がありますが、1つは必要です。想定しないでください!
One Two Three

@ OneTwoThree提案されたソリューションは、単なる例です。数は需要に基づいています。
Chihung Yu 2016

22

あなたが説明した解決策よりも一般的に良いランタイムを得るつもりはありません。すべての単語を評価するには、少なくともO(n)の作業を行う必要があります。次に、上位kの用語を見つけるためにO(k)の追加作業を行う必要があります。

問題セットが非常に大きい場合は、map / reduceなどの分散ソリューションを使用できます。n個のマップワーカーにそれぞれテキストの1 / nの頻度をカウントさせ、単語ごとに、単語のハッシュに基づいて計算されたm個のレデューサーワーカーの1つに送信します。次に、レデューサーはカウントを合計します。レデューサーの出力をマージソートすると、人気の高い順に最も人気のある単語が表示されます。


13

ソリューションのわずかな変化により、上位Kのランク付けを気にしない場合はO(n)アルゴリズムが生成され、ランク付けする場合はO(n + k * lg(k))ソリューションが生成されます。これらの範囲は両方とも一定の係数内で最適であると私は信じています。

ここでの最適化は、リストを実行してハッシュテーブルに挿入した後に再び行われます。中央値の中央値アルゴリズムを使用して、リスト内でK番目に大きい要素を選択できます。このアルゴリズムはおそらくO(n)です。

K番目に小さい要素を選択した後、クイックソートの場合と同様に、その要素の周りにリストを分割します。これも明らかにO(n)です。ピボットの「左側」にあるものはすべてK要素のグループに含まれているので、完了です(他のすべてを破棄するだけで済みます)。

したがって、この戦略は次のとおりです。

  1. 各単語を調べて、ハッシュテーブルに挿入します:O(n)
  2. K番目に小さい要素を選択します:O(n)
  3. その要素の周りのパーティション:O(n)

K個の要素をランク付けする場合は、効率的な比較ソートを使用してO(k * lg(k))時間でソートするだけで、合計実行時間はO(n + k * lg(k))になります。

各単語を少なくとも1回調べる必要があるため、O(n)の時間制限は一定の係数内で最適です。

K * lg(k)時間未満でk個の要素をソートする比較ベースの方法がないため、O(n + k * lg(k))の時間制限も最適です。


K番目に小さい要素を選択すると、選択されるのはK番目に小さいハッシュキーです。ステップ3の左パーティション内のKの言葉を正確に存在している必要はない
プラカシュムラリは

2
スワップを実行するため、ハッシュテーブルで「中央値の中央値」を実行することはできません。ハッシュテーブルから一時配列にデータをコピーする必要があります。したがって、O(n)ストレージが必要になります。
user674669 2013

O(n)でK番目に小さい要素をどのように選択できるかわかりませんか?
Michael Ho Chum 2015年

O(n)でK番目に小さい要素を見つけるためのアルゴリズムについてはこれをチェックしてください-wikiwand.com/en/Median_of_medians
Piyush

ハッシュテーブル+最小ヒープを使用しても、複雑さは同じです。最適化が見られません。
Vinay 2016

8

「ビッグワードリスト」が十分に大きい場合は、単純にサンプリングして見積もりを取得できます。そうでなければ、私はハッシュ集約が好きです。

編集

サンプルとは、ページのサブセットを選択し、それらのページで最も頻繁に使用される単語を計算することを意味します。合理的な方法でページを選択し、統計的に有意なサンプルを選択した場合、最も頻繁に使用される単語の見積もりは合理的であるはずです。

このアプローチは、データが多すぎてすべてを処理するのがばかげている場合にのみ、実際に合理的です。メガ数が少ない場合は、見積もりを計算するのではなく、データを分解して正確な答えを計算することができます。


たとえば、Webサイトごと、または主題ごとに頻繁に使用される単語のリストを取得しようとしている場合など、これを何度も繰り返す必要がある場合があります。その場合、「汗をかくことなく」は実際にはそれをカットしません。それでも、可能な限り効率的にそれを行う方法を見つける必要があります。
itsadok 2009

1
関係のない複雑さの問題に対処しない実用的な答えの場合は+1。@itsadok:実行ごとに:十分な大きさの場合は、サンプリングします; そうでない場合は、対数係数を取得することは関係ありません。
Nikana Reklawyks 2015

2

単語の最初の文字を使用して分割し、次にk個の単一単語セットができるまで次の文字を使用して最大の複数単語セットを分割することにより、時間をさらに短縮できます。リーフに部分的/完全な単語のリストがある一種の256ウェイツリーを使用します。どこにでも文字列のコピーが発生しないように十分に注意する必要があります。

このアルゴリズムはO(m)です。ここで、mは文字数です。これにより、kへの依存が回避されます。これは大きなkの場合に非常に便利です[投稿された実行時間が間違っているため、O(n * lg(k))である必要がありますが、それが何であるかはわかりません。 m]。

両方のアルゴリズムを並べて実行すると、漸近的に最適なO(min(m、n * lg(k)))アルゴリズムであると確信できますが、関与しないため、平均して高速になるはずです。ハッシュまたはソート。


7
あなたが説明していることは「トライ」と呼ばれます。
ニックジョンソン

こんにちはStrilanc。パーティション分割のプロセスについて詳しく説明していただけますか?
モーガンチェン

1
これはどのようにソートを含まないのですか?トライを取得したら、最も頻度の高いk個の単語をどのように抽出しますか。意味がありません
通常の

2

説明にバグがあります。カウントにはO(n)時間がかかりますが、並べ替えにはO(m * lg(m))がかかります。ここで、mは一意の単語の数です。これは通常、単語の総数よりもはるかに少ないため、ハッシュの構築方法を最適化する必要があります。



2

あなたが求めているのが、実用的なkと自然な言語のテキストで最も頻繁に使用されるk個の単語のリストである場合、アルゴリズムの複雑さは関係ありません。

ただ、サンプル任意のアルゴリズムを使用していることを、たとえば、あなたのテキストから数百万の単語、プロセス、ほんの数秒で、最も頻繁にカウント非常に正確になります。

ちなみに、ダミーアルゴリズム(1.すべてをカウント2.カウントをソート3.最善を尽くす)の複雑さはO(n + m * log(m))です。ここで、mはあなたの異なる単語の数です。テキスト。log(m)は(n / m)よりもはるかに小さいため、O(n)のままです。

実際には、長いステップが重要です。


2
  1. メモリ効率の高いデータ構造を利用して単語を保存します
  2. MaxHeapを使用して、上位K個の頻繁な単語を検索します。

これがコードです

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;

import com.nadeem.app.dsa.adt.Trie;
import com.nadeem.app.dsa.adt.Trie.TrieEntry;
import com.nadeem.app.dsa.adt.impl.TrieImpl;

public class TopKFrequentItems {

private int maxSize;

private Trie trie = new TrieImpl();
private PriorityQueue<TrieEntry> maxHeap;

public TopKFrequentItems(int k) {
    this.maxSize = k;
    this.maxHeap = new PriorityQueue<TrieEntry>(k, maxHeapComparator());
}

private Comparator<TrieEntry> maxHeapComparator() {
    return new Comparator<TrieEntry>() {
        @Override
        public int compare(TrieEntry o1, TrieEntry o2) {
            return o1.frequency - o2.frequency;
        }           
    };
}

public void add(String word) {
    this.trie.insert(word);
}

public List<TopK> getItems() {

    for (TrieEntry trieEntry : this.trie.getAll()) {
        if (this.maxHeap.size() < this.maxSize) {
            this.maxHeap.add(trieEntry);
        } else if (this.maxHeap.peek().frequency < trieEntry.frequency) {
            this.maxHeap.remove();
            this.maxHeap.add(trieEntry);
        }
    }
    List<TopK> result = new ArrayList<TopK>();
    for (TrieEntry entry : this.maxHeap) {
        result.add(new TopK(entry));
    }       
    return result;
}

public static class TopK {
    public String item;
    public int frequency;

    public TopK(String item, int frequency) {
        this.item = item;
        this.frequency = frequency;
    }
    public TopK(TrieEntry entry) {
        this(entry.word, entry.frequency);
    }
    @Override
    public String toString() {
        return String.format("TopK [item=%s, frequency=%s]", item, frequency);
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + frequency;
        result = prime * result + ((item == null) ? 0 : item.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        TopK other = (TopK) obj;
        if (frequency != other.frequency)
            return false;
        if (item == null) {
            if (other.item != null)
                return false;
        } else if (!item.equals(other.item))
            return false;
        return true;
    }

}   

}

これがユニットテストです

@Test
public void test() {
    TopKFrequentItems stream = new TopKFrequentItems(2);

    stream.add("hell");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("hero");
    stream.add("hero");
    stream.add("hero");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("home");
    stream.add("go");
    stream.add("go");
    assertThat(stream.getItems()).hasSize(2).contains(new TopK("hero", 3), new TopK("hello", 8));
}

詳細については、このテストケースを参照してください


1
  1. ハッシュテーブルを使用して、単語シーケンス全体をトラバースしながら、すべての単語の頻度を記録します。このフェーズでは、キーは「単語」であり、値は「単語頻度」です。これにはO(n)時間がかかります。これは上記で説明したすべてのものと同じです。

  2. ハッシュマップにそれ自体を挿入する間、上位10の頻繁な単語を保持するために、サイズ10(k = 10)のTreeset(javaに固有、すべての言語で実装があります)を保持します。サイズが10未満になるまで、追加し続けます。サイズが10に等しい場合、挿入された要素が最小要素、つまり最初の要素より大きい場合。はいの場合はそれを削除し、新しい要素を挿入します

ツリーセットのサイズを制限するには、このリンクを参照してください


0

単語シーケンス "ad" "ad" "boy" "big" "bad" "com" "come" "cold"があるとします。そしてK = 2。「単語の最初の文字を使用したパーティション分割」とおっしゃったように、( "ad"、 "ad")( "boy"、 "big"、 "bad")( "com" "come" "cold") "then k個のシングルワードセットができるまで、次の文字を使用して最大のマルチワードセットを分割します。」パーティション化( "boy"、 "big"、 "bad")( "com" "come" "cold")、最初のパーティション( "ad"、 "ad")は失われますが、 "ad"は実際には最も頻繁な単語。

おそらく私はあなたの主張を誤解しています。パーティションについてのプロセスを詳しく教えてください。


0

この問題はO(n)アルゴリズムで解決できると思います。その場で仕分けができました。言い換えると、その場合の並べ替えは、ハッシュテーブルにアクセスするたびに1つのカウンターだけが増分されるため、従来の並べ替え問題のサブ問題です。すべてのカウンターがゼロであるため、最初はリストがソートされます。ハッシュテーブルでカウンターをインクリメントし続けると、次のように頻度順に並べられたハッシュ値の別の配列をブックキープします。カウンターをインクリメントするたびに、ランク付けされた配列でそのインデックスをチェックし、そのカウントがリスト内の前のカウンターを超えているかどうかをチェックします。その場合、これら2つの要素を交換します。そのため、最大でO(n)の解が得られます。ここで、nは元のテキストの単語数です。


これは一般的に良い方向ですが、欠点があります。カウントが増えると、「その前身」をチェックするだけでなく、「前任者」をチェックする必要があります。たとえば、配列が[4,3,1,1,1,1,1,1,1,1,1]になる可能性が高く、1は同じ数になる可能性があり、効率が低下します。交換する適切なものを見つけるために、すべての前任者を振り返る必要があるためです。
ショーン

これは実際、O(n)よりもはるかに悪いのではないでしょうか。本質的にかなり非効率的なソートであるため、O(n ^ 2)に似ていますか?
dcarr622 2013年

こんにちはショーン。はい、あなたに賛成です。しかし、あなたが言及した問題は問題の根本的なものだと思います。実際、ソートされた値の配列だけを保持するのではなく、(値、インデックス)ペアの配列を保持することができれば、インデックスは繰り返される要素の最初の出現を指し、問題はOで解決できるはずです。 (n)時間。たとえば、[4,3,1,1,1,1,1,1,1,1,1]は[(4,0)、(3,1)、(1,2)、(1 、2)、(1,2、...、(1,2)];インデックスは0から始まる
アリーFarahat

0

私もこれに苦労していて、@ alyに触発されました。後で並べ替える代わりに、事前に並べ替えられた単語のリスト(List<Set<String>>)を維持するだけで、単語は位置Xのセットに含まれます。Xは単語の現在の数です。一般的に、それがどのように機能するかは次のとおりです。

  1. 単語ごとに、その出現のマップの一部として保存しますMap<String, Integer>
  2. 次に、カウントに基づいて、前のカウントセットから削除し、新しいカウントセットに追加します。

これの欠点は、リストがおそらく大きいことです-を使用して最適化できますTreeMap<Integer, Set<String>>-しかし、これはいくらかのオーバーヘッドを追加します。最終的には、HashMapまたは独自のデータ構造を組み合わせて使用​​できます。

コード

public class WordFrequencyCounter {
    private static final int WORD_SEPARATOR_MAX = 32; // UNICODE 0000-001F: control chars
    Map<String, MutableCounter> counters = new HashMap<String, MutableCounter>();
    List<Set<String>> reverseCounters = new ArrayList<Set<String>>();

    private static class MutableCounter {
        int i = 1;
    }

    public List<String> countMostFrequentWords(String text, int max) {
        int lastPosition = 0;
        int length = text.length();
        for (int i = 0; i < length; i++) {
            char c = text.charAt(i);
            if (c <= WORD_SEPARATOR_MAX) {
                if (i != lastPosition) {
                    String word = text.substring(lastPosition, i);
                    MutableCounter counter = counters.get(word);
                    if (counter == null) {
                        counter = new MutableCounter();
                        counters.put(word, counter);
                    } else {
                        Set<String> strings = reverseCounters.get(counter.i);
                        strings.remove(word);
                        counter.i ++;
                    }
                    addToReverseLookup(counter.i, word);
                }
                lastPosition = i + 1;
            }
        }

        List<String> ret = new ArrayList<String>();
        int count = 0;
        for (int i = reverseCounters.size() - 1; i >= 0; i--) {
            Set<String> strings = reverseCounters.get(i);
            for (String s : strings) {
                ret.add(s);
                System.out.print(s + ":" + i);
                count++;
                if (count == max) break;
            }
            if (count == max) break;
        }
        return ret;
    }

    private void addToReverseLookup(int count, String word) {
        while (count >= reverseCounters.size()) {
            reverseCounters.add(new HashSet<String>());
        }
        Set<String> strings = reverseCounters.get(count);
        strings.add(word);
    }

}

0

私はこの問題の他の解決策を見つけました。しかし、それが正しいかどうかはわかりません。解決:

  1. ハッシュテーブルを使用して、すべての単語の頻度を記録しますT(n)= O(n)
  2. ハッシュテーブルの最初のk個の要素を選択し、それらを1つのバッファー(スペース= k)に復元します。T(n)= O(k)
  3. 毎回、最初にバッファの現在のmin要素を見つけ、バッファのmin要素をハッシュテーブルの(n --k)要素と1つずつ比較する必要があります。ハッシュテーブルの要素がバッファのこの最小要素よりも大きい場合は、現在のバッファの最小要素を削除し、ハッシュテーブルの要素を追加します。したがって、バッファ内の最小値を見つけるたびにT(n)= O(k)が必要であり、ハッシュテーブル全体をトラバースするにはT(n)= O(n --k)が必要です。したがって、このプロセスの全体の時間計算量はT(n)= O((nk)* k)です。
  4. ハッシュテーブル全体をトラバースした後、結果はこのバッファにあります。
  5. 全体の時間計算量:T(n)= O(n)+ O(k)+ O(kn-k ^ 2)= O(kn + n-k ^ 2 + k)。なぜなら、kは一般的にnよりも実際に小さいからです。したがって、このソリューションの場合、時間計算量はT(n)= O(kn)です。kが本当に小さいとき、それは線形時間です。正しいですか?よくわかりません。

0

この種の問題に取り組むために、特別なデータ構造を考えてみてください。この場合、文字列を特定の方法で格納するためのトライのような特別な種類のツリーで、非常に効率的です。または、単語を数えるなど、独自のソリューションを構築する2番目の方法。このTBのデータは英語であると思いますが、一般に約600,000語あるので、それらの単語のみを保存し、繰り返される文字列を数えることができます+このソリューションでは、一部の特殊文字を削除するために正規表現が必要になります。最初の解決策はより速くなるでしょう、私はかなり確信しています。

http://en.wikipedia.org/wiki/Trie



0

最も頻繁に使用される単語の出現を取得するための最も単純なコード。

 function strOccurence(str){
    var arr = str.split(" ");
    var length = arr.length,temp = {},max; 
    while(length--){
    if(temp[arr[length]] == undefined && arr[length].trim().length > 0)
    {
        temp[arr[length]] = 1;
    }
    else if(arr[length].trim().length > 0)
    {
        temp[arr[length]] = temp[arr[length]] + 1;

    }
}
    console.log(temp);
    var max = [];
    for(i in temp)
    {
        max[temp[i]] = i;
    }
    console.log(max[max.length])
   //if you want second highest
   console.log(max[max.length - 2])
}

0

このような状況では、Javaの組み込み機能を使用することをお勧めします。以来、それらはすでに十分にテストされ、安定しています。この問題では、HashMapデータ構造を使用して単語の繰り返しを見つけます。次に、結果をオブジェクトの配列にプッシュします。Arrays.sort()でオブジェクトを並べ替え、上位k個の単語とその繰り返しを出力します。

import java.io.*;
import java.lang.reflect.Array;
import java.util.*;

public class TopKWordsTextFile {

    static class SortObject implements Comparable<SortObject>{

        private String key;
        private int value;

        public SortObject(String key, int value) {
            super();
            this.key = key;
            this.value = value;
        }

        @Override
        public int compareTo(SortObject o) {
            //descending order
            return o.value - this.value;
        }
    }


    public static void main(String[] args) {
        HashMap<String,Integer> hm = new HashMap<>();
        int k = 1;
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("words.in")));

            String line;
            while ((line = br.readLine()) != null) {
                // process the line.
                //System.out.println(line);
                String[] tokens = line.split(" ");
                for(int i=0; i<tokens.length; i++){
                    if(hm.containsKey(tokens[i])){
                        //If the key already exists
                        Integer prev = hm.get(tokens[i]);
                        hm.put(tokens[i],prev+1);
                    }else{
                        //If the key doesn't exist
                        hm.put(tokens[i],1);
                    }
                }
            }
            //Close the input
            br.close();
            //Print all words with their repetitions. You can use 3 for printing top 3 words.
            k = hm.size();
            // Get a set of the entries
            Set set = hm.entrySet();
            // Get an iterator
            Iterator i = set.iterator();
            int index = 0;
            // Display elements
            SortObject[] objects = new SortObject[hm.size()];
            while(i.hasNext()) {
                Map.Entry e = (Map.Entry)i.next();
                //System.out.print("Key: "+e.getKey() + ": ");
                //System.out.println(" Value: "+e.getValue());
                String tempS = (String) e.getKey();
                int tempI = (int) e.getValue();
                objects[index] = new SortObject(tempS,tempI);
                index++;
            }
            System.out.println();
            //Sort the array
            Arrays.sort(objects);
            //Print top k
            for(int j=0; j<k; j++){
                System.out.println(objects[j].key+":"+objects[j].value);
            }


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

詳細については、https://github.com/m-vahidalizadeh/foundations/blob/master/src/algorithms/TopKWordsTextFile.javaにアクセスしてください。お役に立てば幸いです。


これは、質問でスケッチされたアプローチをどのように改善しますか?(にしてくださいません(。SEに提示したコードからのコメントを出したまま)I recommend to use Java built-in featuresのようなforeach文処理ストリーム?)
老い

ご存知のように、効率的なアルゴリズムを設計する上で最も重要な要素の1つは、適切なデータ構造を選択することです。次に、問題にどのように取り組むかが重要です。たとえば、分割統治法で問題を攻撃する必要があります。あなたは貪欲によって別のものを攻撃する必要があります。ご存知のように、オラクル社はJavaに取り組んでいます。彼らは世界で最高のテクノロジー企業の1つです。Javaの組み込み機能に取り組んでいる最も優秀なエンジニアが何人かいます。したがって、これらの機能は十分にテストされており、防弾です。私たちがそれらを利用することができれば、私の意見ではそれらを使用する方が良いです。
モハマド

0
**

C ++ 11上記の考えの実装

****

class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {

    unordered_map<int,int> map;
    for(int num : nums){
        map[num]++;
    }

    vector<int> res;
    // we use the priority queue, like the max-heap , we will keep (size-k) smallest elements in the queue
    // pair<first, second>: first is frequency,  second is number 
    priority_queue<pair<int,int>> pq; 
    for(auto it = map.begin(); it != map.end(); it++){
        pq.push(make_pair(it->second, it->first));

        // onece the size bigger than size-k, we will pop the value, which is the top k frequent element value 

        if(pq.size() > (int)map.size() - k){
            res.push_back(pq.top().second);
            pq.pop();
        }
    }
    return res;

}

};

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.