希爾排序是對插入排序的改進,引入維基百科的說明:php
希爾排序是基於插入排序的如下兩點性質而提出改進方法的:java
插入排序在對幾乎已經排好序的數據操做時,效率高,便可以達到線性排序的效率git
但插入排序通常來講是低效的,由於插入排序每次只能將數據移動一位github
原文連接希爾排序,本文將介紹希爾排序的實現思路,時間複雜度,穩定性等.數組
要改進插入排序,就要解決"插入排序每次只能將數據移動一位",而且利用"插入排序在幾乎已經排好序的數據操做時,效率高"的特性.邏輯描述以下.net
1. 初始時將數組劃分爲subArrsNum=arr.length/2個子數組 2. 對這些子數組subArrs使用插入排序排序 3. subArrsNum縮減爲一半,縮減後若是subArrsNum>=1,跳轉到2,不然 結束.
subArrs的定義以下code
subArrs[i]={arr[i],arr[i+k],arr[i+2k],...arr[i+nk]} i<k,arr.length/2 >= k >=1
這裏的難點在於對邏輯子數組的理解,下面結合subArrs的定義,舉例說明blog
arr={1,4,6,5,2,7,3,8}
初始時subArrsNum = arr.length/2=8/2=4,四個子數組分別爲排序
subArrs[0]={1,2}={arr[0],arr[0+4]} subArrs[1]={4,7}={arr[1],arr[1+4]} subArrs[2]={6,3}={arr[2],arr[2+4]} subArrs[3]={5,8}={arr[3],arr[3+4]}
若是咱們想遍歷某個子數組,跨度應該等同於subArrsNum而非1.達成"每次能夠將數據移動不止一位"的效果",ip
而隨着子數組數量愈來愈少,數組也愈來愈有序,更好的利用"插入排序在幾乎已經排好序的數據操做時,效率高"這個特性,代碼以下
public void sort(Comparable[] arr) { if (arr.length <= 1) { return; } for (int subArrsNum = arr.length / 2; subArrsNum >= 1; subArrsNum /= 2) { //處理每一個子數組 for (int secondElePos = subArrsNum; secondElePos < 2 * subArrsNum; secondElePos++) { for (int elePos = secondElePos; elePos < arr.length; elePos += subArrsNum) { int toInsertELePos = elePos; Comparable toInsertEleVal = arr[elePos]; for (int orderedElePos = elePos - subArrsNum; orderedElePos >= 0; orderedElePos -= subArrsNum) { if (toInsertEleVal.compareTo(arr[orderedElePos]) < 0) { arr[orderedElePos + subArrsNum] = arr[orderedElePos]; toInsertELePos = orderedElePos; } else { toInsertELePos = orderedElePos + subArrsNum; break; } } arr[toInsertELePos] = toInsertEleVal; } } } }
最壞狀況下優於O(n^2),詳見 希爾排序&選擇排序&時間複雜度分析
是的,希爾排序不須要使用額外空間
不穩定,插入排序是穩定的,可是希爾排序不是,劃分子數組後,是對各個子數組分別進行插入排序,這時可能兩個相同的元素在不一樣的子數組的排序位置是不一樣的,例如[1,4,3,3,6,5],一次排序後爲[1,3,3,4,6,5],"3"的相對位置發生了變化
源碼地址github