摘要
看希爾排序需要先想象出一個二維的矩陣,在這個矩陣中,有多少列資料全看步長(一定的規則得到)。處理完之後,就再接著用另一個步長組成矩陣處理。直到步長全部使用完。
這裡的巧妙之處就是沒有把序列先處理成二維陣列,而是通過與步長配合,依舊在一維的序列中處理。
邏輯
希爾排序相當於把序列當作一個矩陣,逐列進行排序。當全部排序完成,整個序列就完全有序
矩陣的列數取決於步長序列
流程
- 建立步長序列
- 從最大步長開始,整列排序,直到排序完成
實現
建立步長序列,這裡是有一個數組存放步長資料。步長是序列的長度減半直到步長為0 結束。這裡必定會有步長是 1 的資料,所以不用擔心序列沒有完全排序完。
List<Integer> shellStepSequence() {
List<Integer> stepSequence = new ArrayList<>();
int step = array.length;
while ((step >>= 1) > 0) {
stepSequence.add(step);
}
return stepSequence;
}
按照步長序列遍歷,序列就會按照不同的步長去處理排序。然後通過不斷地縮小步長來使得序列逐漸成為一維的序列。
List<Integer> stepSequence = shellStepSequence();
for (Integer step : stepSequence) {
sort(step);
}
把每一列進行排序。陣列中的索引是 col+row*step
。這個地方就是巧妙的地方了,通過 col+row*step
來處理每一列中的元素比較排序處理。用這種方式就相當於把一個一維陣列給拆分成每一行最多有 step 的元素的多列序列,即二維陣列。
而這裡的巧妙就是,給我們營造了一個二維序列的空間,實際的比較和交換邏輯還是在一維陣列上進行,不用額外創造空間,這樣的邏輯,真的是牛。
/*
* 分成 step 列進行排序
*/
void sort(int step) {
// col: 第幾列
for (int col = 0; col < step; col++) {
// 對第 col 列進行排序
// 對 [0, array.length) 進行插入排序
for (int begin = col + step; begin < array.length; begin+=step) {
int cur = begin;
while (cur > col && cmp(cur, cur - step) < 0) {
swap(cur, cur - step);
cur -= step;
}
}
}
}
時間和空間複雜度
- 最好、平均時間複雜度:O(1)
- 最壞時間複雜度:O(n^2)
- 空間複雜度:O(1)
- 屬於不穩定排序