web-dev-qa-db-ja.com

ヒープソートの直感的な理解?

学校では、現在Javaでソートアルゴリズムを学習しています。宿題のためにヒープソートを取得しました。読書をしましたが、できる限り調べようとしましたが、コンセプトが理解できません。

Javaプログラムです。ヒープソートの仕組みをできるだけ簡単に説明していただければ、私に書いてもらえません。

42
Rok Novosel

そう、基本的にはヒープを取得し、ヒープ内の最初のノードを引き出します。最初のノードは、ソートの方向に応じて最大/最小であることが保証されているためです。トリッキーなことは、最初にヒープの再バランス/作成を行うことです。

ヒーププロセスを理解するには、2つの手順が必要でした。まず、これをツリーと考え、頭を回してから、そのツリーを配列にして、役立つようにしました。

その2番目の部分は、基本的にまずツリーの幅をトラバースし、左から右に各要素を配列に追加することです。したがって、次のツリー:

                                    73                          
                                 7      12          
                               2   4  9   10    
                             1          

{73,7,12,2,4,9,10,1}になります

最初の部分には2つのステップが必要です。

  1. 各ノードに2つの子があることを確認します(上のツリーのように、それを行うのに十分なノードがない場合を除きます)。
  2. 各ノードがその子よりも大きい(または最初にソートする場合は小さい)ことを確認してください。

したがって、数値のリストをヒープ化するには、それぞれをヒープに追加してから、これらの2つのステップを順番に実行します。

上記のヒープを作成するには、最初に10を追加します。これが唯一のノードなので、何もする必要はありません。左側の子として12を追加します。

    10
  12

これは1を満たしますが、2を満たさないので、それらを交換します:

    12
  10

7を追加-何もする

    12
  10  7

追加73

          12
       10     7
    73

10 <73なので、それらを交換する必要があります。

          12
       73     7
    10

12 <73なので、それらを交換する必要があります。

          73
       12     7
    10

追加2-何もする

          73
       12     7
    10   2

追加4-何もする

          73
       12     7
    10   2  4

9を追加

          73
       12     7
    10   2  4   9

7 <9-スワップ

          73
       12     9
    10   2  4   7

1を追加-何もする

          73
       12     9
    10   2  4   7
  1

ヒープがあります:D

次に、各要素を上から削除し、最後の要素を毎回ツリーの最上部に入れ替えてから、ツリーのバランスを再調整します。

73を外す-その場所に1を置く

          1
       12     9
    10   2  4   7

1 <12-それらを交換する

          12
        1    9
    10   2  4   7

1 <10-それらを交換する

          12
       10     9
     1   2  4   7

12オフ-7に交換

          7
       10     9
     1   2  4   

7 <10-それらを交換する

          10
       7     9
     1   2  4   

10オフ-4に置き換え

          4
       7     9
    1   2  

4 <7-スワップ

          7
       4     9
    1   2  

7 <9-スワップ

          9
       4     7
    1   2 

9を外して-2に置き換える

          2
       4     7
    1   

2 <4-それらを交換する

          4
       2     7
    1  

4 <7-それらを交換する

          7
       2     4
    1  

7を外す-1に置き換える

          1
       2     4

1 <4-それらを交換する

          4
       2     1

テイク4-1に置き換え

          1
       2

1 <2-それらを交換する

          2
       1

テイク2-1に置き換え

          1

テイク1

ソートされたリストの出来上がり。

120
Matt Fellows

ヒープソートを考える1つの方法は、選択ソートの巧妙に最適化されたバージョンとしてです。選択ソートでは、ソートは、まだ正しく配置されていない最大の要素を繰り返し見つけ、それを配列の次の正しい位置に配置することによって機能します。ただし、選択ソートは時間O(n2)それは束から最大の要素をnラウンド探して(そして見るべき最大n個の異なる要素がある可能性があります)、それを所定の場所に配置する必要があるためです。

直感的に、ヒープの並べ替えは、配置されていない配列要素から最大の要素の検索を高速化する バイナリヒープ と呼ばれる特別なデータ構造を構築することで機能します。バイナリヒープは次の操作をサポートします。

  • Insert、要素をヒープに挿入し、
  • Delete-Maxは、ヒープの最大の要素を削除して返します。

非常に高いレベルでは、アルゴリズムは次のように機能します。

  • Insert配列の各要素を新しいバイナリヒープに挿入します。
  • I = nから1:まで
    • ヒープでDelete-Maxを呼び出して、ヒープの最大の要素を取得します。
    • この要素をi番目の位置に書き込みます。

Delete-Maxによって返される要素は降順であるため、これは配列を並べ替えます。すべての要素が削除されると、配列がソートされます。

ヒープのソートは、InsertおよびDelete-Max操作で効率的です。ヒープは両方ともO(log n)時間で実行されます。つまり、O(n log n)時間でヒープに対してn回の挿入と削除を実行できます。 より正確な分析 を使用して、実際には、入力配列に関係なくΘ(n log n)時間かかることを示すことができます。

通常、ヒープソートは2つの主要な最適化を採用しています。最初に、ヒープは通常、配列自体をヒープの圧縮表現として扱うことによって 配列の内部に組み込まれます です。ヒープソートの実装を見ると、通常、2による乗算と除算に基づく配列インデックスの異常な使用法が見られます。これらのアクセスは、配列を圧縮データ構造として扱っているため機能します。その結果、アルゴリズムはO(1)補助記憶域のみを必要とします。

次に、ヒープは一度に1つの要素を構築するのではなく、通常、構築されます 特殊なアルゴリズムを使用して 時間内に実行されるΘ(n)で、ヒープをインプレースで構築します。興味深いことに、コードを再利用できるため、コードが読みやすくなる場合もありますが、アルゴリズム自体を理解して分析するのが少し難しくなります。

項ヒープ でヒープソートが行われることが時々あります。これには、平均でわずかに高速であるという利点がありますが、何を表示しているかを知らずにこれを使用したヒープソート実装を見つけた場合、かなり読みにくい場合があります。他のアルゴリズムも同じ一般的な構造を使用しますが、より複雑なヒープ構造を使用します。 Smoothsort ははるかに複雑なヒープを使用してO(n)ベストケースの動作を維持しながらO(1)スペース使用量を維持しますおよびO(n log n)の最悪の場合の動作 Poplar sort はsmoothsortに似ていますが、O(log n)のスペース使用量とわずかに優れたパフォーマンスを備えています。 ヒープソートバリアントとしての挿入ソートと選択ソート

ヒープソートをよく理解したら、クイックソート、ヒープソート、挿入ソートを組み合わせた introsort アルゴリズムを調べて、クイックソートの強さを組み合わせた非常に高速なソートアルゴリズム(高速平均ソート)、ヒープソート(最悪の場合の動作)、挿入ソート(小さな配列の場合は高速ソート)。 Introsortは、C++のstd::sort関数の多くの実装で使用されているもので、ヒープソートが機能していれば、実装するのはそれほど難しくありません。

お役に立てれば!

31
templatetypedef

数値のリストを保存し、O(lg n)時間内の最小のデータ構造を取得および削除できる特別なデータ構造(ヒープと呼ばれる)があるとします。

これが非常に単純なソートアルゴリズムにどのようにつながるか、わかりますか?

難しい部分(実際にはそれほど難しくありません)は、ヒープの実装です。

2
tskuzzy

おそらく、インタラクティブトレースはアルゴリズムをよりよく理解するのに役立ちます。これが demo です。

1
tenorsax

私がこれにどのように答えるかを見ていきます。これは、ヒープの並べ替えとヒープとは何かについての説明が少しなるからです...

...ええと、ひどい

とにかく、まず、ヒープが何であるかを確認することをお勧めします。

Wikipedia から取得したように、ヒープは次のとおりです。

コンピュータサイエンスでは、ヒープは、ヒーププロパティを満たす特別なツリーベースのデータ構造です。BがAの子ノードである場合、key(A)≥key(B)です。これは、最大のキーを持つ要素が常にルートノードにあることを意味します。そのため、そのようなヒープは最大ヒープと呼ばれることもあります。 (または、比較を逆にすると、最小要素は常にルートノードにあるため、最小ヒープになります。)

ほとんどの場合、ヒープはバイナリツリーであり、ノードのすべての子はそのノードよりも小さくなります。

現在、ヒープソートはO(n lg(n))ソートアルゴリズムです。あなたはそれについて少し読むことができます ここここ 。これは、ソートしようとしているもののすべての要素をヒープに入れ、次にソートされた配列を最大の要素から最小の要素に構築することでほぼ機能します。ヒープの再構築を続けます。最大の要素は常にヒープの先頭(ルート)にあるため、その要素を取得し、並べ替えられた配列の後ろに配置するだけで済みます。 (つまり、ソートされた配列を逆に構築します)

なぜこのアルゴリズムはO(n lg(n))なのですか?ヒープ上のすべての操作はO(lg(n))であり、結果としてn演算、その結果、実行時間の合計はO(n lg(n))になります。

私のひどい暴言があなたを助けてくれたことを願っています!それは少し冗長です。申し訳ありません...

1
blahman

ヒープのソートには、時間の複雑さO(nlogn)およびスペースの複雑さO(1)

 public class HeapSort {

public static void main(String[] args) {
     Integer [] a={12,32,33,8,54,34,35,26,43,88,45};

     HeapS(a,a.length-1);

    System.out.println(Arrays.asList(a));

}

private static void HeapS(Integer[] a, int l) {


    if(l<=0)
        return;

    for (int i = l/2-1; i >=0 ; i--) {

        int index=a[2*i+1]>a[2*i+2]?2*i+1:2*i+2;
        if(a[index]>a[i]){
            int temp=a[index];
            a[index]=a[i];
            a[i]=temp;
        }

    }
    int temp=a[l];
    a[l]=a[0];
    a[0]=temp;

    HeapS(a,l-1);

  }
}
0
brahmananda Kar

ヒープソートアルゴリズムは砂利のヒープのように機能することを教えてくれたアルゴリズム分析の教授を覚えています。

砂利で満たされた袋があり、床でそれを空にしたとします。大きな石は下に転がり、小さい石(または砂)は上に留まります。

これで、ヒープの最上部を取得し、ヒープの最小値で保存します。残りのヒープを再び袋に入れて、繰り返します。 (または、反対のアプローチを取り、床で転がっているのを見た最大の石をつかむことができます。例はまだ有効です)

それは多かれ少なかれ、ヒープソートがどのように機能するかを説明するために私が知っている単純な方法です。

0
STT LCU