ヒープソート は、以下に基づく比較ベースの並べ替え手法です。 バイナリ ヒープ データ構造。それは、 選択ソート ここで、最初に最小要素を見つけて、その最小要素を先頭に配置します。残りの要素についても同じプロセスを繰り返します。
Javaバブルソート
ヒープソートアルゴリズム
この問題を解決するには、次のアイデアに従ってください。
推奨問題 解決策に進む前に、まず PRACTICE で解いてください。まず heapify を使用して配列をヒープ データ構造に変換し、次に Max ヒープのルート ノードを 1 つずつ削除し、ヒープ内の最後のノードに置き換えてから、ヒープのルートをヒープ化します。ヒープのサイズが 1 より大きくなるまで、このプロセスを繰り返します。
- 指定された入力配列からヒープを構築します。
- ヒープに要素が 1 つだけ含まれるまで、次の手順を繰り返します。
- ヒープのルート要素 (最大の要素) をヒープの最後の要素と交換します。
- ヒープの最後の要素 (現在は正しい位置にあります) を削除します。
- ヒープの残りの要素をヒープ化します。
- ソートされた配列は、入力配列内の要素の順序を逆にすることによって取得されます。
ヒープソートの詳細な動作
ヒープ ソートをより明確に理解するために、ソートされていない配列を取得し、ヒープ ソートを使用してソートしてみます。
配列 arr[] = {4, 10, 3, 5, 1} について考えてみましょう。完全なバイナリ ツリーを構築する: 配列から完全なバイナリ ツリーを構築します。
ヒープソートアルゴリズム |完全なバイナリ ツリーを構築する
最大ヒープに変換します。 その後、ソートされていない配列からツリーを構築し、それを次の形式に変換することがタスクになります。 最大ヒープ。
- ヒープを最大ヒープに変換するには、親ノードが常に子ノード以上である必要があります。
- ここで、この例では親ノードとして 4 子ノードより小さい 10、 したがって、それらを交換して最大ヒープを構築します。
- 今、 4 親は子供より小さいので 5 したがって、これらの両方を再度交換すると、結果のヒープと配列は次のようになります。
ヒープソートアルゴリズム | Max Heapify で構築されたバイナリ ツリー
ヒープソートを実行します。 各ステップで最大要素を削除し (つまり、要素を終了位置に移動して削除し)、残りの要素を考慮して最大ヒープに変換します。
- ルート要素を削除する (10) 最大ヒープから。このノードを削除するには、最後のノードと交換してみてください。 (1)。 ルート要素を削除した後、再度ヒープ化して最大ヒープに変換します。
- 結果のヒープと配列は次のようになります。
ヒープソートアルゴリズム |ルートから最大値を削除し、最大ヒープ化します
- 上記の手順を繰り返すと、次のようになります。
ヒープソートアルゴリズム | root と max heapify から次の最大値を削除します
CSSの背景
- 次に、ルート (つまり 3) を再度削除し、heapify を実行します。
ヒープソートアルゴリズム |前の手順を繰り返します
- ここで、ルートをもう一度削除すると、ソートされます。ソートされた配列は次のようになります arr[] = {1、3、4、5、10} 。
ヒープソートアルゴリズム |最終的にソートされた配列
ヒープソートの実装
C++ // C++ program for implementation of Heap Sort #include using namespace std; // To heapify a subtree rooted with node i // which is an index in arr[]. // n is size of heap void heapify(int arr[], int N, int i) { // Initialize largest as root int largest = i; // left = 2*i + 1 int l = 2 * i + 1; // right = 2*i + 2 int r = 2 * i + 2; // If left child is larger than root if (l < N && arr[l]>arr[最大]) 最大 = l; // 右の子が最大の子より大きい場合 // これまでのところ if (r< N && arr[r]>arr[最大]) 最大 = r; // 最大値がルートでない場合 if (largest != i) { swap(arr[i], arr[largest]); // 影響を受けるサブツリーを再帰的にヒープ化します。 // heapify(arr, N, biggest); } } // ヒープソートを行うメイン関数 void heapSort(int arr[], int N) { // ヒープを構築 (配列を並べ替え) for (int i = N / 2 - 1; i>= 0; i--) heapify(arr, N, i); // ヒープから要素を 1 つずつ抽出します。 // for (int i = N - 1; i> 0; i--) { // 現在のルートを末尾に移動します swap(arr[0], arr[i]); // 削減されたヒープで max heapify を呼び出します heapify(arr, i, 0); } } // サイズ n の配列を出力するユーティリティ関数 void printArray(int arr[], int N) { for (int i = 0; i< N; ++i) cout << arr[i] << ' '; cout << '
'; } // Driver's code int main() { int arr[] = { 12, 11, 13, 5, 6, 7 }; int N = sizeof(arr) / sizeof(arr[0]); // Function call heapSort(arr, N); cout << 'Sorted array is
'; printArray(arr, N); }> C // Heap Sort in C #include // Function to swap the position of two elements void swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } // To heapify a subtree rooted with node i // which is an index in arr[]. // n is size of heap void heapify(int arr[], int N, int i) { // Find largest among root, // left child and right child // Initialize largest as root int largest = i; // left = 2*i + 1 int left = 2 * i + 1; // right = 2*i + 2 int right = 2 * i + 2; // If left child is larger than root if (left < N && arr[left]>arr[最大]) 最大 = 左; // 右の子が最大の子より大きい場合 // これまでのところ if (右< N && arr[right]>arr[最大]) 最大 = 右; // スワップしてヒープ化を続行 // ルートが最大でない場合 // 最大がルートではない場合 if (largest != i) { swap(&arr[i], &arr[largest]); // 影響を受けるサブツリーを再帰的にヒープ化します。 // heapify(arr, N, biggest); } } // ヒープソートを行うメイン関数 void heapSort(int arr[], int N) { // (int i = N / 2 - 1; i>= 0; i--) の最大ヒープを構築 heapify(arr) 、N、i); // ヒープソート for (int i = N - 1; i>= 0; i--) { swap(&arr[0], &arr[i]); // ルート要素をヒープ化 // ルートで最上位の要素を取得します // heapify(arr, i, 0); } } // サイズ n の配列を出力するユーティリティ関数 void printArray(int arr[], int N) { for (int i = 0; i< N; i++) printf('%d ', arr[i]); printf('
'); } // Driver's code int main() { int arr[] = { 12, 11, 13, 5, 6, 7 }; int N = sizeof(arr) / sizeof(arr[0]); // Function call heapSort(arr, N); printf('Sorted array is
'); printArray(arr, N); } // This code is contributed by _i_plus_plus_.> ジャワ // Java program for implementation of Heap Sort public class HeapSort { public void sort(int arr[]) { int N = arr.length; // Build heap (rearrange array) for (int i = N / 2 - 1; i>= 0; i--) heapify(arr, N, i); // ヒープから要素を 1 つずつ抽出します for (int i = N - 1; i> 0; i--) { // 現在のルートを末尾に移動します int temp = arr[0]; arr[0] = arr[i]; arr[i] = 温度; // 削減されたヒープで max heapify を呼び出します heapify(arr, i, 0); } } // arr[] のインデックスである // ノード i をルートとするサブツリーをヒープ化します。 n はヒープのサイズです。 void heapify(int arr[], int N, int i) { int biggest = i; // 最大値を root int l = 2 * i + 1 として初期化します。 // left = 2*i + 1 int r = 2 * i + 2; // right = 2*i + 2 // 左の子がルートより大きい場合 if (l< N && arr[l]>arr[最大]) 最大 = l; // 右の子がこれまでの最大の子よりも大きい場合 if (r< N && arr[r]>arr[最大]) 最大 = r; // 最大値がルートでない場合 if (largest != i) { int swap = arr[i]; arr[i] = arr[最大]; arr[最大] = スワップ; // 影響を受けるサブツリーを再帰的にヒープ化します heapify(arr, N, biggest); } } /* サイズ n の配列を出力するユーティリティ関数 */ static void printArray(int arr[]) { int N = arr.length; for (int i = 0; i< N; ++i) System.out.print(arr[i] + ' '); System.out.println(); } // Driver's code public static void main(String args[]) { int arr[] = { 12, 11, 13, 5, 6, 7 }; int N = arr.length; // Function call HeapSort ob = new HeapSort(); ob.sort(arr); System.out.println('Sorted array is'); printArray(arr); } }> C# // C# program for implementation of Heap Sort using System; public class HeapSort { public void sort(int[] arr) { int N = arr.Length; // Build heap (rearrange array) for (int i = N / 2 - 1; i>= 0; i--) heapify(arr, N, i); // ヒープから要素を 1 つずつ抽出します for (int i = N - 1; i> 0; i--) { // 現在のルートを末尾に移動します int temp = arr[0]; arr[0] = arr[i]; arr[i] = 温度; // 削減されたヒープで max heapify を呼び出します heapify(arr, i, 0); } } // arr[] のインデックスである // ノード i をルートとするサブツリーをヒープ化します。 n はヒープのサイズです。 void heapify(int[] arr, int N, int i) { int biggest = i; // 最大値を root int l = 2 * i + 1 として初期化します。 // left = 2*i + 1 int r = 2 * i + 2; // right = 2*i + 2 // 左の子がルートより大きい場合 if (l< N && arr[l]>arr[最大]) 最大 = l; // 右の子がこれまでの最大の子よりも大きい場合 if (r< N && arr[r]>arr[最大]) 最大 = r; // 最大値がルートでない場合 if (largest != i) { int swap = arr[i]; arr[i] = arr[最大]; arr[最大] = スワップ; // 影響を受けるサブツリーを再帰的にヒープ化します heapify(arr, N, biggest); } } /* サイズ n の配列を出力するユーティリティ関数 */ static void printArray(int[] arr) { int N = arr.Length; for (int i = 0; i< N; ++i) Console.Write(arr[i] + ' '); Console.Read(); } // Driver's code public static void Main() { int[] arr = { 12, 11, 13, 5, 6, 7 }; int N = arr.Length; // Function call HeapSort ob = new HeapSort(); ob.sort(arr); Console.WriteLine('Sorted array is'); printArray(arr); } } // This code is contributed // by Akanksha Rai(Abby_akku)> JavaScript // JavaScript program for implementation // of Heap Sort function sort( arr) { var N = arr.length; // Build heap (rearrange array) for (var i = Math.floor(N / 2) - 1; i>= 0; i--) heapify(arr, N, i); // ヒープから要素を 1 つずつ抽出します for (var i = N - 1; i> 0; i--) { // 現在のルートを末尾に移動します var temp = arr[0]; arr[0] = arr[i]; arr[i] = 温度; // 削減されたヒープで max heapify を呼び出します heapify(arr, i, 0); } } // arr[] のインデックスである // ノード i をルートとするサブツリーをヒープ化します。 n はヒープのサイズです。 関数 heapify(arr, N, i) { var biggest = i; // 最大値をルートとして初期化します var l = 2 * i + 1; // left = 2*i + 1 var r = 2 * i + 2; // right = 2*i + 2 // 左の子がルートより大きい場合 if (l< N && arr[l]>arr[最大]) 最大 = l; // 右の子がこれまでの最大の子よりも大きい場合 if (r< N && arr[r]>arr[最大]) 最大 = r; // 最大値が root でない場合 if (largest != i) { var swap = arr[i]; arr[i] = arr[最大]; arr[最大] = スワップ; // 影響を受けるサブツリーを再帰的にヒープ化します heapify(arr, N, biggest); } } /* サイズ n の配列を出力するユーティリティ関数 */ function printArray(arr) { var N = arr.length; for (var i = 0; i< N; ++i) document.write(arr[i] + ' '); } var arr = [12, 11, 13, 5, 6, 7]; var N = arr.length; sort(arr); document.write( 'Sorted array is'); printArray(arr, N); // This code is contributed by SoumikMondal> PHP // Php program for implementation of Heap Sort // To heapify a subtree rooted with node i which is // an index in arr[]. n is size of heap function heapify(&$arr, $N, $i) { $largest = $i; // Initialize largest as root $l = 2*$i + 1; // left = 2*i + 1 $r = 2*$i + 2; // right = 2*i + 2 // If left child is larger than root if ($l < $N && $arr[$l]>$arr[$largest]) $largest = $l; // 右の子がこれまでの最大の子よりも大きい場合 if ($r< $N && $arr[$r]>$arr[$largest]) $largest = $r; // 最大値が root でない場合 if ($largest != $i) { $swap = $arr[$i]; $arr[$i] = $arr[$largest]; $arr[$largest] = $swap; // 影響を受けるサブツリーを再帰的にヒープ化します heapify($arr, $N, $largest); } } // ヒープソートを実行するメイン関数 function heapSort(&$arr, $N) { // ヒープを構築 (配列を並べ替え) for ($i = $N / 2 - 1; $i>= 0; $i- -) heapify($arr, $N, $i); // ヒープから要素を 1 つずつ抽出します for ($i = $N-1; $i> 0; $i--) { // 現在のルートを末尾に移動 $temp = $arr[0]; $arr[0] = $arr[$i]; $arr[$i] = $temp; // 削減されたヒープで max heapify を呼び出します heapify($arr, $i, 0); } } /* サイズ n の配列を出力するユーティリティ関数 */ function printArray(&$arr, $N) { for ($i = 0; $i< $N; ++$i) echo ($arr[$i].' ') ; } // Driver's program $arr = array(12, 11, 13, 5, 6, 7); $N = sizeof($arr)/sizeof($arr[0]); // Function call heapSort($arr, $N); echo 'Sorted array is ' . '
'; printArray($arr , $N); // This code is contributed by Shivi_Aggarwal ?>>> Python3 # Python program for implementation of heap Sort # To heapify subtree rooted at index i. # n is size of heap def heapify(arr, N, i): largest = i # Initialize largest as root l = 2 * i + 1 # left = 2*i + 1 r = 2 * i + 2 # right = 2*i + 2 # See if left child of root exists and is # greater than root if l < N and arr[largest] < arr[l]: largest = l # See if right child of root exists and is # greater than root if r < N and arr[largest] < arr[r]: largest = r # Change root, if needed if largest != i: arr[i], arr[largest] = arr[largest], arr[i] # swap # Heapify the root. heapify(arr, N, largest) # The main function to sort an array of given size def heapSort(arr): N = len(arr) # Build a maxheap. for i in range(N//2 - 1, -1, -1): heapify(arr, N, i) # One by one extract elements for i in range(N-1, 0, -1): arr[i], arr[0] = arr[0], arr[i] # swap heapify(arr, i, 0) # Driver's code if __name__ == '__main__': arr = [12, 11, 13, 5, 6, 7] # Function call heapSort(arr) N = len(arr) print('Sorted array is') for i in range(N): print('%d' % arr[i], end=' ') # This code is contributed by Mohit Kumra> 出力
Sorted array is 5 6 7 11 12 13>
の複雑さの分析 ヒープソート
時間計算量: O(N log N)
補助スペース: O(log n)、再帰呼び出しスタックによる。ただし、反復実装では補助空間を O(1) にすることができます。
ヒープ ソートに関する重要な点:
- ヒープ ソートはインプレース アルゴリズムです。
- その典型的な実装は安定していませんが、安定させることができます (「 これ )
- 通常、適切に実装されたものより 2 ~ 3 倍遅い クイックソート 。速度が遅い理由は、参照の局所性が欠如していることです。
ヒープソートの利点:
- 効率的な時間計算量: ヒープ ソートの時間計算量は、すべての場合で O(n log n) です。これにより、大規模なデータセットを効率的に並べ替えることができます。の ログn この係数はバイナリ ヒープの高さによって決まり、多数の要素がある場合でもアルゴリズムが良好なパフォーマンスを維持することが保証されます。
- メモリ使用量 - メモリ使用量を最小限に抑えることができます (再帰的な heapify() の代わりに反復的な heapify() を作成することによって)。したがって、並べ替える項目の最初のリストを保持するために必要なものを除けば、動作するために追加のメモリ領域は必要ありません。
- シンプルさ – 再帰などの高度なコンピューター サイエンスの概念を使用しないため、他の同様に効率的な並べ替えアルゴリズムよりも理解しやすいです。
ヒープソートの欠点:
- 高価な : 時間計算量が両方とも O(n Log n) であっても、ヒープ ソートはマージ ソートに比べて定数が大きいためコストがかかります。
- 不安定 : ヒープソートが不安定です。相対的な順序が並べ替えられる可能性があります。
- 効率的: ヒープ ソートは、非常に複雑なデータを扱う場合にはあまり効率的ではありません。
ヒープソートに関するよくある質問
Q1.ヒープ ソートの 2 つのフェーズとは何ですか?
ヒープ ソート アルゴリズムは 2 つのフェーズで構成されます。最初のフェーズでは、配列が最大ヒープに変換されます。そして 2 番目のフェーズでは、最上位の要素 (つまり、ツリーのルートにある要素) が削除され、残りの要素を使用して新しい最大ヒープが作成されます。
Q2.ヒープソートが安定していないのはなぜですか?
カット・ティンフの妹
heapSort() で arr[i] と arr[0] を交換するため、同等のキーの相対的な順序が変更される可能性があるため、ヒープ ソート アルゴリズムは安定したアルゴリズムではありません。
Q3.ヒープ ソートは分割統治アルゴリズムの一例ですか?
ヒープソートは ない まったく、分割統治アルゴリズムです。要素を並べ替えるための分割統治アプローチではなく、ヒープ データ構造を使用して要素を効率的に並べ替えます。
Q4.ヒープ ソートとマージ ソートのどちらのソート アルゴリズムが優れていますか?
答えは、時間の複雑さとスペース要件の比較にあります。マージ ソートはヒープ ソートよりわずかに高速です。しかしその一方で、マージソートは余分なメモリを必要とします。要件に応じて、どちらを使用するかを選択する必要があります。
Q5.ヒープ ソートが選択ソートよりも優れているのはなぜですか?
ヒープ ソートは選択ソートに似ていますが、最大の要素を取得するためのより良い方法が異なります。ヒープデータ構造を利用して、一定時間内に最大の要素を取得します。
Javaの挿入ソート