希爾排序
基本思想:希爾排序把n個元素按一定的間隔分成幾組,然後按組爲單位進行插入排序。 。
將待排記錄序列以一定的增量間隔h 分割成多個子序列,對每個子序列分別進行一趟直接插入排序, 然後逐步減小分組的步長h ,對於每一個步長h 下的各個子序列進行同樣方法的排序,直到步長爲1 時再進行一次整體插入排序。
因爲不管記錄序列多麼龐大,關鍵字多麼混亂,在先前較大的分組步長h下每個子序列的規模都不大,用直接插入排序效率都較高。 儘管在隨後的步長h遞減分組中子序列越來越大,但由於整個序列的有序性也越來越明顯,則排序效率依然較高。這種改進抓住了直接插入排序的兩點本質,大大提高了它的時間效率。
希爾算法的本質是縮小增量排序,是對直接插入排序算法的改進。
程序的增量序列常採用的表達式:
(1)h=3*h+1。(h=1,4,13,40,121,364...)
(2)h=2^k-1。(h=1,3,7,15,31,63...
例:待排序列:5,9,1,4,8,2,6,3,7
package cn.test;
import java.util.Arrays;
public class SortTest{
/**
* 以指定的步長將數組元素後移,步長指定每個元素間的間隔
* @param array 待排序數組
* @param startIndex 從哪裏開始移
* @param endIndex 到哪個元素止
* @param step 步長
*/
private void move(int[] array, int startIndex, int endIndex, int step) {
for (int i = endIndex; i >= startIndex; i -= step) {
array[i + step] = array[i];
}
}
/**
* 希爾排序算法的實現,對數組中指定的元素進行排序
* @param array 待排序的數組
*/
public void shelltSort(int[] array) { //升序排列
//初始步長,實質爲每輪的分組數
int step = initStep(array.length);
//第一層循環是對排序輪次進行循環。(step + 1) / 2 - 1 爲下一輪步長值
for (; step >= 1; step = (step + 1) / 2 - 1) {
//對每輪裏的每個分組進行循環
for (int groupIndex = 0; groupIndex < step; groupIndex++) {
//對每組進行直接插入排序
insertSort(array, groupIndex, step);
}
}
}
/**
* 直接插入排序實現
* @param array 待排序數組
* @param groupIndex 對每輪的哪一組進行排序
* @param step 步長
* @param end 整個數組要排哪個元素止
* @param c 比較器
*/
private void insertSort(int[] array, int groupIndex, int step) {
int startIndex = groupIndex;//從哪裏開始排序
int endIndex = startIndex;//排到哪裏
/*
* 排到哪裏需要計算得到,從開始排序元素開始,以step步長,可求得下元素是否在數組範圍內,
* 如果在數組範圍內,則繼續循環,直到索引超現數組範圍
*/
while ((endIndex + step) <= array.length-1) {
endIndex += step;
}
// i爲每小組裏的第二個元素開始
for (int i = groupIndex + step; i <= endIndex; i += step) {
for (int j = groupIndex; j < i; j += step) {
int insertedElem = array[i];
//從有序數組中最一個元素開始查找第一個大於待插入的元素
if (array[j]>insertedElem) {
//找到插入點後,從插入點開始向後所有元素後移step位
move(array, j, i - step, step);
array[j] =insertedElem;
break;
}
}
}
}
private static int initStep(int len) {
//從最小步長1推導出最長初始步長值
/*
* 初始值設置爲步長公式中的最小步長,從最小步長推導出最長初始步長值,即按照以下公式來推:
* 1,3,7,15,...,2^(k-1)-1,2^k-1
* 如果數組長度小於等於4時,步長爲1,即長度小於等於4的數組不用分組,此時直接退化爲直接插
* 入排序
*/
int step = 1;
//試探下一個步長是否滿足條件,如果滿足條件,則步長置爲下一步長
while ((step + 1) * 2 - 1 < len - 1) {
step = (step + 1) * 2 - 1;
}
System.out.println("初始步長 : " + step);
return step;
}
/**
* 測試
* @param args
*/
public static void main(String[] args) {
int[] intArr = { 5, 9, 1, 4, 1, 2, 6, 3, 8, 0, 7 };
System.out.println("源 : " + Arrays.toString(intArr));
SortTest test=new SortTest();
test.shelltSort(intArr);
System.out.println("升 : " + Arrays.toString(intArr));
}
}