希尔排序的一种缩小增量(Incremental)的排序,是直接插入排序的一种升级。
基本思想就是,将原有的数组arr,分成Incremental 个小数组,然后对各个小数组进行插入排序,当然我认为,也可以用其他排序算法。
然后将增量(Incremental)减半,再分组,再排序。
直到最后Incremental=1,也必须等于1,这时,就只能分成一组了,相当于对整个数组进行一次插入排序。
这样做的好处,在于,不断细分数组,使得在排序过程中减少了数据的移动量。
我的C#实现如下:
/// <summary>
/// Shell sort O((nlogn)^2)
/// </summary>
/// <param name="arr"></param>
public static void ShellSort(int[] arr)
{
if (arr == null)
{
throw new ArgumentNullException();
}
// Initializes the Incremental
int inc = arr.Length / 2;
while (inc > 0)
{
// Splits the array to arrays as many as number of Incremental.
// Members of index(i * inc + n) of the original array are in one splitted array.
for (int n = 0; n < inc; n++)
{
// Executes InsertSort logic in every splitted array,
// Of course, this area can using other sort algorithms to implement, I think
for (int i = 1; i < arr.Length / inc; i++)
{
int j = i;
int temp = arr[j * inc + n];
while (j > 0 && temp < arr[(j - 1) * inc + n])
{
arr[j * inc + n] = arr[(j - 1) * inc + n];
j--;
}
arr[j * inc + n] = temp;
}
}
// subtract half of the Incremental till the Incremental equals 1
inc = inc / 2;
}
}
/// Shell sort O((nlogn)^2)
/// </summary>
/// <param name="arr"></param>
public static void ShellSort(int[] arr)
{
if (arr == null)
{
throw new ArgumentNullException();
}
// Initializes the Incremental
int inc = arr.Length / 2;
while (inc > 0)
{
// Splits the array to arrays as many as number of Incremental.
// Members of index(i * inc + n) of the original array are in one splitted array.
for (int n = 0; n < inc; n++)
{
// Executes InsertSort logic in every splitted array,
// Of course, this area can using other sort algorithms to implement, I think
for (int i = 1; i < arr.Length / inc; i++)
{
int j = i;
int temp = arr[j * inc + n];
while (j > 0 && temp < arr[(j - 1) * inc + n])
{
arr[j * inc + n] = arr[(j - 1) * inc + n];
j--;
}
arr[j * inc + n] = temp;
}
}
// subtract half of the Incremental till the Incremental equals 1
inc = inc / 2;
}
}
这个算法的排序效率确实很高,据我测试怎么比QuickSort的还略快。
理论上快排的时间复杂度为O(nlogn),而希尔排序为O((nlogn)^2)。这就有点理论和实际的差距了吗。
快排的思想就是:
1)对整个数组(arr)取Left=0,Right=arr.Length-1
2)在(Left,Right)中取一个Key值(比如arr[Left]),也可以称作哨站,用两个index(i=Left,j=Right),i从左往右遍历,j从右往左遍历,找到两个值(arr[i]>=key,arr[j]<=key)
3)然后交换这两个值,知道i>=j结束一轮排序。
4)再递归取新的(Left=0,Right=i-1 )和(Left=i+1,Right=Right)两个分组进行2),3), 直到i-1<=Left 和 i+1>=Right, 结束
代码快排如下:
/// <summary>
/// Quick sort, Expect time O(nlogn), bad case O(n^2)
/// </summary>
/// <param name="arr"></param>
public static void QuickSort(int[] arr)
{
if (arr == null)
{
throw new ArgumentNullException();
}
if (arr.Length > 1)
{
QuickSortRecursive(arr, 0, arr.Length - 1);
}
}
private static void QuickSortRecursive(int[] arr, int left, int right)
{
// Assumption the left first value is the key value of the splitted array.
int key = arr[left];
int i = left;
int j = right;
do
{
// Find index of the array to make sure the data of the index not less than the key, from left to right.
while (i < right && arr[i] < key)
{
i++;
}
// Find index of the array to make sure the data of the index not greater than the key, from right to left.
while (j > 0 && arr[j] > key)
{
j--;
}
// if i less than j, than exchange values of the two indexes.
if (i < j)
{
// if the value of both indexes are equivalency,that means they equal to the value of key, so they not need be exchanged.
if (arr[i] == arr[j])
{
i++;
continue;
}
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
} while (i < j);
if (i - 1 > left)
{
// Sort left part of the array
QuickSortRecursive(arr, left, i - 1);
}
if (j + 1 < right)
{
// Sort right part of the array
QuickSortRecursive(arr, j + 1, right);
}
}
/// Quick sort, Expect time O(nlogn), bad case O(n^2)
/// </summary>
/// <param name="arr"></param>
public static void QuickSort(int[] arr)
{
if (arr == null)
{
throw new ArgumentNullException();
}
if (arr.Length > 1)
{
QuickSortRecursive(arr, 0, arr.Length - 1);
}
}
private static void QuickSortRecursive(int[] arr, int left, int right)
{
// Assumption the left first value is the key value of the splitted array.
int key = arr[left];
int i = left;
int j = right;
do
{
// Find index of the array to make sure the data of the index not less than the key, from left to right.
while (i < right && arr[i] < key)
{
i++;
}
// Find index of the array to make sure the data of the index not greater than the key, from right to left.
while (j > 0 && arr[j] > key)
{
j--;
}
// if i less than j, than exchange values of the two indexes.
if (i < j)
{
// if the value of both indexes are equivalency,that means they equal to the value of key, so they not need be exchanged.
if (arr[i] == arr[j])
{
i++;
continue;
}
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
} while (i < j);
if (i - 1 > left)
{
// Sort left part of the array
QuickSortRecursive(arr, left, i - 1);
}
if (j + 1 < right)
{
// Sort right part of the array
QuickSortRecursive(arr, j + 1, right);
}
}