目次
序文:
前回のブログでは、「ヒープ」の予備的な概念がすでにありました。その後、「ヒープ」を使用して日常生活の問題を解決できます。この記事では、よく使用される 2 つのアプリケーション シナリオをそれぞれ示します。 」と「Top-k 問題」。前回のブログは「ヒープ」のシミュレーション実装 - CSDN ブログです。
「ヒープソート」について:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void AdjustDown(int* arr, int sz, int parent)
{
int child = parent * 2 + 1;
while (child < sz)
{
if (child + 1 < sz && arr[child] < arr[child + 1])
{
child++;
}
if (arr[child] > arr[parent])
{
swap(&arr[child], &arr[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void AdjustUp(int* arr, int sz, int child)
{
while (child > 0)
{
int parent = (child - 1) / 2;
if (arr[parent] < arr[child])
{
swap(&arr[parent], &arr[child]);
}
child = parent;
}
}
int main()
{
int arr[] = { 2, 6, 9, 3, 1, 7 };
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = (sz - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(arr, sz, i);
}//向下调整算法
//for (int i = 1; i<sz; i++)
//{
// AdjustUp(arr, sz, i);
//}//向上调整算法
int end = sz - 1;
while (end > 0)
{
swap(&arr[0], &arr[end]);
AdjustDown(arr, end, 0);
--end;
}
return 0;
}
ステップ 1: 杭を構築する
「ヒープ」を使用すると、特定の順序が狂った配列のソートが容易になります。まず、ソート操作のために大きなヒープを選択する必要があります。
ヒープを構築するために小さなヒープを使用することを選択してみてはいかがでしょうか?
以前のブログの「ヒープ」の説明によると、ヒープが小さいとは、先頭の要素が最も小さい要素であり、他のノードの数が最初の要素よりも少ないことを意味するため、小さいヒープであれば、最小の数はすでに存在します。最初の要素。次に小さい要素を見つけたい場合は、残りの要素の間にヒープを構築し、このサイクルを繰り返して並べ替えを完了する必要があります。これには時間がかかり、並べ替えには役立ちません。
そこで、大規模なヒープを使用してヒープを構築することを選択し、大規模なヒープを実現した後、最初と最後の要素を交換し、残りの n-1 個の要素を下方調整法を使用して調整してから交換します。ソートを実現できます。
int arr[] = { 2, 6, 9, 3, 1, 7 };
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 1; i<sz; i++)
{
AdjustUp(arr, sz, i);
}
図に示すように配列を上方向に調整してヒープを構築します。
ステップ 2: 並べ替え
まず、最初と最後の要素を交換します。
最後の要素を除くすべての要素を下方に調整して、大きな山に続けます
上記の手順を繰り返します
最終的なヒープは次のとおりです。
これでヒープソートは完了です。
「トップK問題」
Top-k の質問について:
これは、データの組み合わせから最初の K 個の最大の要素または最小の要素を見つけることですが、一般にデータ量は比較的多くなります。
例: トップ 10 のプロフェッショナル、フォーチュン 500、リッチ リスト、ゲームのトップ 100 のアクティブ プレーヤーなど。n データ内の最初の K 個の最大要素を見つける例を見て説明しましょう: (n=10000 と仮定) (k=10 と仮定)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
const char* file = "data.txt";
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void AdjustDown(int* arr, int sz, int parent)
{
int child = 2 * parent + 1;
while (child < sz)
{
if (child + 1 < sz && arr[child + 1] < arr[child])
{
child++;
}
if (arr[child] < arr[parent])
{
swap(&arr[child], &arr[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void CreateFile()
{
//创建随机数的种子
srand((unsigned int)time(NULL));
FILE* Fin = fopen(file, "w");
if (Fin == NULL)
{
perror("Fopen error");
exit(-1);
}
int n = 10000000;
for (int i = 0; i < n; i++)
{
int x = (rand() + i) % n;
fprintf(Fin, "%d\n", x);
}
fclose(Fin);
Fin = NULL;
}
void Print()
{
FILE* Fout = fopen(file, "r");
if (Fout == NULL)
{
perror("Fout error");
exit(-1);
}
//取前k个数进小堆
int* minheap = (int*)malloc(sizeof(int) * 5);
if (minheap == NULL)
{
perror("minheap -> malloc");
return;
}
for (int i = 0; i < 5; i++)
{
fscanf(Fout, "%d", &minheap[i]);
}
for (int i = (5-1-1)/2; i >=0; --i)
{
AdjustDown(minheap, 5, i);
}
//读取数据
int x = 0;
while (fscanf(Fout, "%d", &x) != EOF)
{
if (minheap[0] < x)
{
minheap[0] = x;
}
AdjustDown(minheap, 5, 0);
}
for (int i = 0; i < 5; i++)
{
printf("%d ", minheap[i]);
}
fclose(Fout);
Fout = NULL;
}
int main()
{
//CreateFile();
Print();
return 0;
}
まず、10000000 個の乱数を作成し、次にその数値を変更し、ランダムに 5 つの数値を選択して、次のように変更します。
10000001、10000002、10000003、10000004、10000005
別の小さな山を構築します。これは小さな山でなければならないことに注意してください。
大きなヒープを構築する場合、最初にデータを検索して 10000005 が見つかった場合、その数値はヒープの先頭にある必要があります。次に小さい数値が見つかると、ヒープに入ることができないため、小さなヒープを使用します。 !
次に、データの最初の 5 要素を小さなヒープに置きます。
次に、残りの 9999995 個の数値を調べて比較し、それらの数値がヒープの最上位の要素より大きい場合は、直接置き換えます。
置換後、再度下方に調整し、データ全体を走査した後、ヒープを挿入します。
10000001、10000002、10000003、10000004、10000005