ソートとヒープソートを選択
1.選択ソート
1.原則
選択とソートは比較的簡単です。つまり、各トラバース後に、無秩序領域の最大(最小)値が無秩序領域の最初または最後の値と交換されます。このようにして、無秩序領域は徐々に順序付けられ、最後に並べ替えを完了します。その時間計算量もO(n 2)O(n ^ 2)です。O (n2)
2.プロセス
3.コード
int selectsort(int arry[], int len)
{
int min, i, j, temp;
for(i = 0; i < len - 1; ++i)
{
min = i;
for (j = i; j < len; ++j)
{
if (arry[j] < arry[min])
{
min = j;
}
}
temp = arry[i];
arry[i] = arry[min];
arry[min] = temp;
}
}
2.ヒープソート
1.ヒープの概念
シーケンスk1、k 2、⋅⋅⋅、kn(k_1、k_2、\ cdot \ cdot \ cdot、k_n)を想定します。k1、k2、⋅⋅⋅ 、kn個次の関係が満たされる場合にのみ、ヒープと呼ばれます。
{ki≤k2i、ki≤k2i + 1または{ki≥k2i、ki≥k2i + 1ここで、(i = 1、2、⋅⋅⋅、n 2)\ begin {cases}k_i≤k_{2i}、\\k_i≤k_{2i + 1} \ end {cases}または\ begin {cases}k_i≥k_{2i} 、\\k_i≥k_{2i + 1} \ end {cases} where(i = 1,2、\ cdot \ cdot \ cdot、\ frac {n} {2}){{
k私≤k2私、k私≤k2 I + 1または{{
k私≥k2私、k私≥k2 I + 1これは(I=1 、2 、⋅⋅⋅ 、2n個)。
シーケンス番号は1から始まることに注意してください。
前者は関係以下であるため、これを呼びます。小根堆
後者は関係以上であるため、次のようにも呼びます。大根堆
2.ヒープと完全な二分木の関係
次に、このシーケンス(配列)を介して完全な二分木をシミュレートすると、ツリー構造の下で、ヒープには次のプロパティがあります。
各親ノードはその息子ノードよりも大きい(小さい)。図を参照してください。
この小さなルートがあるとします。ヒープ:
それを完全な二分木に「変換」するには:
ノードnの子ノードが2nと2n +1であることを確認するのは難しくありません。
3.ヒープソートのプロセス
このヒープバイナリツリーのルートノード(シーケンシャルシーケンスでシミュレート)はである必要が最大(小)
あり、要素は順序付けられた状態である必要があることがわかりました。次に、ソートのアイデアは次のようになります。
- ヒープを構築する
- ルートノード要素を除外します(一番上の要素を除外するか、最後の要素と交換して除外します)
- 残りの要素が山を形成するように構造を再調整します
- すべての要素が整うまで2と3を繰り返します
もちろん、2番目のポイントでは、基本的なツリー構造が破壊されないようにするために、トップ要素とエンド要素交换
をパイルトップ要素の後に配置し、エンド要素を有序区
、、つまり、除外、およびに分類することを選択します。ポイント3の構造の再調整には参加しないでください。
したがって、これに基づいて、通常は昇順大根堆
と降順を使用します小根堆
。
複雑な導出の後、その時間計算量はO(nlog 2 n)O(nlog_2n)です。O (n l o g2n )
4.ヒープを構築します
ヒープを初期化するには、次の手順が必要です。
すべての親ノードで次の调整堆
操作を実行します。
- 子ノードがすべて親ノードよりも小さい場合、それは終了します。
- 非1の場合:親ノードと子ノードの最大値が交換され、交換後に子ノードで1と2の操作が実行されます。
葉でいっぱいの完全な二分木を仮定し、それがn個のノードを持っていると仮定すると、葉のノードの数は(n + 1)2 \ frac {(n + 1)} {2}であることがわかっています。2(N + 1 )、次に、親ノードは(n − 1)2 \ frac((n-1))(2)を持ちます2(N - 1 )一つは
その後、我々は最後の親ノードから開始し、ループ内でヒープを構築するために(下からトップに、右から左へ)前進、我々はヒープを初期化するとします。
コードの観点から:
// 从最后一个父结点开始,将所有结点给调整一次。
for (i = len/2; i > 0; --i)
{
HeapAdjust(heapArry, i, len);
}
5.ヒープを調整します
ヒープと考えを調整し、次のように述べました。
- 子ノードがすべて親ノードよりも小さい場合、それは終了します。
- 非1の場合:親ノードと子ノードの最大値が交換され、交換後に子ノードで1と2の操作が実行されます。
コードは次のように表示されます。
int HeapAdjust(int arry[], int index, int len)
{
int i;
arry[0] = arry[index]; // arry[0] 没被使用,刚好可以拿来当交换时的临时变量用
for (i = 2*index; i <= len; i*=2) // 从该结点的左孩子开始,且每次循环都直接到孩子的孩子,且i肯定不能超过树的大小
{
if (i < len && arry[i] < arry[i+1]) // 这里是判断i当前孩子是左孩子大还是右孩子大,将i指向最大的孩子
{
++i;
}
// 把上者<改成>,下者>=改为<=,则该堆排序变成降序
if (arry[0] >= arry[i]) // 如果我们的处理的结点大于了孩子结点,那么就必须交换。
{
break;
}
else
{
arry[index] = arry[i]; // 我们处理的结点被孩子顶替
index = i; // 我们新处理的结点变成了被顶替的孩子位置
}
}
arry[index] = arry[0]; // 一直到了最后,孩子一直顶替,直到没法顶替了,就是我们最开始待处理结点的位置
}
凡例:
ルートノードのみが調整されていない場合、index = 1でノードを調整する必要があります:
最初に入力して要素のステータスを決定します:5が調整され、7が息子への現在のポイントです:
これで時間、[0] <[i]、つまり5 <7。調整が必要なため、[1]は[2]と等しくなり、調整する値は[1]から[1]に変更されます。 [2]で、先のとがった息子が[4]に変更されました]:
このとき、調整が必要なため、[0] <[i]、つまり5 <6であるため、[2]は[4]と等しくなります。 ]、次に調整対象の調整が[2]から[4]に変更され、の息子が[8]に変更された(存在しない)を指しています。
次に、ループを終了した後、調整する位置index = 4を決定したので、tempはarry [0]からarry [4]の値です。
これでヒープ調整は完了です。
6.コード
特定のプロセスについては、ヒープソートを途中で出力して、ヒープの並べ替えと構築のプロセスを確認できます。
#include <stdio.h>
#include <stdlib.h>
int HeapAdjust(int arry[], int index, int len)
{
int i;
arry[0] = arry[index]; // arry[0] 没被使用,刚好可以拿来当交换时的临时变量用
for (i = 2*index; i <= len; i*=2) // 从该结点的左孩子开始,且每次循环都直接到孩子的孩子,且i肯定不能超过树的大小
{
if (i < len && arry[i] < arry[i+1]) // 这里是判断i当前孩子是左孩子大还是右孩子大,将i指向最大的孩子
{
++i;
}
// 把上者<改成>,下者>=改为<=,则该堆排序变成降序
if (arry[0] >= arry[i]) // 如果我们的处理的结点大于了孩子结点,那么就必须交换。
{
break;
}
else
{
arry[index] = arry[i]; // 我们处理的结点被孩子顶替
index = i; // 我们新处理的结点变成了被顶替的孩子位置
}
}
arry[index] = arry[0]; // 一直到了最后,孩子一直顶替,直到没法顶替了,就是我们最开始待处理结点的位置
}
int HeapSort(int arry[], int len)
{
int i = 1;
int *heapArry = (int*)malloc(sizeof(int) * (len+1)); // 构造一个从下标1开始的序列。
for (i = 1; i <= len; ++i)
{
heapArry[i] = arry[i - 1];
}
// 从最后一个父结点开始,将所有结点给调整一次。
for (i = len/2; i > 0; --i)
{
HeapAdjust(heapArry, i, len);
}
// 堆排序。
for (i = len; i > 0; --i)
{
arry[i-1] = heapArry[1]; // 堆顶是我们的最大元素,赋值给原数组
heapArry[1] = heapArry[i]; // 因为是交换,所以要把最后一个元素给堆顶,堆顶给最后一个元素(有序),但我们可以舍弃这个保存,因为存到了老数组里
HeapAdjust(heapArry, 1, i - 1); // i之后的结点是有序的(尽管没有赋值),所以不参与堆的构造
}
}
int main()
{
int a[7] = {
5,6,3,7,2,1,4};
int i;
HeapSort(a, 7);
for (i = 0; i < 7; ++i)
{
printf("%d ", a[i]);
}
return 0;
}