内部ソート方法の図とコード
1.直接挿入ソート
直接挿入ソートは単純なソート方法です。具体的な方法は、i番目のレコードを挿入するとき、R1、R2、...、Ri-1はすでにソートされており、RiのキーワードKiの後にキーワードKiが続きます。 -1、Ki-2等を比較して挿入位置を求め、Riを挿入します。挿入位置以降のレコードが順番に後方に移動します。
void Insertsort(int data[],int n)
/*将数组data[0]~data[n-1]中的n个整数按非递减有序的方式进行排列*/
{
int i,j;
int tmp;
for(i=1;i<n;i++)
if(data[i]<data[i-1]){
tmp=data[i];
data[i]=data[i-1];
for(j=i-1;j>=0&&data[j]>tmp;j--)
data[j+1]=data[j];
data[j+1]=tmp;
}
}
2.バブルソート
nレコードのバブルソートの方法は、最初のレコードのキーを2番目のレコードのキーと比較し、逆順の場合、2つのレコードの値を交換してから、2番目のレコードを比較します。そして、3番目のレコードのキーワード、というように、n-1番目のレコードとn番目のレコードのキーワードの位置が比較されるまで続きます。上記のプロセスは最初のバブルソートであり、結果として、最大のキーを持つレコードがn番目のレコードの位置にスワップされます。その後、2回目のバブルソートを行い、最初のn-1個のレコードに対して同様の操作を行うと、キーが2番目に大きいレコードがn-1番目のレコードの位置に入れ替わります。最大でn-1回、すべてのレコードが順番に配置されます。バブルソーティング工程で隣接位置の要素交換処理がない場合は、ソーティング工程を終了することができる。
3.単純選択ソート
nレコードの単純な選択と並べ替えの基本的な方法は、2番目のキーワード間でni(1 <= i <= n)を比較して、n-i + 1レコードから最小のキーを持つレコードを選択することです。 i番目のレコードと交換します。iがnと等しい場合、すべてのレコードが順番に配置されます。
void SelectSort(int data[],int n)
/*将数组data中的n个整数按非递减有序的方式进行排序*/
{
int i,j,k,tmp;
for(i=0;i<n-1;i++){
k=i;
for(j=i+1;j<n;j++) /*找出最小关键字的下标*/
if(data[j]<data[k])
k=j;
if(k!=i){
tmp=data[i];
data[i]=data[k];
data[k]=tmp;
}
}
}
4.ヒルソート
ヒルソーティングは、直接挿入ソーティング方式の改良版である「インクリメンタルソーティングの削減」とも呼ばれます。
ヒルソートの基本的な考え方は、まず、ソートするレコードのシーケンス全体をいくつかのサブシーケンスに分割し、それぞれ直接挿入ソートを実行します。シーケンス全体のレコードが基本的に順序どおりである場合、すべてのレコードで直接挿入ソートを実行します。具体的な方法は、最初の増分としてnより小さい整数d1を最初に取り、ファイルのすべてのレコードをd1グループに分割します。つまり、距離がd1の倍数であるすべてのレコードを同じグループに入れ、各グループで続行します。並べ替えを直接挿入します。次に、2番目の増分d2(d2 <d1)を取得し、取得した増分di = 1(di <di-1 <... d2 <d1)になるまで、上記のグループ化と並べ替えの作業を繰り返します。 、つまり、すべてのレコードが同じグループに配置され、直接挿入ソートが行われます。
void ShellSort(int data[],int n)
{int *delta,k,i,t,dk,j;
k=n;
/*从k=n开始,重复k=k/2运算,直到k=1,所得k值的序列作为增量序列存入delta*/
i=0;
do{
k=k/2;
delta[i++]=k;
}while(k>1);
i=0;
while((dk=delta[i])>0){
for(k=delta[i];k<n;++k)
if(data[k]<data[k-dk]){ /*将元素data[k]插入到有序增量子表中*/
t=data[k]; /*备份待插入的元素,空出一个元素位置*/
for(j=k-dk;j>=0 && t<data[j];j-=dk)
data[j+dk]=data[j]; /*寻找插入位置的同时元素后移*/
data[j+dk]=t; /*找到插入位置,插入元素*/
}
++i; /*取下一个增量值*/
}
}
5.マージソート
所谓“归并”,是将两个或两个以上的有序文件合并成为一个新的有序文件。归并排序的一种实现方法是把一个有n个记录的无序文件看成是由n个长度为1的有序子文件组成的文件,然后进行两两归并,得到n/2个长度为2或1的有序文件,再两两归并,如此重复,直到最后形成包含n个记录的有序文件为止。这种反复将两个有序文件归并成一个有序文件的排序方法称为两路归并排序。
两路归并排序的核心操作是将一维数组中前后相邻的两个有序序列归并为一个有序序列。
void Merge(int data[],int s,int m,int n){
/*将分别有序的data[s..m]和data[m+1..n]归并为有序的data[s..n]*/
int i,start=s,k=0;
int *temp;
temp=(int *)malloc((n-s+1)*sizeof(int)); /*辅助空间*/
for(i=m+1;s<=m && i<=n;++k) /*将data[s..m]与data[m+1..n]归并后存入temp*/
if(data[s]<data[i])
temp[k]=data[s++];
else temp[k]=data[i++];
for(;s<=m;++k) /*将剩余的data[s..m]复制到temp*/
temp[k]=data[s++];
for(i=0;i<k;i++)
data[start++]=temp[i];
free(temp);
}
void MSort(int data[],int s,int t){ /*对data[s..t]进行归并排序*/
{
int m;
if(s<t){ /*将data[s..t]均分为data[s..m]和data[m+1..t]*/
m=(s+t)/2;
MSort(data,s,m); /*递归地对data[s..m]进行归并排序*/
MSort(data,m+1,t); /*递归地对data[m+1..t]进行归并排序*/
Merge(data,s,m,t); /*将data[s..m]和data[m+1..t]归并为data[s..t]*/
}
}
6. 快速排序
快速排序的基本思想是:通过一趟排序将待排的记录划分为独立的两部分,称为前半区和后半区,其中,前半区中记录的关键字均不大于后半区记录的关键字,然后再分别对这两部分记录继续进行快速排序,从而使整个序列有序。
一趟快速排序的过程称为一次划分,具体做法是:附设两个位置指示变量i和j,它们的初值分别指向序列的第一个记录和最后一个记录。设枢轴记录(通常是第一个记录)的关键字为pivot,则首先从j所指位置起向后搜索,找到第一个关键字小于pivot的记录时将该记录向后移到j所指位置,重复该过程直至i与j相等为止。
int partition(int data[],int low,int high)
/*用date[low]作为枢轴元素pivot进行划分*/
/*使得data[low..i-1]均不大于pivot,data[i+1..high]均不小于pivot*/
{int i,j;
int pivot;
pivot=data[low];
i=low; j=hight;
while(i<j){ /*从数组的两端交替向中间扫描*/
while(i<j && data[j]>=pivot)
j--;
data[i]=data[j]; /*比枢轴元素小者往前移*/
while(i<j && data[i]<=pivot)
i++;
data[j]=data[i]; /*比枢轴元素大者往后移*/
}
data[i]=pivot;
return i;
}
void quickSort(int data[],int low,int high)
/*用快速排序方法对数组元素data[low..high]作非递减排序*/
{
if(low<high){
int loc=partition(data,low,high) /*进行划分*/
quickSort(data,low,loc-1); /*对前半区进行快速排序*/
quickSort(data,loc+1,high); /*对后半区进行快速排序*/
}
}
7. 堆排序
ヒープのソートの考え方:キーワードのグループをソートするには、まずヒープの定義に従って(つまり、最初のヒープを確立して)キーワードをシーケンスに配置します。これにより、ヒープの一番大きなキー(大きなルートヒープの場合)が出力され、次に残りのキーワードは新しいパイルに調整され、次に大きいキーワードが取得され、すべてのキーワードが整然とした順序で配置されるまで続きます。
初期ヒープを確立する方法は、ソートするキーワードを完全なバイナリツリーのノードに配置することです(現時点では、完全なバイナリツリーは必ずしもヒープの特性を持っているとは限りません)。明らかに、すべてのi>(n / 2)ノードKiには子ノードがありません。このようなKiをルートとするサブツリーは既にヒープであるため、完全なバイナリツリーのi番目(i =(n / 2))のノードKiから初期ヒープを構築できます。 、K(n / 2)、K(n / 2)-1、K(n / 2)-2、...、K2、K1をルートとするサブツリーを徐々にヒープの定義に合わせます。
Kiをルートとするサブツリーのヒープを構築するプロセスでは、KiとK2i(またはK2i + 1)の値を交換する必要がある場合があります。その結果、K2i(またはK2i + 1)をルートとするサブツリーは、杭の定義は、ルートとしてK2i(またはK2i + 1)を使用して引き続き調整する必要があり、このように繰り返して、リーフノードまで拡張できます。このメソッドは、ふるいにかけ、最大(または最小)キーワードをレイヤーごとにフィルターで除外し、最後に最大(または最小)要素をヒープの最上部に出力するようなものです。
元のシーケンス
最初のヒープ(大きなルートヒープ)を構築する
void HeapAdjust(int data[],int s,int m)
/*在data[s..m]所构成的一个元素序列中,除了data[s]外,其余元素均满足大根堆的定义*/
/*调整元素data[s]的位置,使data[s..m]成为一个大根堆*/
{
int tmp,j;
tmp=data[s]; /*备份元素data[s],为其找到适当位置后再插入*/
for(j=2*s+1;j<=m;j=j*2+1){ /*沿值较大的孩子结点想下筛选*/
if(j<m && data[j]<data[j+1])
++j; /*j是值较大的元素的下标*/
if(tmp>=data[j])
break;
data[s]=data[j]; s=j; /*用s记录待插入元素的位置(下标)*/
}
data[s]=tmp; /*将备份元素插入由s所指出的插入位置*/
}
void HeapSort(int data[],int n)
/*数组data[0..n-1]中的n个元素进行堆排序*/
{
int i; int tmp;
for(i=n/2;i>=0;--i) /*将data[0..n-1]调整为大根堆*/
HeapAdjust(data,i,n-1);
for(i=n-1;j>0;--i)
{
tmp=data[0]; data[0]=data[i];
data[i]=tmp; /*堆顶元素data[0]与序列末的元素data[i]交换*/
HeapAdjust(data,0,i-1); /*待排元素的个数减一,将data[0..i-1]重新调整为大根堆*/
}
}
8.内部ソート方法の要約
選別方法 | 時間の複雑さ | 補助スペース | 安定 |
---|---|---|---|
直接挿入 | O(n²) | O(1) | 安定した |
簡単な選択 | O(n²) | O(1) | 不安定 |
バブルソート | O(n²) | O(1) | 安定した |
ヒルソート | O(n ^ 1.3) | O(1) | 不安定 |
クイックソート | O(nlogn) | ログインする | 不安定 |
ヒープソート | O(nlogn) | O(1) | 不安定 |
マージソート | O(nlogn) | オン) | 安定した |