希爾排序算法彩條
**希爾排序**（Shellsort），也稱**遞減增量排序算法**，是插入排序的一種更高效的改進版本。希爾排序是非穩定排序算法。

希爾排序是基於插入排序的以下兩點性質而提出改進方法的：

* 插入排序在對幾乎已經排好序的數據操作時，效率高，即可以達到線性排序的效率
* 但插入排序一般來說是低效的，因為插入排序每次只能將數據移動一位

歷史
--

希爾排序按其設計者希爾（Donald Shell）的名字命名，該算法由1959年公佈。一些老版本教科書和參考手冊把該算法命名為Shell-Metzner，即包含Marlene Metzner Norton的名字，但是根據Metzner本人的說法，"我沒有為這種算法做任何事，我的名字不應該出現在算法的名字中。"[來源請求]

算法實現
----

原始的算法實現在最壞的情況下需要進行O(*n*2)的比較和交換。 V. Pratt的書對算法進行了少量修改，可以使得性能提升至O(*n* log2 *n*)。這比最好的比較算法的O(*n* log *n*)要差一些。

希爾排序通過將比較的全部元素分為幾個區域來提升插入排序的性能。這樣可以讓一個元素可以一次性地朝最終位置前進一大步。然後算法再取越來越小的步長進行排序，算法的最後一步就是普通的插入排序，但是到了這步，需排序的數據幾乎是已排好的了（此時插入排序較快）。

假設有一個很小的數據在一個已按升序排好序的數組的末端。如果用複雜度為O(*n*2)的排序（冒泡排序或插入排序），可能會進行*n*次的比較和交換才能將該數據移至正確位置。而希爾排序會用較大的步長移動數據，所以小數據只需進行少數比較和交換即可到正確位置。

一個更好理解的希爾排序實現：將數組列在一個表中並對列排序（用插入排序）。重複這過程，不過每次用更長的來進行。最後整個表就只有一了。將數組轉換至表是為了更好地理解這算法，算法本身僅僅對原數組進行排序（通過增加索引的步長，例如是用`i += step_size`而不是`i++` ）。

例如，假設有這樣一組數[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ]，如果我們以步長為5開始進行排序，我們可以通過將這列表放在有5的表中來更好地描述算法，這樣他們就應該看起來是這樣：

```
13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10

```

然後我們對每進行排序：

```
10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45

```

將上述四數字，依序接在一起時我們得到：[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].這時10已經移至正確位置了，然後再以3為步長進行排序：

```
10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45

```

排序之後變為：

```
10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94

```

最後以1步長進行排序（此時就是簡單的插入排序了）。

步長序列
----

步長的選擇是希爾排序的重要部分。只要最終步長為1任何步長序列都可以工作。算法最開始以一定的步長進行排序。然後會繼續以一定步長進行排序，最終算法以步長為1進行排序。當步長為1時，算法變為普通插入排序，這就保證了數據一定會被排序。

Donald Shell最初建議步長選擇為\frac{n}{2}並且對步長取半直到步長達到1。雖然這樣取可以比{\mathcal {O}}(n^{2})類的算法（插入排序）更好，但這樣仍然有減少平均時間和最差時間的餘地。

| 步長序列 | 最壞情況下複雜度 |
| --- | --- |
| {n/2^i} | \mathcal{O}(n^2) |
| 2^k - 1 | \mathcal{O}(n^{3/2}) |
| 2^i 3^j | \mathcal{O}( n\log^2 n ) |

已知的最好步長序列是由Sedgewick提出的(1, 5, 19, 41, 109,...)，該序列的項，從第0項開始，偶數來自9\times 4^{i}-9\times 2^{i}+1和奇數來自2^{{i+2}}\times (2^{{i+2}}-3)+1這兩個算式 （页面存档备份，存于）。這項研究也表明"比較在希爾排序中是最主要的操作，而不是交換。"用這樣步長序列的希爾排序比插入排序要快，甚至在小數組中比快速排序和堆排序還快，但是在涉及大量數據時希爾排序還是比快速排序慢。

另一個在大數組中表現優異的步長序列是（斐波那契數列除去0和1將剩餘的數以黃金分割比的兩倍的冪進行運算得到的數列）：(1, 9, 34, 182, 836, 4025, 19001, 90358, 428481, 2034035, 9651787, 45806244, 217378076, 1031612713,…)

偽代碼
---

```
input: an array a of length n with array elements numbered 0 to n − 1
inc ← floor(n/2)
while inc > 0 do:    
    for i = inc .. n − 1 do:        
        temp ← a[i]        
        j ← i        
        while j ≥ inc and a[j − inc] > temp do:            
            a[j] ← a[j − inc]            
            j ← j − inc        
        a[j] ← temp    
    inc ← floor(inc / 2)

```

```
-{}-
输入：1个长度为n的矩阵a，矩阵的编号从0到n - 1
整数inc从n / 2到1，每次循环inc变为inc / 2向下取整
    i从inc到n - 1，每次循环i变为i + 1
        将a[ i ]的值赋给temp
        j从i到inc，每次循环j变为j - inc
            如果a[ j − inc ]大于temp，则将a[ j - inc ]的值赋给a[ j ]
            否则跳出j循环
        j循环结束
        将temp的值赋给a[ j ]
    i循环结束
inc循环结束

```

程式代碼
----

### C語言

```
void shell\_sort(int arr[], int len) {
 int gap, i, j;
 int temp;
 for (gap = len >> 1; gap > 0; gap >>= 1)
 for (i = gap; i < len; i++) {
 temp = arr[i];
 for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
 arr[j + gap] = arr[j];
 arr[j + gap] = temp;
 }
}

```

### C++

```
template<typename T>
void shell\_sort(T array[], int length) {
 int h = 1;
 while (h < length / 3) {
 h = 3 \* h + 1;
 }
 while (h >= 1) {
 for (int i = h; i < length; i++) {
 for (int j = i; j >= h && array[j] < array[j - h]; j -= h) {
 std::swap(array[j], array[j - h]);
 }
 }
 h = h / 3;
 }
}

```

### Java

```
public static void shellSort(int[] arr) {
 int length = arr.length;
 int temp;
 for (int step = length / 2; step >= 1; step /= 2) {
 for (int i = step; i < length; i++) {
 temp = arr[i];
 int j = i - step;
 while (j >= 0 && arr[j] > temp) {
 arr[j + step] = arr[j];
 j -= step;
 }
 arr[j + step] = temp;
 }
 }
 }

```

### C#

```
public void shellSort(int[]a)
 {
 int h = a.Length / 2;
 while(h>=1)
 {
 for(int i=0;i<h;i++)
 {
 for(int j=i+h;j<a.Length;j+=h)
 {
 int temp=a[j];
 int loc = j;
 while (loc - h >= i&&temp < a[loc - h])
 {
 a[loc] = a[loc - h];
 loc = loc - h;
 }
 a[loc] = temp;
 }
 }
 h = h / 2;
 }
 }

```

### JavaScript

```
function shell\_sort(arr) {
 for (let gap = arr.length >> 1; gap > 0; gap >>= 1) {
 for (let i = gap; i < arr.length; i++) {
 let temp = arr[i],j;
 for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap) {
 arr[j + gap] = arr[j];
 }
 arr[j + gap] = temp;
 }
 }
 return arr;
};

```

### Python

```
def shell\_sort(list):
    n = len(list)
    # 初始步長
    gap = n // 2
    while gap > 0:
        for i in range(gap, n):
            # 每个步長進行插入排序
            temp = list[i]
            j = i
            # 插入排序
            while j >= 0 and j-gap >= 0 and list[j - gap] > temp:
                list[j] = list[j - gap]
                j -= gap
            list[j] = temp
        # 得到新的步長
        gap = gap // 2
    return list

```

### PHP

```
function shell\_sort(&$arr) {//php的陣列視為基本型別，所以必須用傳參考才能修改原陣列
	for ($gap = count($arr)>>1; $gap > 0; $gap>>=1)
		for ($i = $gap; $i < count($arr); $i++) {
			$temp = $arr[$i];
			for ($j = $i - $gap; $j >= 0 && $arr[$j] > $temp; $j -= $gap)
				$arr[$j + $gap] = $arr[$j];
			$arr[$j + $gap] = $temp;
		}
}

```

### Go

```
package main

import (
 "fmt"
)

func ShellSort(array []int) {
 n := len(array)
 if n < 2 {
 return
 }
 key := n / 2
 for key > 0 {
 for i := key; i < n; i++ {
 j := i
 for j >= key && array[j] < array[j-key] {
 array[j], array[j-key] = array[j-key], array[j]
 j = j - key
 }
 }
 key = key / 2
 }
}

func main() {
 array := []int{
 55, 94, 87, 1, 4, 32, 11, 77, 39, 42, 64, 53, 70, 12, 9,
 }
 fmt.Println(array)
 ShellSort(array)
 fmt.Println(array)

}

```

### Julia语言

```
# Julia Sample : ShellSort

function ShellSort(A)
 inc = div(length(A), 2)
 
 while inc > 0
 for i in inc+1:length(A)
 j = i
 tmp = A[i]

 while j > inc && A[j - inc] > tmp
 A[j] = A[j-inc]
 j -= inc
 end
 
 A[j] = tmp
 end
 
 if inc == 2
 inc = 1
 else
 inc = floor(Int, inc \* 5.0 / 11)
 end
 end
 return A
end

# Main Code
A = [16,586,1,31,354,43,3]
println(A) # Original Array
println(ShellSort(A)) # Shell Sort Array

```

引用
--

1. ↑  Pratt, V.. Garland. 1979. ISBN 0-824- 04406-1.  (This was originally presented as the author's Ph.D. thesis, Stanford University, 1971)
2. ↑  A154393 The fibonacci to the power of two times the golden ratio gap sequence

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
排序算法 |
| --- |
|  |
| 理论 | 
* 計算複雜性理論
* 大O符号
* 全序关系
* 数据结构术语列表
* 原地算法
* 稳定性
* 比较排序
* 自适应排序（英语：）
* 排序网络（英语：）
* 整数排序（英语：）
* X+Y排序（英语：）
* 量子排序（英语：）

 |
|  |
| 交换排序 | 
* 冒泡排序
* 鸡尾酒排序
* 奇偶排序
* 梳排序
* 侏儒排序
* 快速排序
* 慢速排序
* 臭皮匠排序
* Bogo排序

 |
|  |
| 选择排序 | 
* 选择排序
* 堆排序
* 平滑排序（英语：）
* 笛卡尔树排序（英语：）
* 锦标赛排序（英语：）
* 圈排序（英语：）
* 弱堆排序（英语：）

 |
|  |
| 插入排序 | 
* 插入排序
* 希尔排序
* 伸展排序
* 二叉查找树排序
* 图书馆排序
* 耐心排序

 |
|  |
| 归并排序 | 
* 归并排序
* 梯级归并排序（英语：）
* 振荡归并排序（英语：）
* 多相归并排序（英语：）

 |
|  |
| 分布排序 | 
* 美国旗帜排序（英语：）
* 珠排序
* 桶排序
* 爆炸排序（英语：）
* 计数排序
* 比較計數排序
* 插值排序
* 鸽巢排序
* 相邻图排序（英语：）
* 基数排序
* 闪电排序（英语：）

 |
|  |
| 并发排序 | 
* 双调排序器（英语：）
* Batcher归并网络
* 两两排序网络（英语：）

 |
|  |
| 混合排序 | 
* 塊排序（英语：）
* Tim排序
* 内省排序
* Spread排序（英语：）
* 归并插入排序（英语：）

 |
|  |
| 其他 | 
* 拓撲排序
* 煎餅排序
* 意粉排序（英语：）

 |

 |

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
算法 |
| --- |
|  |
| 排序 | 

|  |  |
| --- | --- |
| 比较排序 | 
* 冒泡排序
* 选择排序
* 插入排序
* 希尔排序
* 快速排序
* 归并排序
* 堆排序
* 鸡尾酒排序
* 梳排序
* 侏儒排序
* 图书馆排序
* 内省排序
* 奇偶排序

 |
|  |
| 线性时间排序 | 
* 鸽巢排序
* 基数排序
* 計數排序
* 桶排序

 |
|  |
| 并行排序 | 
* 排序网络（英语：）
* Batcher归并网络

 |
|  |
| 不实用的 | 
* Bogo排序
* 臭皮匠排序

 |
|  |
| 图 | 
* 拓撲排序

 |

 |
|  |
| 搜索 | 

|  |  |
| --- | --- |
| 列表 | 
* 线性搜索
* 二分搜索
* 插值搜尋

 |
|  |
| 树・图 | 
* 广度优先搜索
	+ 最良優先搜索（英语：）
	+ 均一开销搜索
	+ A\*
* 深度优先搜索
	+ 迭代深化深度优先搜索
	+ 深度限制搜索（日语：）
	+ 双向搜索
* 分枝限定法（英语：）

 |
|  |
| 字符串 | 
* KMP算法
* 博耶-穆尔字符串搜索算法
* AC自动机算法
* 拉宾-卡普算法
* bitap算法

 |

 |
|  |
| 最短路问题 | 
* 戴克斯特拉算法
* 贝尔曼-福特算法
* A\*搜尋演算法
* Floyd-Warshall算法

 |
|  |
| 最小生成树 | 
* 普林姆算法
* 克鲁斯克尔演算法

 |
|  |
| 最大流最小割 | 
* 福特-富尔克森算法
* 埃德蒙兹-卡普算法
* 迪尼茨算法

 |
|  |
| 线性规划 | 
* 单纯形法
* 卡马卡尔算法（英语：）

 |
|  |
| 順序統計量 | 
* 选择算法
* 中位数的中位数（英语：）

 |
|  |
| 種類 | 
* 精确算法
* 近似算法
* 随机化算法

 |
|  |
| 其他 | 
* 分治法
* 动态规划
* 贪心算法

 |
|  |
| **Category:算法** |

 |