希爾排序--基於快速排序的高階排序
有個叫希爾的人提出的演算法
據說是基於插入排序的性質進行的優化:
1、插入排序在對幾乎已經排好序的資料操作時,效率高,即可以達到線性排序的效率。
2、但插入排序一般來說是低效的,因為插入排序每次只能將資料移動一位。
解釋:
假設全域性上大的全部在後面,小的前面在前面;區域性上也滿足這個條件,那麼交換的次數將大大降低。
假設最大值在前面,排序完成最大值需要移動n次才能移動到最後,因為每次都只能移動一位。假設我們能一次把這個最大值移動到後面,接近將來目標的位置,效率就提高了很多。
希爾排序思路:
將序列按照步長分組,分別兩兩取0 0+step, 1 1+step, 2 2+step進行排序,這樣大的部分就到了後面
然後按照一定規則,遞減步長,直到步長為1,排序完成,,,
只要保證最後一次會用1進行簡單插入排序,結果一定是滿足排序規則的,前面的過程其實是將陣列往前面小,後面大的趨勢格式化。所以我們可以藉助之前實現的二分插入排序,在外面再套一層迴圈,迴圈一個增量序列,將間隔傳入內層迴圈,我的程式碼也是這麼實現的。
序列理論上可以任意選擇,只要最後一次序列為1,理論上序列怎麼選都可以,當然目的是更快
這個數列如何選取才是最優是個數學難題,至今未有最優解,只有根據經驗來的最優解
有多種增量序列,其中最快的是塞奇威克的增量序列
1、希爾(Shell)原始步長序列:N / 2,N / 4,…,1(重複除以2),步長的選擇簡單粗暴,但是思想很重要;
2、希伯德(Hibbard)的步長序列:1,3,7,…。由前面的數用2 k + 1遞迴獲得;
3、克努特(Knuth)的步長序列:1,4,13,…。由前面的數用3 k + 1遞迴獲得;
4、塞奇威克(Sedgewick) 的步長序列:1,5,19,41,109,….
它是通過交織兩個序列的元素獲得的: 步長序列陣列下標 n 從0開始,奇數的k值從0開始,逢奇數+1;偶數的下標從2開始,逢偶數+1,定義k1 = 0;k2 = 2;
n偶數用 :1,19,109,505,2161,…。
公式為:9(4 ^k1 - 2^ k1)+ 1;然後k1++…
n奇數用 :5,41,209,929,3905,…。
公式為:4 ^k2- 3(2^k2)+ 1;然後k2++…
專家們提倡,幾乎任何排序工作在開始時都可以用希爾排序,若在實際使用中證明它不夠快,再改成快速排序這樣更高階的排序演算法.
時間複雜度:
最差n(log(n)),最快n
穩定性:
不穩定,因為被拆開之後不會相鄰比較,所以不能保持穩定性
Java程式碼實現:
public static void main(String[] args){
int[] arr = new int[]{10, 1, 4 ,2 , 8, 3, 5};
System.out.println(Arrays.toString(indexSort1(arr))); arr = new int[]{10, 1, 4 ,2 , 8, 3, 5}; System.out.println(Arrays.toString(indexSort2(arr))); arr = new int[]{10, 1, 4 ,2 , 8, 3, 5}; System.out.println(Arrays.toString(shellIndexSort(arr)));
}
/**
* 相鄰逐個比較,找到合適的位置,放入
* @param arr
* @return
*/
private static int[] indexSort1(int[] arr){
// 外層控制次數,從1開始,不必從0開始
for(int i = 1;i < arr.length;i++){
// 從後向前比較
int idx = i;
int value = arr[idx];
// 1 2 3 4 3
// 拿著value和前面的資料比,直到找到第一個大於等於的位置,和j比大於等於,那麼j+1,就是要插入的位置
// 找到第一個後面大等於前面的位置,停下來,這個位置後一個位置就是要放入的位置
// 有可能第一趟就停了下來,這個時候j應該為j+1
// 2 3 4 1
// 如果一直找不到,找到0
for(int j = idx - 1; j >= 0; j--){
// 記錄當前位置
idx = j;
if(value >= arr[j]){
idx = j + 1;
break;
}
}
// 將從找到的位置開始到i的位置(不包括i)的資料全部後移一位
for(int j = i - 1; j >= idx; j--){
arr[j + 1] = arr[j];
}
arr[idx] = value;
}
return arr;
}
/**
* 使用二分查詢法查詢應該插入的位置
* @param arr
* @return
*/
private static int[] indexSort2(int[] arr){
return binarySort(arr, 1);
}
/**
* 使用二分查詢的插入排序,使用step作為引數,用來在希爾排序中複用這個方法
* @param arr
* @param step
* @return
*/
private static int[] binarySort(int[] arr, int step){
for(int i = 1; i< arr.length;i++){
int idx = i;
int value = arr[idx];
// 找到應該插入的位置
int insertIndex = binarySearch(arr, value, 0, i - 1);
for(int j = i - 1; j >= insertIndex; j–){
arr[j + 1] = arr[j];
}
arr[insertIndex] = value;
}
return arr;
}
/**
* 遞迴查詢
* @param arr
* @param value
* @param startIndex
* @param endIndex
* @return
*/
private static int binarySearch(int[] arr, int value, int startIndex, int endIndex){
// 推到startIndex = endIndex的時候,就是應該退出的時候
// 假設0-9,獲取中間一位4,前半部分[0 - 4] 後半部分 [5 - 9]
// 假設0-8,獲取中間一位4,前半部分[0-4] 後半部分[5-8]
// 如果中間一位大於等於,往後找,否則往前找
// 如果找到startIndex等於endIndex的時候,無論如何都應該退出
// 考慮一般情況,如果
// 最終返回值應該是0~endIndex+1之間的數,因為多了一位數,這個數可能在最後
// 舉特例:假設往1 或者 2 後面插入2,2的位置應該是endIndex + 1
// 假設往2 裡插入1,1的位置應該是0,也就是startIndex
if(startIndex == endIndex){
// 往後往前的邏輯和下面一樣其實
if(arr[startIndex] <= value){
return startIndex + 1;
}else{
return startIndex;
}
}
int midIndex = (startIndex + endIndex) / 2;
if(arr[midIndex] <= value){
return binarySearch(arr, value, midIndex + 1, endIndex);
}else{
return binarySearch(arr, value, 0, midIndex);
}
}
/**
* 使用希爾的分組排序,最後直接插入排序
* 其實就是在上面的排序外面再套一層殼子,然後先進行一些少次但是結果很有用的排序,這樣後面的排序就會快很多
* @param arr
* @return
*/
private static int[] shellIndexSort(int[] arr){
// 先計算步長陣列
int[] gap = getGap(arr.length);
// 從大到小的間隔
for(int i = gap.length - 1; i >= 0; i--){
// 使用間隔來比較
arr = binarySort(arr, gap[i]);
}
return arr;
}
/**
* 獲取塞奇威克的增量序列,這個公式網上全是錯誤的,麻痺
* @param len
* @return
*/
private static int[] getGap(int len){
List gap = new ArrayList();
int i = 0;
int startup1 = 0;
int startup2 = 2;
int value = 0;
while(true){
if(i % 2 == 0){
value = (int)(9 * (Math.pow(4, startup1) - Math.pow(2 ,startup1)) + 1);
startup1++;
}else{
value = (int)(Math.pow(4, startup2) - 3 * Math.pow(2 ,startup2) + 1);
startup2++;
}
gap.add(value);
i++;
if(value >= len){
System.out.println(gap);
int[] arr = new int[gap.size()];
for(int j = 0; j < arr.length;j++){
arr[j] = gap.get(j);
}
return arr;
}
}
}
相關推薦
希爾排序--基於快速排序的高階排序
有個叫希爾的人提出的演算法 據說是基於插入排序的性質進行的優化: 1、插入排序在對幾乎已經排好序的資料操作時,效率高,即可以達到線性排序的效率。 2、但插入排序一般來說是低效的,因為插入排序每次只能將資料移動一位。 解釋: 假設全域性上大的全部
冒泡選擇插入希爾歸並快速排序等python實現
python實現 election count partition sub star poi point shell def bubble_sort(a_list): for pass_num in range(len(a_list) - 1, 0, -1):
歸併,快速,希爾,普通插入四種排序演算法的比較
import java.util.Arrays; public class ShellSort { public static void main(String[] args) { int[] arr = new int[10000]
數據結構--希爾排序和快速排序
循環控制 全部 元素 思想 print 序列 log display col 1 /*希爾排序:對插入排序的改進,其排序是按照一個增量序列來進行 2 *增量序列的個數就是排序的趟數。在任意增量K下,保證a[i]<=a[i+k] 3 *該算法的效率和增量需序
所有排序算法匯總,冒泡,選擇,插入,快速,優化快速,歸並,希爾,堆排序
合並 左移 詳細 left for循環 兩個 第一個元素 保存 匯總 冒泡排序,不多說,兩次for循環比較相鄰兩個元素的大小,然後進行交換。 選擇排序,我們第一次for循環遍歷所有元素,並把當前元素假設為最小的元素,然後再一個for循環去尋找真正最小的元素進行交換,這樣每
排序演算法(直接插入、氣泡排序、選擇排序、快速排序、希爾排序、堆排序、歸併排序)
main函式 int main() { int data[] = {1,2,6,3,4,7,7,9,8,5}; //bubble_sort(data,10); //select_sort(data,10); Insert_Sort(data,10); fo
JAVA實現冒泡、歸併、希爾、堆排、快速、插入、簡單選擇、排序演算法
氣泡排序 public void bubbleSort(int []nums) { int exchange=nums.length-1; while(exchange!=0) { int bound=exchange; exchange=0; for(i
希爾排序,快速排序,堆排序
最近在準備資料結構的考試,於是用部落格記錄下自己複習的過程。今天的內容是三種高階排序。 希爾排序 當序列增量為incr[k]=2(t-k+1)-1時,時間複雜度為O(n1.5)。以序列增量分組,對每組進行大小調整。 template<class T> void Shell
基於visual Studio2013解決面試題之1404希爾排序
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
讀取檔案內的資料(數字)並進行三種排序,1(快速排序)2(歸併排序)3(希爾排序)。
#include<iostream> #include<fstream> #include<stdlib.h> int n1=0; using namespace std; void Merge(int a[], i
野生前端的資料結構練習(10)希爾排序,歸併排序,快速排序
一.希爾排序 shell sort也稱縮小增量排序,是對插入排序演算法的改進,其工作原理是定義一個間隔序列來表示排序過程中進行比較的元素之間有多遠的間隔,每次將具有相同間隔的數分為一組,進行插入排序,大部分場景中,間隔是可以提前定義好的,也可以動態生成。在較大的資料集上,希爾排序對於插排的優化效果是非常
野生前端的數據結構練習(10)希爾排序,歸並排序,快速排序
merge lang quick 分治法 ado uic pos 快速 ons 一.希爾排序 shell sort也稱縮小增量排序,是對插入排序算法的改進,其工作原理是定義一個間隔序列來表示排序過程中進行比較的元素之間有多遠的間隔,每次將具有相同間隔的數分為一組,進行插入
排序演算法圖解(插入、選擇、冒泡、快速、合併、希爾)
插入排序 從左至右兩兩對比,右邊的數比左邊的小,交換,交換,不斷往右移動 選擇排序 選定最左邊的數A,第二個數B,A和B比較,A>B則交換;B大於A,則取B後一位與A做相同的比較,不斷右移遍歷完,則把最小的放在了最左邊。再取第二個數變為A,做同樣的步驟 氣泡排序
Python排序演算法(二) 快速排序、希爾排序、歸併排序
這篇文章有的排序演算法是:快速排序、希爾排序、歸併排序。 快速排序 ''' 快速排序 ''' def quick_sort(aList, first, last): if first >= last: return min_va
八大排序演算法(希爾排序、快速排序、堆排序)
希爾排序-插入排序的擴充套件 function shellSort(arr) { var len = arr.length; for (var fraction = Math.floor(len / 2); fraction > 0; fractio
3. 排序通常有多種演算法,如氣泡排序、插入排序、選擇排序、希爾排序、歸併排序、快速排序,請選擇任意2種用java實現 [分值:20] 您的回答:(空) (簡答題需要人工評分)
3. 排序通常有多種演算法,如氣泡排序、插入排序、選擇排序、希爾排序、歸併排序、快速排序,請選擇任意2種用java實現 [分值:20] 您的回答:(空) (簡答題需要人工評分) package com.interview; /** * 各種排序演算法 */
C語言中常用排序演算法(氣泡排序、選擇排序、插入排序、希爾排序、快速排序、堆排序)實現比較
以下程式在win10 X64位作業系統,使用VS2017執行驗證可行 排序是非常重要且很常用的一種操作,有氣泡排序、選擇排序、插入排序、希爾排序、快速排序、堆排序等多種方法。 例項1 冒泡法排序 1.前言: 陣列中有N個整數,用冒泡法將它們從小到大(或從大到小)排序。冒泡法
希爾、快速、歸併排序總結
冒泡、選擇、插入排序的效率都是O(N^2),但插入排序稍微快一些 歸併排序的效率是O(NlogN),希爾排序的效率大約是O(N(logN)2),快速排序需要O(N*logN)時間,希爾排序效率不穩定O(N*N(1.3~2)),理論上歸併比快速還要快,但歸併要建立
各種排序演算法的場景以及c++實現(插入排序,希爾排序,氣泡排序,快速排序,選擇排序,歸併排序)
對現有工作並不是很滿意,所以決定找下一個坑。由工作中遇到排序場景並不多,大都是用氣泡排序,太low,面試又經常問到一些排序演算法方面的東西。剛好讓小學妹郵的資料結構也到了。就把各種排序演算法重新總結一下,以作留存。 排序分為內部排序和外部排序,內部排序是在記憶體中排序。外
Java實現快速排序、歸併排序、堆排序和希爾排序
快速排序 演算法思想 1.將陣列的第一個元素取為target,定義兩個指標i 和 j; 2.指標i ,從左向右找到第一個比target大的元素,指標j從右向左找到第一個比target小的元素,