【任务目标】
将一组大规模无序数组变为有序
【希尔排序原理】
在此之前,请先理解插入排序原理。
对于{9,8,7,6,5,4,3,2,1,0}这样一个数组来说,要把0移动到最前面,按照插入排序的移动方式,要移动很多次。可以看到,当我们选择无序部分的元素时,选择的无序部分的第一个元素(按数组下标从左到右),也就是有序部分的最后一个元素右边的元素,即我们每次选择无序部分元素时的增量是1,如果要选择到整个数组的最后一个元素(也即无序部分最右边的元素)时,要经过很多次。如何能很快选择到无序部分最右边的元素呢?显然我们要让增量变大。
我们知道在进行插入排序时,数组左边的有序部分在增加,右边的无序部分在减少,如果我们的增量始终固定在一个较大的值时,那么肯定会遇到下标越界的情况和有些元素总是选择不到的情况。所以每次插入排序了一次后,我们改变增量,把增量减小,直到减小为1。
那么增量按照什么样的规律减小呢?这没有确定的标准。本文按照2倍的方式减小。每次插入排序时的增量构成了一个增量序列。
另一方面,如果增量不为1,那么左边的有序部分不再像插入排序算法中那样有序,因为增量不为1时,从数组0的位置到要选择的这个待排序元素的位置,有很多元素没有被选择排序过。
此时,无序数组不能像插入排序算法中分为左边的有序部分和右边的无序部分。无序数组被增量分为多个小的无序数组,例如元素个数为9,增量为3时,按照下标被分为了三个小的无序数组:{0,3,6},{1,4,7},{2,5,8}。而每个小的无序数组通过插入排序算法变为有序,也即在小的无序数组中可以有无序部分和有序部分。
【希尔排序原理概括】
通过增量序列将数组分为多个部分分别进行插入排序,使得末尾小元素快速前移。
【代码实现】
using System;
namespace Sort
{
class Program
{
static void Main(string[] args)
{
int[] A = new int[30];
Random ra = new Random();
for (int i = 0; i < 30; i++)
{
A[i] = ra.Next(200);
}
Program ps = new Program();
ps.ShellSort(A);
Console.WriteLine("排序结果:");
foreach (int a in A)
{
Console.Write(a + " ");
}
bool isSorted = true;
for (int i = 0; i < A.Length - 1; i++)
{
if (A[i] > A[i + 1])
isSorted = false;
}
Console.Write(isSorted);
Console.ReadKey();
}
public void ShellSort(int[] A)
{
for (int gap = A.Length; gap > 0; gap/=2)
{
for (int i = gap; i < A.Length; i++)//这段代码和插入排序完全一样,只是把原来的增量1改为了gap
{
int temp = A[i];
int j = i - gap;
while (j>=0&&temp<A[j])
{
A[j + gap] = A[j];
j -= gap;
}
A[j + gap] = temp;
}
}
}
}
}