一般的に使用されるさまざまな並べ替えアルゴリズム
アルゴリズムの概要
アルゴリズムの分類
10 個の一般的な並べ替えアルゴリズムは、次の 2 つの大きなカテゴリに分類できます。
- 比較ソート: 要素の相対的な順序を比較によって決定し、時間計算量が O(nlogn) を超えることができないため、非線形時間比較ソートとも呼ばれます。
- 非比較ソート: 要素の相対的な順序は比較によって決定されません。比較ベースのソートの下限を突破し、線形時間で実行できるため、線形時間非比較ソートとも呼ばれます。
アルゴリズムの複雑さ
関連概念
- 安定: a が元々 b の前にあり、a=b の場合、ソート後も a は b の前にあります。
- 不安定: a が元々 b の前にあり、a=b の場合、ソート後に a が b の後ろに現れる可能性があります。
- 時間計算量: 並べ替えられたデータに対する操作の合計数。これは、n が変化するときの操作数の規則性を反映しています。
- 空間の複雑さ:コンピューター内のアルゴリズムを指します。
これは内部実行に必要な記憶領域の尺度であり、データ サイズ n の関数でもあります。
1. バブルソート
バブル ソートは単純な並べ替えアルゴリズムです。ソート対象の配列を繰り返し調べて、一度に 2 つの要素を比較し、順序が間違っている場合はそれらを交換します。シーケンスを訪問する作業は、交換する必要がなくなるまで、つまりシーケンスがソートされるまで繰り返されます。このアルゴリズムの名前は、小さな要素が交換によってシーケンスの先頭にゆっくりと「浮く」という事実に由来しています。
1.1 アルゴリズムの説明
- 隣接する要素を比較します。最初の値が 2 番目の値より大きい場合は、両方を交換します。
- 隣接する要素の各ペアに対して、最初の最初のペアから最後の最後のペアまで同じことを行い、最後の要素が最大の数値になるようにします。
- 最後の要素を除くすべての要素に対して上記の手順を繰り返します。
- 並べ替えが完了するまで手順 1 ~ 3 を繰り返します。
1.2 アニメーションのプレゼンテーション
1.3 コードの実装
C/C++ 実装:
//冒泡排序
void bubble_sort(int a[], int n) {//n为a[]的实际长度-1,例如a[4]={3,2,9,10} n为3
int i,j,tem;
for (i=0; i<n; i++)
for (j=0; j<n-i; j++) //如果左边大于右边则将他们调换位置
if (a[j] > a[j+1])tem=a[j],a[j]=a[j+1],a[j+1]=tem;
}
Java 実装:
//冒泡排序算法
private static void bubble_sort(int a[], int n) {//n为a[]的实际长度-1,例如a[4]={3,2,9,10} n为3
for (int i=0; i<n; i++)
for (int j=0; j<n-i; j++) //如果左边大于右边则将他们调换位置
if (a[j] > a[j+1]) {
int tem=a[j];a[j]=a[j+1];a[j+1]=tem;
}
}
2. 選択範囲の並べ替え
選択ソート (Selection-sort) は、シンプルで直感的なソート アルゴリズムです。仕組み: まず、ソートされていないシーケンス内で最小 (最大) の要素を見つけ、それをソートされたシーケンスの先頭に格納します。次に、引き続きソートされていない残りの要素から最小 (最大) の要素を見つけて、それをソート済みのシーケンスに置きます。シーケンスの終わり。すべての要素がソートされるまで続きます。
2.1 アルゴリズムの説明
n レコードの直接選択と並べ替えでは、n-1 個の直接選択と並べ替えを通じて順序付けされた結果を取得できます。具体的なアルゴリズムは次のように説明されます。
- 初期状態: 順序付けされていない領域は R[1...n] で、順序付けされた領域は空です。
- i 番目のソート (i=1,2,3...n-1) が開始されるとき、現在の順序付き領域と順序なし領域は R[1...i-1] と R(i...n) になります。それぞれ。このソート処理では、現在の非順序領域から最小のキーを持つレコード R[k] が選択され、それが非順序領域の最初のレコード R と交換され、R[1...i] と R[i+1. ..n) それぞれ、レコード数が 1 増加する新しい順序付けられた領域と、レコード数が 1 つ減少する新しい無秩序領域になります。
- n-1 回の移動が終了し、配列がソートされました。
2.2 アニメーションのプレゼンテーション
2.3 コードの実装
C/C++ 実装:
//选择排序
void selection_sort (int a[], int n) {//n为a[]的实际长度-1,例如a[4]={3,2,9,10} n为3
for(int i=0;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(a[i]>a[j]){int tem=a[i];a[i]=a[j];a[j]=tem;}
}
Java 実装:
//选择排序
public static void selection_sort (int a[], int n) {
//n为a[]的实际长度-1,例如a[4]={3,2,9,10} n为3
for(int i=0;i<=n;i++) //每次选出第i小的数
for(int j=i+1;j<=n;j++)//与后面没排序的依次比较 选出当前最小的数
if(a[i]>a[j]){
int tem=a[i];a[i]=a[j];a[j]=tem;}
}
2.4 アルゴリズム解析
最も安定した並べ替えアルゴリズムの 1 つ。どのようなデータが入力されても、時間計算量は O(n2) であるため、使用する場合はデータ サイズが小さいほど優れています。唯一の利点は、追加のメモリ領域を占有しないことです。理論的に言えば、選択ソートは、ほとんどの人がソートするときに通常考えるソート方法でもあります。
3. 挿入ソート
Insertion-Sort のアルゴリズムの説明は、シンプルで直感的な並べ替えアルゴリズムです。これは、順序付けられたシーケンスを構築することによって機能し、ソートされていないデータの場合は、ソートされたシーケンスの後ろから前にスキャンして、対応する位置を見つけて挿入します。
3.1 アルゴリズムの説明
一般に、挿入ソートはインプレースを使用して配列に実装されます。具体的なアルゴリズムは次のように説明されます。
- 最初の要素から始めて、要素はソートされていると見なされます。
- 次の要素を取り出し、ソートされた要素シーケンスを後ろから前にスキャンします。
- (ソートされた) 要素が新しい要素より大きい場合は、要素を次の位置に移動します。
- 並べ替えられた要素が新しい要素以下になる位置が見つかるまで、手順 3 を繰り返します。
- その位置に新しい要素を挿入した後。
- 手順2~5を繰り返します。
3.2 アニメーションのプレゼンテーション
3.2 コードの実装
C/C++の実装
//插入排序
void insertion_sort (int a[], int n) {
//n为a[]的实际长度-1,例如a[4]={3,2,9,10} n为3
int i,j,tem;//tem 中间临时变量
for (i=1; i<=n; i++) {
//当前i与前(i-1)个做比较如果当前i元素小于前(i-1)个,则第(i-1)个向挪一位
for (tem=a[i],j=i-1;j>=0&&tem<a[j];j--)a[j+1]=a[j];
a[j+1]=tem;//确定当前i应该到的位置
}
}
Javaの実装
//插入排序算法:
private static void insertion_sort (int a[], int n) {
//n为a[]的实际长度-1,例如a[4]={3,2,9,10} n为3
int i,j,tem;//中间临时变量
for (i=1; i<=n; i++) {
//当前i与前(i-1)个做比较如果当前i元素小于前(i-1)个,则第(i-1)个向挪一位
for (tem=a[i],j=i-1;j>=0&&tem<a[j];j--)a[j+1]=a[j];
a[j+1]=tem;//确定当前i应该到的位置
}
}
3.4 アルゴリズム解析
挿入ソートの実装では、通常、インプレースソート (つまり、O(1) 個の追加スペースのみを使用するソート) が使用されるため、前から前へスキャンする過程で、要素を段階的に後方にソートし、最新の要素の挿入スペースを提供します。
4. シェルソート
1959 年に Shell によって発明された、O(n2) を突破した最初のソート アルゴリズムは、単純な挿入ソートの改良版です。挿入ソートとは異なり、遠くにある要素を最初に比較します。ヒル ソートは、縮小増分ソートとも呼ばれます。
4.1 アルゴリズムの説明
まず、ソート対象のレコード シーケンス全体を直接挿入ソート用のいくつかのサブシーケンスに分割します。具体的なアルゴリズムの説明は次のとおりです。
- インクリメンタルシーケンス t1、t2、...、tk を選択します。ここで、ti>tj、tk=1。
- 増分シーケンス番号 k に従って、シーケンスを k 回ソートします。
- 各ソートでは、対応する増分 ti に従って、ソート対象の列が長さ m のいくつかのサブシーケンスに分割され、各サブリストに対して直接挿入ソートがそれぞれ実行されます。インクリメント係数が 1 の場合のみ、シーケンス全体がテーブルとして扱われ、テーブルの長さがシーケンス全体の長さになります。
4.2 アニメーションのプレゼンテーション
4.3 コードの実装
C/C++ 实现:
//希尔排序
void shell_sort(int a[],int n){
//n为a[]的实际长度-1,例如a[4]={3,2,9,10} n为3
for(int d=n/2;d>0;d/=2)//d 为增量
for(int i=d;i<=n;i++){
int j=i;
int tem=a[j];//j-d 代表 当前j的前一个元素(当前同一个分组)
while(j-d>=0&&a[j-d]>tem) a[j]=a[j-d],j=j-d;// 插入排序的方法
a[j]=tem;
}
}
Java 実装:
//希尔排序
private static void shell_sort(int a[],int n){
for(int d=n/2;d>0;d/=2)//d 为增量
for(int i=d;i<=n;i++){
int j=i;
int tem=a[j];//j-d 代表 当前j的前一个元素(当前同一个分组)
while(j-d>=0&&a[j-d]>tem) {
a[j]=a[j-d];j=j-d;}// 插入排序的方法
a[j]=tem;
}
}
4.4 アルゴリズム解析
ヒルソートの核心は、間隔シーケンスの設定にあります。間隔シーケンスは事前に設定することも、動的に定義することもできます。間隔シーケンスを動的に定義するアルゴリズムは、Algorithms (第 4 版) の共著者である Robert Sedgewick によって提案されました。
5. マージソート
マージ ソートは、マージ操作に基づく効率的な並べ替えアルゴリズムです。このアルゴリズムは、分割統治の非常に典型的なアプリケーションです。順序付けられたサブシーケンスを結合して、完全に順序付けられたシーケンスを取得します。つまり、最初に各サブシーケンスを順番に作成し、次にサブシーケンス セグメントを順番に作成します。2 つのソート済みリストを 1 つのソート済みリストにマージすることを、双方向マージと呼びます。
5.1 アルゴリズムの説明
- 長さ n の入力シーケンスを長さ n/2 の 2 つのサブシーケンスに分割します。
- 2 つのサブシーケンスにそれぞれマージ ソートを使用します。
- 2 つのソートされたサブシーケンスを最終的なソートされたシーケンスにマージします。
5.2 アニメーションのプレゼンテーション
5.3 コードの実装
C/C++ 実装:
//归并排序
void merge_core(int a[],int l,int mid,int r){
//合并数组
int k=0,i=l,j=mid+1;
int *tem=(int*)malloc(sizeof(int)*(r-l+1));//开临时中间数组
// 执行合并 谁小谁先进tem 两个组比大小
while(i<=mid||j<=r)tem[k++]=((a[i]<a[j]&&i<=mid)||j>r)?a[i++]:a[j++];
while(k--)a[r--]=tem[k]; free(tem);//清除空间
}
void merge_sort(int a[],int l,int r){
if(r>l){
int mid=(l+r)>>1;//取中间 一分为二
merge_sort(a,l,mid);
merge_sort(a,mid+1,r);
merge_core(a,l,mid,r);//合并
}
}
Java 実装:
//归并排序
private static void mergeSort(int a[],int l,int r){
if(r>l){
int mid=(l+r)>>1;//取中间 一分为二
mergeSort(a,l,mid);
mergeSort(a,mid+1,r);
mergeCore(a,l,mid,r);//合并
}
}
private static void mergeCore(int a[],int l,int mid,int r){
//合并数组
int k=0,i=l,j=mid+1;
int tem[]=new int[r-l+2];//开临时中间数组
// 执行合并 谁小谁先进 两个组比大小
while(i<=mid||j<=r)tem[k++]=(j>r||(i<=mid&&a[i]<a[j]))?a[i++]:a[j++];
while(k!=0)a[r--]=tem[--k];
}
5.4 アルゴリズム解析
マージソートは安定したソート方法です。選択ソートと同様、マージ ソートのパフォーマンスは入力データの影響を受けませんが、時間計算量が常に O(nlogn) であるため、パフォーマンスは選択ソートよりもはるかに優れています。その代償として、追加のメモリ領域が必要になります。
6. クイックソート
クイックソートの基本的な考え方: ソート対象のレコードはワンパスソートによって 2 つの独立した部分に分離され、レコードの一方の部分のキーワードが他方の部分のキーワードより小さく、その後、レコードの 2 つの部分が小さくなります。レコードを個別に並べ替えることで、全体の順序が整います。
6.1 アルゴリズムの説明
クイックソートは分割統治法を使用して、文字列 (リスト) を 2 つの部分文字列 (サブリスト) に分割します。具体的なアルゴリズムは次のように説明されます。
- シーケンスから「ピボット」(ピボット) と呼ばれる要素を選択します。
- シーケンスを並べ替えます。基準値より小さいすべての要素は基準値の前に配置され、基準値より大きいすべての要素は基準値の後ろに配置されます (同じ数値をどちらの側にも配置できます)。このパーティションが終了すると、ベンチマークはシーケンスの途中になります。これはパーティション操作と呼ばれます。
- 基本値より小さい要素を含む部分配列と、基本値より大きい要素を含む部分配列を再帰的に並べ替えます。
6.2 アニメーションのプレゼンテーション
6.3 コードの実装
C/C++ 実装:
int mp(int a[], int l, int r) {
int p = a[r];
while (l<r) {
while (l<r && p>a[l])l++;//从左向右寻找比标准p大的数
if(l<r) a[r--]=a[l];//找到了就放到最右边,比标准p大就应该在右边
while (l<r && p<a[r])r--;//从右向左寻找比标准p小的数
if(l<r) a[l++]=a[r];//找到就放到最左边,比标准p大就应该在左边
}
a[r]=p;
return r;
}
//快速排序
void quick_sort (int a[], int l, int r) {
//r为a[]的实际长度-1,例如a[4]={3,2,9,10} l为0 r为3
if (l < r) {
int q = mp(a, l, r);//找到标准应该在的位置
quick_sort(a, l, q-1);//根据标准的位置分为左边部分
quick_sort(a, q+1, r);//根据标准的位置分为右边部分
}
}
Java 実装:
//快速排序
private static void quickSort(int[] a, int l, int r) {
//r为a[]的实际长度-1,例如a[4]={3,2,9,10} l为0 r为3
if(l<r) {
int p=mpSort(a,l,r);//找到标准应该在的位置
quickSort(a,l,p-1);//根据标准的位置分为左边部分
quickSort(a,p+1,r);//根据标准的位置分为右边部分
}
}
private static int mpSort(int[] a, int l, int r) {
int p=a[r];
while(l<r) {
while(l<r&&p>a[l])l++;//从左向右寻找比标准p大的数
if(l<r)a[r--]=a[l];//找到了就放到最右边,比标准p大就应该在右边
while(l<r&&p<a[r])r--;//从右向左寻找比标准p小的数
if(l<r)a[l++]=a[r];//找到就放到最左边,比标准p大就应该在左边
} a[l]=p;
return l;
}
6.4 アルゴリズム解析
クイック ソート アルゴリズムの時間計算量は、各標準データ要素の値と大きな関係があります。毎回選択される標準要素が 2 つの部分配列の長さを均等に分割できる場合、そのような迅速なソート プロセスは完全な 2 分木構造になります。(つまり、各ノードは現在の配列を同じサイズの 2 つの配列ノードに分割し、n 要素配列のルート ノードの分解数によって完全な二分木が構成されます)。このとき、分解回数は完全な二分木の深さ log2n に等しく、各クイックソート処理で配列をどのように分割しても、合計の比較回数は n-1 回に近いため、時間計算量は非常に大きくなります。クイック ソート アルゴリズムの最良のケースでは O(nlog2n) です。 : クイック ソート アルゴリズムの最悪のケースは、データ要素がすべて順番に並んでいることです。このとき、データ要素配列のルート ノードの時間は次のようになります。バイナリ縮退ツリー (つまり、単一分岐バイナリ ツリー) とバイナリ縮退ツリー 深さは n なので、クイックソート アルゴリズムの最悪の場合の時間計算量は O(n2) です。一般に、標準要素値の分布はランダムであり、そのようなバイナリ ツリーの深さは log2n に近いため、クイック ソート アルゴリズムの平均 (または予想される) 時間計算量は O(nlog2n) です。
7. ヒープソート
ヒープソート (Heapsort) は、ヒープのデータ構造を使用して設計されたソート アルゴリズムを指します。スタッキングは完全なバイナリ ツリーに近似する構造であり、同時にスタッキングの性質も満たします。つまり、子ノードのキー値またはインデックスは常にその親ノードよりも小さい (または大きい) ものです。
7.1 アルゴリズムの説明
- 並べ替えるキーワードの最初のシーケンス (R1、R2....Rn) を、最初の順序付けされていない領域である大きな上部ヒープに構築します。
- 最上位要素 R[1] を最後の要素 R[n] と交換し、新しい非順序領域 (R1, R2,...Rn-1) と新しい順序領域 (Rn) を取得し、R [1, 2...n-1]<=R[n];
- 交換後の新しいヒープ先頭 R[1] はヒープの性質に反する可能性があるため、現在の未順序領域 (R1, R2,...Rn-1) を新しいヒープに調整して R を結合する必要があります。 [1] 再び無秩序領域を使用します。領域の最後の要素が交換されて、新しい無秩序領域 (R1、R2....Rn-2) と新しい順序領域 (Rn-1、Rn) が得られます。この処理を順序付け領域の要素数が n-1 個になるまで繰り返し、ソート処理全体が完了します。
7.2 アニメーションのプレゼンテーション
7.3 コードの実装
C/C++ 実装:
//大顶堆
void max_heapify(int a[], int l, int r) {
int f= l;//建立父节点指标和子节点指标
int s= f*2+1;
while (s <=r) {
//若子节点指标在范围内才做比较
if (s+1<=r&&a[s]<a[s+1])s++;//先比较两个子节点大小,选择最大的
if (a[f]<a[s]){
//如果父节点小于子节点代表需调整,交换父子内容再继续子节点和孙节点比较
int tem=a[f];a[f]=a[s];a[s]=tem;
f=s;
s=f*2+1;//交换父子内容再继续子节点和孙节点比较
}else return;//调整好了直接跳出
}
}
//堆排序
void heap_sort(int a[], int n) {
for (int i=n/2; i>= 0; i--)max_heapify(a,i,n);//i从最後一个父节点开始调整
//先将第一个元素和已排好元素前一位做交换,再重新调整,直到排序完毕
for (int i=n; i>0;i--) {
int tem=a[0];a[0]=a[i];a[i]=tem;
max_heapify(a,0,i-1);
}
}
Java 実装:
//堆排序
private static void heapSort(int a[], int n) {
for (int i=n/2; i>= 0; i--)maxHeapify(a,i,n);//i从最後一个父节点开始调整
//先将第一个元素和已排好元素前一位做交换,再重新调整,直到排序完毕
for (int i=n; i>0;i--) {
int tem=a[0];a[0]=a[i];a[i]=tem;
maxHeapify(a,0,i-1);
}
}
//大顶堆
private static void maxHeapify(int a[], int l, int r) {
int f= l;//建立父节点指标和子节点指标
int s= f*2+1;
while (s <=r) {
//若子节点指标在范围内才做比较
if (s+1<=r&&a[s]<a[s+1])s++;//先比较两个子节点大小,选择最大的
if (a[f]<a[s]){
//如果父节点小于子节点代表需调整,交换父子内容再继续子节点和孙节点比较
int tem=a[f];a[f]=a[s];a[s]=tem;
f=s;
s=f*2+1;//交换父子内容再继续子节点和孙节点比较
}else return;//调整好了直接跳出
}
}
7.4 アルゴリズム解析
ヒープ ソートは不安定なソート アルゴリズムです。n 個の要素のシーケンスの場合、ヒープ プロセスを構築するには、走査される要素の数は O(n) で、各要素の調整数は O(log2n) です。ヒープ構築の複雑さは O(nlog2n) です。並べ替えるセットの最初と最後の要素を置き換える反復回数は O(n)、各置換後の調整回数は O(log2n) であるため、反復操作の複雑さは O(nlog2n) です。ヒープ ソートの時間計算量は O(nlog2n) であり、ソート プロセスは追加のストレージ領域を必要としないインプレース ソートに属しているため、スペース計算量は O(1) であることがわかります。
8. カウントソート
カウントソートは、比較に基づくソートアルゴリズムではなく、入力データ値をキーに変換し、追加の配列スペースに格納することが中心です。一種の線形時間計算量として、カウンティング ソートでは、入力データが特定の範囲の整数である必要があります。
8.1 アルゴリズムの説明
- 並べ替える配列内の最大要素と最小要素を見つけます。
- 配列内で値が i である各要素の出現数をカウントし、それを配列 C の i 番目の項目に格納します。
- すべてのカウントを累積します (C の最初の要素から開始して、各項目が前の項目に追加されます)。
- ターゲット配列を逆に埋めます。各要素 i を新しい配列の C(i) 番目の項目に置き、要素が配置されるたびに C(i) から 1 を減算します。
8.2 アニメーションのプレゼンテーション
8.3 コードの実装
C/C++ 実装:
//计数排序
void counting_sort(int a[],int n){
int max_num=0,k=0; //获取最大值
for(int i=1;i<=n;i++)if(a[i]>a[max_num])max_num=i;max_num=a[max_num];
int count[max_num+1]; memset(count,0,sizeof(count));//初始化计数数组
for(int i=0;i<=n;i++)count[a[i]]++;//开始计数
for(int i=0;i<=max_num;i++){
//排序
while(count[i]--)a[k++]=i;//到正确的位置
}
}
Java 実装:
private static void counting_sort(int a[],int n){
int max_num=0,k=0; //获取最大值
for(int i=1;i<=n;i++)if(a[i]>a[max_num])max_num=i;
max_num=a[max_num];
int []count=new int[max_num+1]; //初始化计数数组
for(int i=0;i<=n;i++)count[a[i]]++;//开始计数
for(int i=0;i<=max_num;i++){
//排序
while(0<count[i]--)a[k++]=i;//到正确的位置
}
}
8.4 アルゴリズム解析
カウンティングソートは安定したソートアルゴリズムです。入力要素が 0 から k までの n 個の整数の場合、時間計算量は O(n+k)、空間計算量も O(n+k) となり、ソート速度はどの比較ソート アルゴリズムよりも高速になります。カウンティング ソートは、k がそれほど大きくなく、シーケンスが比較的集中している場合に効率的なソート アルゴリズムです。
9. バケットソート
バケット ソートは、カウンティング ソートのアップグレード バージョンです。これは関数のマッピング関係を利用するもので、高効率の鍵はこのマッピング関数の決定にあります。バケット ソートの動作原理 (バケット ソート): 入力データが均一に分散されていると仮定すると、データは限られた数のバケットに分割され、各バケットは個別にソートされます (他のソート アルゴリズムを使用することも、引き続き使用することも可能です)再帰的に) バケットソート)。
9.1 アルゴリズムの説明
- 定量的配列を空のバケットとして設定します。
- 入力データを走査し、データを 1 つずつ対応するバケットに入れます。
- 空ではない各バケットを並べ替えます。
- 空ではないバケットからソートされたデータを連結します。
9.2 アニメーションのプレゼンテーション
9.3 コードの実装
function bucketSort(arr, bucketSize) {
if (arr.length === 0) {
return arr;
}
var i;
var minValue = arr[0];
var maxValue = arr[0];
for (i = 1; i < arr.length; i++) {
if (arr[i] < minValue) {
minValue = arr[i]; // 输入数据的最小值
}else if (arr[i] > maxValue) {
maxValue = arr[i]; // 输入数据的最大值
}
}
// 桶的初始化
var DEFAULT_BUCKET_SIZE = 5; // 设置桶的默认数量为5
bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
var buckets =new Array(bucketCount);
for (i = 0; i < buckets.length; i++) {
buckets[i] = [];
}
// 利用映射函数将数据分配到各个桶中
for (i = 0; i < arr.length; i++) {
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
}
arr.length = 0;
for (i = 0; i < buckets.length; i++) {
insertionSort(buckets[i]); // 对每个桶进行排序,这里使用了插入排序
for (var j = 0; j < buckets[i].length; j++) {
arr.push(buckets[i][j]);
}
}
return arr;
}
9.4 アルゴリズム解析
バケット ソートでは、最良の場合、線形時間 O(n) が使用されます。他の部分の時間計算量は O(n) であるため、バケット ソートの時間計算量は、バケット間でのデータの並べ替えの時間計算量に依存します。明らかに、バケットの分割が小さくなるほど、各バケット間のデータが少なくなり、並べ替えにかかる時間が短くなります。ただし、それに応じてスペース消費量も増加します。
10. 基数ソート
カーディナリティの並べ替えでは、最初に低位に従って並べ替えてから収集し、次に上位に従って並べ替えてから収集するということを、最高位まで繰り返します。場合によっては、一部の属性に優先順位が付けられ、最初に優先順位の低い順に並べ替えられ、次に優先順位の高い順に並べ替えられることがあります。最終的な順序は、優先度が高いものが先、同じ優先度が高くても優先度が低いものが先となります。
10.1 アルゴリズムの説明
- 配列内の最大数を取得し、桁数を取得します。
- arr は元の配列で、各ビットが最下位ビットから取得されて基数配列を形成します。
- 基数に対してカウントソートを実行します(カウントソートが狭い範囲の数値に適しているという機能を使用します)。
10.2 アニメーションのプレゼンテーション
10.3 コードの実装
var counter = [];
function radixSort(arr, maxDigit) {
var mod = 10;
var dev = 1;
for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
for(var j = 0; j < arr.length; j++) {
var bucket = parseInt((arr[j] % mod) / dev);
if(counter[bucket]==null) {
counter[bucket] = [];
}
counter[bucket].push(arr[j]);
}
var pos = 0;
for(var j = 0; j < counter.length; j++) {
var value =null;
if(counter[j]!=null) {
while ((value = counter[j].shift()) !=null) {
arr[pos++] = value;
}
}
}
}
return arr;
}
10.4 アルゴリズム解析
基数ソートは個別にソートして個別に収集することに基づいているため、安定しています。ただし、基数ソートのパフォーマンスはバケット ソートよりわずかに悪く、各キーワード バケットの割り当てには O(n) 時間の計算量が必要で、割り当て後の新しいキーワード シーケンスの取得には O(n) 時間の計算量が必要です。ソート対象のデータが d 個のキーワードに分割できる場合、基数ソートの時間計算量は O(d*2n) になります。もちろん、d は n よりもはるかに小さいため、基本的には線形です。
基数ソートの空間計算量は O(n+k) です。ここで、k はバケットの数です。一般的に言えば、n>>k であるため、追加のスペースは約 n 個必要になります。