交換ソートの基本的な考え方は、ソートされるシーケンスレコードのキーコードをペアごとに比較することです。レコードが逆の場合は、逆のペアがなくなるまでレコードが交換されます。バブルソートとクイックソートは、一般的な交換ソートアルゴリズムです。
バブルソート
バブルの並べ替えと直接挿入の並べ替えは、おそらく私たちの生活の中で最も一般的に使用される並べ替え方法です。バブルの並べ替えは、入れ子になったループの2つの層で構成されます。外側のループは一度に位置を決定し、内側のループは外側の層に対して相対的です。隣接する要素は、外層の現在の位置まで1つずつ比較され、対応する値が取得されます。
動的表示図は次のとおりです。
コードは次のように実装されます(動的表示図の並べ替えの反対方向)。
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args){
// 待排序列
int[] arrOriginal = new int[]{5, 9, 7, 4, 22, 2, 65, 1, 45};
System.out.println("before sort, the array is: ");
System.out.println(Arrays.toString(arrOriginal));
// 临时变量,内层循环中交换元素时应用
int temp = 0;
boolean noSwap;
// 外层:从某一端开始,确定要排序的某个位置
for (int i = 0; i < arrOriginal.length; i++) {
noSwap = true;
// 内层:从另一端开始,确定外层位置对应的值
for (int j = arrOriginal.length - 1 ; j > i; j--) {
if(arrOriginal[j] < arrOriginal[j-1]){
temp = arrOriginal[j];
arrOriginal[j] = arrOriginal[j-1];
arrOriginal[j-1] = temp;
noSwap = false;
}
}
if(noSwap) {
break;
}
}
System.out.println("\nend sort, the array is: ");
System.out.println(Arrays.toString(arrOriginal));
}
}
直接挿入ソートと同様に、最適な時間の複雑さはO(n)であり、平均および最悪のケースはO(n 2)です。
クイックソート
1962年に発明されたクイックソートは、分割と征服のアイデアを具体化しています。最初に、軸の値を選択し、次に、ソートされるシーケンス要素をそれぞれ軸の値と比較し、軸の値の両側に分割し、軸の値の要素の位置を固定します。最後に、ソートされるシーケンス要素の数が2未満になるまで、左側と右側はクイックソートアルゴリズムを再帰的に呼び出します。
動的なデモの図は次のとおりです。
コードは次のように実装されています。
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args){
int[] arrOriginal = new int[]{5, 9, 7, 4, 22, 2, 65, 1, 45};
System.out.println("before sort, the arr is: ");
System.out.println(Arrays.toString(arrOriginal));
quickSortProcess(arrOriginal, 0, arrOriginal.length-1);
System.out.println("end sort, the arr is: ");
System.out.println(Arrays.toString(arrOriginal));
}
public static void quickSortProcess(int[] arr, int startIdx, int endIdx) {
if (startIdx >= endIdx){
return;
}
// 1. 选择轴值
int flagIndex = (startIdx + endIdx) / 2;
int flagValue = arr[flagIndex];
// 2. 将轴值与末尾元素交换,确定一个可交换的元素
switchVal(arr, flagIndex, endIdx);
// 设定正向与反向游标指针
int forwardIdx = startIdx;
int reverseIdx = endIdx;
// while 正idx < 反idx
while(forwardIdx < reverseIdx) {
// 正向从起始元素开始,遇到比轴值大的元素时,与反向指针元素交换位置,正向指针暂停,break;否则正向+1
while(forwardIdx < reverseIdx && arr[forwardIdx] < flagValue){
forwardIdx++;
}
switchVal(arr, forwardIdx, reverseIdx);
// 反向从结束元素开始,遇。。。。小。。。。,。正。。。。。。。。。,反向指针暂停,break;。。反向-1
while(forwardIdx < reverseIdx && arr[reverseIdx] > flagValue){
reverseIdx--;
}
switchVal(arr, forwardIdx, reverseIdx);
}
// 3. 二分,递归调用(注释代码为错误示范,出现堆栈溢出的异常)
// quickSortProcess(arr, startIdx, endIdx/2);
// quickSortProcess(arr, (endIdx/2 + 1), endIdx);
quickSortProcess(arr, startIdx, forwardIdx);
quickSortProcess(arr, forwardIdx+1, endIdx);
}
public static void switchVal(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
迅速なソート時間の複雑さは次のとおりです。最悪のケースはO(n 2)であり、最良かつ平均的なケースはO(nlogn)です。
まとめ
バブリングとクイックソートはどちらも、エクスチェンジソートの考え方に基づいており、さまざまな実装を提供しています。その中で、バブリングは10進数の世界でのソートの考え方に基づいており、毎回最大または最小を選択し、残りのデータアイテムから次に大きいまたは最大を探します。 2番目に小さいので安定したソートですが、この直感的なアイデアは2乗レベルの時間の複雑さを持ち、大量のデータボリュームには適していません。
クイックソートでは、分割統治の概念を使用します。トップダウンスプリットにより、ソートの問題のサイズが小さくなります。小規模なソートを完了しながら、最終的に全体的なソート効果が得られ、実行がより効率的になります。
参照:
「データ構造とアルゴリズム」
バブルソート-wikiwand
クイックソート-wikiwand
シリーズサンプルコードのダウンロード:githubをフォローしてください