目次
ポーカーをプレイするのと同様に、カードを並べ替えるときに、ランダムなカードが既にコード化されているカードに挿入されます-自然な挿入並べ替え。
1. 直入れソート
順序付けされていない間隔の最初の要素を毎回選択し、それを順序付けられた間隔の適切な位置に挿入し、配列全体が整うまでこのプロセスを繰り返します。間隔全体は、順序付き間隔 [0, i) と順序なし間隔 [i, n) に分割されます。
/**
* 直接插入排序 稳定的
* 每次选择无序区间的第一个元素,插入到有序区间的合适位置
* @param arr
*/
public static void insertionSortBase(int[] arr) {
//有序区间[0,i) [0,1)
//默认第一个元素就是有序
for (int i = 1; i < arr.length; i++) {
//每次选择无序区间的第一个元素,插入到有序区间的合适位置
//无序区间[i, n)
for (int j = i; j > 0 && arr[j] < arr[j - 1]; j--) {
swap(arr, j, j-1);
//arr[j] > arr[j - 1]此时循环直接终止
//arr[j - 1]已经是有序区间元素,大于前面的所有值
}
}
}
安定性:安定。
arr[j] > arr[j - 1] ループが終了するため、並べ替えの前後で等しい要素の順序は変わりません。
パフォーマンス:
時間計算量: n * 1 + (n - 1) * 2 + (n - 2) * 3 + ... + 2 * (n - 1) + 1 * n => O(n ^ 2)
挿入ソートと選択ソートの最大の違いは次のとおりです。arr[j] >= arr[j - 1] の場合、ループは直接終了でき、arr[j - 1] はすでに順序付けられた間隔要素です。
挿入ソートは、ほぼ順序付けられた配列で、小さなデータ サイズで非常にうまく機能します。これは、高度な並べ替えアルゴリズムの最適化方法としてよく使用されます。
2. バイナリ挿入ソート
順序付けられた間隔でデータを挿入する位置を選択する場合、間隔の順序があるため、バイナリ検索 (バイナリ検索) のアイデアを使用して、挿入位置をすばやく見つけることができます。
/**
* 折半插入排序
* @param arr
*/
public static void insertionSortBS(int[] arr) {
for (int i = 1; i < arr.length; i++) {
//无序区间第一个值
int val = arr[i];
//有序区间[0,i)
int low = 0;
int high = i;
while(low < high) {
int mid = (low + high) >> 1;
//将相等的值放在左半区间,保证稳定性
if(val >= arr[mid]) {
low = mid + 1;
}else{
//右区间取不到,不用 -1
high = mid;
}
}
//数据搬移
for (int j = i; j > low; j--) {
arr[j] = arr[j - 1];
}
//low就是元素插入位置
arr[low] = val;
}
}
安定性:安定。
時間計算量: n * logn + n * (1...n) => O(N^2)
主に検索(順序付き区間探索)では、二分挿入ソート(1つずつ比較して入れ替える O(logn))の方が、直接挿入ソート(挿入位置を見つけてから要素を移動する O(n))よりも高速です。
移動回数==交換回数(最終投入位置が同じなので)。
3. 配列 arr[l...r] で挿入ソートを使用する
/**
* 在数组arr[l...r]上使用插入排序
* @param arr
* @param l
* @param r
*/
private static void insertBase(int[] arr, int l, int r) {
//有序的区间[l...i]
//无序的区间[i...r]
for (int i = l + 1; i <= r; i++) {
for (int j = i; j > l && arr[j] < arr[j - 1]; j--) {
swap(arr, j, j - 1);
}
}
}