[データ構造 - ハンドソート パート 2] この記事では、ヒルソートについて詳しく説明します。

目次

1. 一般的な並べ替えアルゴリズム

1.1 挿入ソートの基本的な考え方

2. ヒルソート

2.1 ヒルソート(縮小増分ソート)

2.1.1 選別前の段階

2.1.2 挿入ソートフェーズ

2.2 One-way Hill ソート

2.2.1 アイデア分析

2.2.2 コードの実装

3. ヒルソートコードの実装

4. ヒルソート時間の複雑さ

5. ヒルソートとインサートソートの効率比較

6. ヒルソート特性の概要


1. 一般的な並べ替えアルゴリズム

1.1 挿入ソートの基本的な考え方

直接挿入ソートは単純な挿入ソート方法です。その基本的な考え方は、
キー値のサイズに従ってソートされたシーケンスに、ソート対象のレコードを 1 つずつ挿入し、すべてのレコードが挿入されるまで続きます
。順序付けられたシーケンスが得られます
実際にポーカーをプレイするときは、挿入ソートの考え方を使用します。

2. ヒルソート

2.1 ヒルソート(縮小増分ソート)

ヒル ソート法は、縮小増分法としても知られています。Hill ソート法の基本的な考え方は、まず整数ギャップを選択し、ソートするファイル内のすべてのレコードを複数のグループに分割し、ギャップの距離差があるすべてのレコードを同じグループにグループ化し、ソートを実行します。 。次に、gap=gap/3+1 として、上記のグループ化と並べ替えの作業を繰り返します。ギャップ = 1 に達すると、すべてのレコードが同じグループにソートされます。

分析するための絵を描いてみましょう:私たちは昇順でここにいます

この図から、最後のギャップ = 1 の場合、本質は挿入ソートであり、ヒル ソートは最初に事前ソートを行い、最後にソートを実現するために 1 回ソートを挿入することであることが実際にわかります

上の図を分析してみましょう。

2.1.1 選別前の段階

1. 最初のトリップで、ギャップ = 5 の場合、次の場合、5 つの数字の間隔ごとにグループが形成され、9 と 4、1 と 8、2 と 6、5 と 3、7 と 5 がグループを形成することを意味します。番号が前の番号より小さい場合は、グループ内の小さい番号を前に、大きい番号を後ろに配置するように交換します。

2. 2 番目のトリップでは、gap = gap/3+1 とします (ここで、gap/3+1 は最終的に gap=1 を取得することです。gap<3 の場合、単純に 3 で割ると 0 が得られるため、完了します)ソートはできません)、gap=2、2 つの数字の間隔ごとにグループ、4 は 2、5、8、5 のグループ、1 は 3、9、6、7 のグループ、サイズを比較、次に交換します。

2.1.2 挿入ソートフェーズ

3. ギャップ = 1 を調整する場合、本質は挿入ソートです。この時点では、前のソート前段階を経て、数値は順序に近くなりますが、完全に順序は整っていません。このときの挿入ソートは効率を大幅に向上させます。

2.2 One-way Hill ソート

2.2.1 アイデア分析

シングルパス ヒル ソートは実際には、ギャップで区切られた数値をグループに分割し、最初にソートします。

図面分析:

1 回の旅行の間隔ごとにギャップのある数値をグループに分け、最初に並べ替えます。

2.2.2 コードの実装

for (int i = 0; i < n - gap; i++)//多组一起排序(没有提升效率,只是少写一组循环)
{
    int end = i;
    int tmp = a[i + gap];
    while (end >= 0)
    {
        if (a[end] > tmp)
        {
            a[end + gap] = a[end];
            end -= gap;
        }
        else
        {
            break;
        }
    }
    a[end + gap] = tmp;
}

3. ヒルソートコードの実装

シングルパスソートにループの層を追加し、ギャップが 1 になるまで継続的に減少させ、ヒルソートを実現します。

void ShellSort(int* a, int n)
{
	//1. 当 gap > 1 时先进性预排序
	//2. 当 gap == 1 时直接插入排序
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;// +1可以保证最后一次一定是1
		for (int i = 0; i < n - gap; i++)//多组一块进行
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

4. ヒルソート時間の複雑さ

ヒル ソートの時間計算量は、ギャップを評価する方法が多数あり、計算が困難であるため、計算が容易ではありません。そのため、多くの書籍で示されているヒル ソートの時間計算量は固定されていません。

ヒル ソートの時間計算量について説明した 2 冊の本を次に示します。

ここでのギャップは、Knuth が提案した方法に従って計算されており、Knuth は数多くの実験統計を行っていますが、当面は O(n^1.25) ~ O(1.6*n^1.25) に従って計算します。 。

5. ヒルソートとインサートソートの効率比較

どちらもサイズが 100,000 の 2 つの配列を開き、Hill ソートと挿入ソートを使用して、消費時間を比較します。

//时间对比
void TestOp()
{
	srand((unsigned int)time(NULL));
	const int n = 100000;
	int* a1 = (int*)malloc(sizeof(int) * n);
	int* a2 = (int*)malloc(sizeof(int) * n);

	for (int i = 0; i < n; ++i)
	{
		a1[i] = rand();
		a2[i] = a1[i];
	}

	int begin1 = clock();
	InsertSort(a1, n);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a2, n);
	int end2 = clock();


	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);

	free(a1);
	free(a2);
}

int main()
{
	TestOp();
	return 0;
}

ヒル ソートは挿入ソートよりもはるかに効率的であり、データ量が大きい場合はヒル ソートの方が優勢であることがわかります。

ただし、配列自体が順序付けされている場合、ヒル ソートには事前ソートがあり、実行に時間がかかるため、ヒル ソートは挿入ソートほど優れていません。

6. ヒルソート特性の概要

1. ヒル ソートは直接挿入ソートを最適化したものです。
2. ギャップ > 1 の場合、事前にソートされます。目的は、配列を順序に近づけることです。ギャップ == 1 の場合、配列はすでに順序に近いため、非常に高速になります。これにより、全体の最適化効果が得られる。実装した後は、パフォーマンス テストを比較できます。
3. ヒル ソートの時間計算量は、ギャップを評価する方法が多数あり、計算が困難であるため、計算が容易ではないため、多くの書籍で示されているヒル ソートの時間計算量は固定されていません。

4. 安定性:不安定です。

おすすめ

転載: blog.csdn.net/Ljy_cx_21_4_3/article/details/131597842