What is the most elegant way of shell sorting (comb / diminishing increment sorting) in C#?
Is there a better way to shell sort using C#?
// array of integers to hold values
private int[] a = new int[100];
// number of elements in array
private int count;
// Shell Sort Algorithm
public void sortArray()
{
int i, j, increment, temp;
increment = 3;
while( increment > 0 )
{
for( i=0; i < count; i++ )
{
j = i;
temp = a[i];
while( (j >= increment) && (a[j-increment] > temp) )
{
a[j] = a[j - increment];
j = j - increment;
}
a[j] = temp;
}
if( increment/2 != 0 )
{
increment = increment/2;
}
else if( increment == 1 )
{
increment = 0;
}
else
{
increment = 1;
}
}
}
By the way, I'm wondering because I have some different examples of 'elegant' sorts in different languages (like bubble sorting in C# and F#), and I'm comparing them. In real life, I'd probably use the following most of the time in C#:
Array.Sort( object[] )
I don't care if these are 'academic' and non-pragmatic patterns. You can down-mod me into obl开发者_Python百科ivion if you want :)
KA
Improvements you could very easily make:
- Don't keep state in the object. This should be easy to do just with local variables.
- Use conventional C# names like
ShellSort
rather thanshellSort
- Use meaningful names like
count
instead ofx
Use the conditional operator, e.g.
// This replaces your last 12 lines int halfIncrement = increment / 2; increment = halfIncrement != 0 ? halfIncrement : 1 - increment;
Make the code generic - why restrict yourself to integers?
- Make your method take the data in question, and make it an
IList<T>
- Make the sort order arbitrary via an
IComparer<T>
, providing an overload which uses the default. - Declare variables as late as possible to reduce their scope and improve readability
Very little of this is actually sort-related - I haven't verified whether your code is actually a legitimate shell sort or not...
In my test this is 75% to %90 faster than array sort with the same System.Func comparer. I use it to sort a custom struct. you can easily modify it to sort classes.
public class DualQuickSort<T> where T : struct
{
private readonly System.Func<T, T, int> comparer;
public DualQuickSort(System.Func<T, T, int> comparer)
{
this.comparer = comparer;
}
public DualQuickSort(IComparer<T> comparer)
: this(comparer.Compare)
{
}
public void Sort(T[] a)
{
Sort(a, 0, a.Length);
}
public void Sort(T[] a, int fromIndex, int toIndex)
{
RangeCheck(a.Length, fromIndex, toIndex);
DualPivotQuicksort(a, fromIndex, toIndex - 1, 3);
}
private static void RangeCheck(int length, int fromIndex, int toIndex)
{
if (fromIndex > toIndex)
{
throw new ArgumentException("fromIndex > toIndex");
}
if (fromIndex < 0)
{
throw new IndexOutOfRangeException(fromIndex + " is less than 0");
}
if (toIndex > length)
{
throw new IndexOutOfRangeException(toIndex + " is greater than " + fromIndex);
}
}
private static void Swap(T[] a, int i, int j)
{
var temp = a[i];
a[i] = a[j];
a[j] = temp;
}
private void DualPivotQuicksort(T[] a, int left, int right, int div)
{
var len = right - left;
if (len < 27)
{ // insertion sort for tiny array
for (var i = left + 1; i <= right; i++)
{
for (var j = i; j > left && comparer(a[j] , a[j - 1])==-1; j--)
{
Swap(a, j, j - 1);
}
}
return;
}
var third = len / div;
// "medians"
var m1 = left + third;
var m2 = right - third;
if (m1 <= left)
{
m1 = left + 1;
}
if (m2 >= right)
{
m2 = right - 1;
}
if (comparer(a[m1] , a[m2])==-1)
{
Swap(a, m1, left);
Swap(a, m2, right);
}
else
{
Swap(a, m1, right);
Swap(a, m2, left);
}
// pivots
var pivot1 = a[left];
var pivot2 = a[right];
// pointers
var less = left + 1;
var great = right - 1;
// sorting
for (var k = less; k <= great; k++)
{
if (comparer(a[k] , pivot1)==-1)
{
Swap(a, k, less++);
}
else if (comparer(a[k], pivot2) == 1)
{
while (k < great && comparer(a[great] , pivot2)==1)
{
great--;
}
Swap(a, k, great--);
if (comparer(a[k], pivot1) == -1)
{
Swap(a, k, less++);
}
}
}
// Swaps
var dist = great - less;
if (dist < 13)
{
div++;
}
Swap(a, less - 1, left);
Swap(a, great + 1, right);
// subarrays
DualPivotQuicksort(a, left, less - 2, div);
DualPivotQuicksort(a, great + 2, right, div);
// equal elements
if (dist > len - 13 && comparer(pivot1,pivot2)!=0)
{
for (int k = less; k <= great; k++)
{
if (comparer(a[k] , pivot1)==0)
{
Swap(a, k, less++);
}
else if (comparer(a[k], pivot2) == 0)
{
Swap(a, k, great--);
if (comparer(a[k], pivot1) == 0)
{
Swap(a, k, less++);
}
}
}
}
// subarray
if (comparer(pivot1 , pivot2)==-1)
{
DualPivotQuicksort(a, less, great, div);
}
}
}
Please refer below code. Automatically it will calculate the distance and distance will be calculated on consideration of maximum three lists would generate.
using System;
namespace ShellSortExample
{
class Program
{
static void Main(string[] args)
{
int[] num = { 70, 30, 40, 10, 80, 20, 90, 110, 75, 60, 45, 199, 35, 2, 31, 22, 1, 11, 211, 100, 23, 55, 50 };
WriteList("Unsorted List", num);
int distance = 0;
// Generate the distance as per the length of the list
//It will consider maximum 3 list should generate
for (int i = 1; i < num.Length; i++)
{
var div = num.Length / i;
// Maximum 3 list should be generate
if (div <= 3)
{
distance = i;
break;
}
}
// Do the grouping and Apply insertion sort
for (int i = distance; i > 0; i--)
{
int tmpListVal = 0;
for (int j = i; j < num.Length;)
{
int tmpVal = num[j];
//Applying insertion sort with each shell/group element
for (int k = j - i; k >= 0; k -= i)
{
if (k <= num.Length - 1 && tmpVal < num[k])
{
num[k + i] = num[k];
num[k] = tmpVal;
}
}
if (j + i >= num.Length)
{
j = tmpListVal + 1;
tmpListVal = j;
if (j == i)
{
break;
}
}
else
{
j += i;
}
}
}
WriteList("After applied shell sort",num);
}
static void WriteList(string msg, int[] list)
{
Console.WriteLine($"{msg} : ");
for (int i = 0; i < list.Length; i++)
{
Console.Write($" {list[i]}");
}
Console.Write("\r\n");
}
}
}
精彩评论