目次
2.2 残りの N - K 要素を先頭の要素と順番に比較し、大きい場合は置き換えます。
TopK の質問の導入:
King of Glory をプレイしているとき、誰もが xxx 市の xxx 番のヒーローと xxx 地区の xxx 番のヒーローに遭遇したことがあります。または、今日テイクアウトを注文するときに特定の食べ物を食べたい場合は、Meituan/Ele.me を開いて、最も近いオプションまたは最高スコアのオプションを選択すると、選択した店舗の上位 x 件の名前が並べ替えられます。順番通りに発送します。フォーブス誌のトップ10、フルンの長者番付のトップ5など。これらの問題では、大量のデータを並べ替えて最大の上位 K 個のデータを選択する必要があります。ここでは、TopK アルゴリズムを使用してこの種の問題を解決します。
1. Top-K 問題とは何ですか?
TOP-K 問題: データの組み合わせから上位 K 個の最大要素または最小要素を見つけます。一般に、データ量は比較的多くなります。
例: トップ 10 のプロ プレーヤー、世界トップ 500、リッチ リスト、ゲームのアクティブ プレーヤー トップ 100 など。
Top-K 問題の場合、考えられる最も単純かつ直接的な方法は並べ替えです。ただし、データ量が非常に大きい場合、並べ替えはお勧めできません (すべてのデータをメモリに読み込むことができない場合があります)。一度)。最善の方法は、ヒープを使用して問題を解決することです。基本的な考え方は次のとおりです。
1.1 Top-Kの基本的な考え方
(1) データセットの最初の K 要素を使用してヒープを構築します。
最初の k 個の最大要素の場合は小さなヒープを構築し、
最初の k 個の最小要素の場合は大きなヒープを構築します。
(2) 残りの NK 要素を使用して、先頭の要素と順番に比較します (例として最大の K を見つけるためにここにいます)。小さいヒープを構築しているため、ヒープの先頭の要素はこの小さいヒープの中で最小になります。残りの NK 要素は、最初にヒープの先頭と比較され、それがヒープの先頭の要素より大きい場合は、ヒープの先頭の要素を置き換え、下方に調整して小さなヒープを再構築します。これはヒープの最上位の要素より小さいため、置き換えずに次の要素を使用します。ヒープの最上位と比較して、残りの NK 要素が順番に比較され、このステップが繰り返されます。
(3) 残りの NK 要素をヒープの最上位要素と順番に比較した後、ヒープ内の残りの K 要素が、探索された最初の K 個の最小または最大要素になります。
2. Top-K 問題の論理分析
(1) まず最初の K を使用して小さなヒープを構築します。
(2) 残りの N - K 要素を先頭の要素と順番に比較し、大きい場合は置き換えます。
(3) プリントヒープ。
これが私たちの大きなロジックです。これら 3 つのステップを段階的に分析していきます。
2.1 ヒープ、サイズ K の小さなヒープを構築する
プロセス:
1. まずサイズ k の空間を開きます。
2. 最初の K データを下方向に調整して小さな山を形成します。(下方調整の方法がわからない方は、ここをクリックして確認してください)
コードは以下のように表示されます。
int* kminheap = (int*)malloc(sizeof(int) * k);
if (NULL == kminheap)
{
perror("malloc fail:");
return;
}
for (int i = 0; i < k; i++)
{
fscanf(fout, "%d", &kminheap[i]);
}
//建小堆
for (int i = (k - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(kminheap, k, i);
}
2.2 残りの N - K 要素を先頭の要素と順番に比較し、大きい場合は置き換えます。
プロセス:
1. 上位 K 個の最大データであるため、小さなヒープを構築します。小さなヒープの最上位要素はヒープ内の最小要素であり、残りの NK 要素がヒープの最上位と順番に比較されます。
2. この要素がヒープの最上位よりも大きい場合は、ヒープの最上位の要素を置き換えます。それより小さい場合は、交換せず、次の要素に順番に移動して比較します。
3. 交換する場合は、ヒープの先頭から開始して下方に調整してヒープを再構築すると、ヒープの先頭が再び最小の要素になります。
4. NK 要素が順番に比較されると、ヒープ内の K 個の要素が最初に見つかった最大の K 個の要素になります。
コードは以下のように表示されます。
int val = 0;
while (!feof(fout))
{
fscanf(fout, "%d", &val);
if (val > kminheap[0])
{
kminheap[0] = val;
AdjustDown(kminheap, k, 0);
}
}
2.3 印刷スタック
for (int i = 0; i < k; i++)
{
printf("%d ", kminheap[i]);
}
3. TopK 実装コード
void PrintTopK(int k)
{
const char* file = "data.txt";
FILE* fout = fopen(file, "r");
if (NULL == fout)
{
perror("fopen error:");
return;
}
int* kminheap = (int*)malloc(sizeof(int) * k);
if (NULL == kminheap)
{
perror("malloc fail:");
return;
}
for (int i = 0; i < k; i++)
{
fscanf(fout, "%d", &kminheap[i]);
}
//建小堆
for (int i = (k - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(kminheap, k, i);
}
int val = 0;
while (!feof(fout))
{
fscanf(fout, "%d", &val);
if (val > kminheap[0])
{
kminheap[0] = val;
AdjustDown(kminheap, k, 0);
}
}
for (int i = 0; i < k; i++)
{
printf("%d ", kminheap[i]);
}
printf("\n");
}
ここのコードはファイルからデータを読み取り、準備されたデータをファイルに保存します。
4. Top-K 問題の完全なコード
まず 1000 個の番号を作成し、その番号をファイルに保存し、次に Top-K を検索するときにファイルからこれらの番号を取得します。
void CreateNData()
{
//造数据
int n = 1000;
srand((unsigned int)time(NULL));
const char* file = "data.txt";
FILE* fin = fopen(file, "w");
if (NULL == fin)
{
perror("fopen error:");
return;
}
for (size_t i = 0; i < n; i++)
{
int x = rand() % 100000;
fprintf(fin, "%d\n", x);
}
fclose(fin);
}
void PrintTopK(int k)
{
const char* file = "data.txt";
FILE* fout = fopen(file, "r");
if (NULL == fout)
{
perror("fopen error:");
return;
}
int* kminheap = (int*)malloc(sizeof(int) * k);
if (NULL == kminheap)
{
perror("malloc fail:");
return;
}
for (int i = 0; i < k; i++)
{
fscanf(fout, "%d", &kminheap[i]);
}
//建小堆
for (int i = (k - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(kminheap, k, i);
}
int val = 0;
while (!feof(fout))
{
fscanf(fout, "%d", &val);
if (val > kminheap[0])
{
kminheap[0] = val;
AdjustDown(kminheap, k, 0);
}
}
for (int i = 0; i < k; i++)
{
printf("%d ", kminheap[i]);
}
printf("\n");
}
結果の表示:
簡単な検証手法: ここのファイルに記述しました。コードが正しく記述されているかどうかを迅速に検証するために、データを生成するインターフェイスを呼び出してコメントアウトし、data.txt ファイルを入力して、上位 5 つを変更します。データを保存してから印刷に進むと、すぐに確認できるようになります。
2 つの写真を比較し、上位 5 つの最大値を出力します。
*** 記事の終わり ***