您的位置:首页 > 编程语言 > C#

常见排序算法的C#实现

2017-02-21 16:59 155 查看
排序算法常见的有直接排序、冒泡排序、快速排序、基数排序、归并排序等,下面是实现的代码,仅供参考。

#region DirectSort
/// <summary>
/// 直接排序.
/// 第一次从R[0]~R[n-1]中选取最小值,与R[0]交换,
/// 第二次从R[1]~R[n-1]中选取最小值,与R[1]交换,....,
/// 第i次从R[i-1]~R[n-1]中选取最小值,与R[i-1]交换,.....,
/// 第n-1次从R[n-2]~R[n-1]中选取最小值,与R[n-2]交换,
/// 总共通过n-1次,得到一个按排序码从小到大排列的有序序列·
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static int DirectSort(int[] data)
{
int min = 0;
int k = 0;
int count = 0;
for (int i = 0; i < data.Length; i++)
{
min = data[i];
k = i;
for (int j = i; j < data.Length; j++)
{
if (min > data[j])
{
min = data[j];
k = j;
}
count++;
}
data[k] = data[i];
data[i] = min;
}
return count;
}
#endregion

#region BubbleSort
/// <summary>
/// 冒泡排序.
/// 重复地走访过要排序的数列,一次比较两个元素,
/// 如果他们的顺序错误就把他们交换过来。
/// 走访数列的工作是重复地进行直到没有再需要交换,
/// 也就是说该数列已经排序完成。
/// 这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端,故名。
/// </summary>
/// <param name="data"></param>
private static int BubbleSort(int[] data)
{
int min = 0;
int count = 0;
for (int i = 0; i < data.Length; i++)
{
for (int j = 0; j < data.Length - 1 - i; j++)
{
if (data[j] > data[j + 1])
{
min = data[j + 1];
data[j + 1] = data[j];
data[j] = min;
}
count++;
}
}
return count;
}
#endregion

#region QuickSort
/// <summary>
/// 快速排序.
/// 通过一趟排序将要排序的数据分割成独立的两部分,
/// 其中一部分的所有数据都比另外一部分的所有数据都要小,
/// 然后再按此方法对这两部分数据分别进行快速排序,
/// 整个排序过程可以递归进行,以此达到整个数据变成有序序列。
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static int QuickSort(int[] data)
{
int count = 0;

QuickSortInner(data, 0, data.Length - 1, ref count);

return count;
}

private static void QuickSortInner(int[] data, int left, int right, ref int count)
{
if (left >= right) { return; }

//完成一次单元排序
int middle = QuickSortUnit(data, left, right, ref count);

//对左边单元进行排序
QuickSortInner(data, left, middle - 1, ref count);

//对右边单元进行排序
QuickSortInner(data, middle + 1, right, ref count);
}

private static int QuickSortUnit(int[] data, int left, int right, ref int count)
{
int key = data[left];
while (left < right)
{
//自右端向左端查找小于key的值
for (; ; right--)
{
if (data[right] < key || right <= left)
{//右边有小于key的值,直接将该值放到左边,
//此时right位置的空间空出,留作放置从左边比较出的大于key的值。
//如果右索引已经到达了左边,right==left,data[left] = data[right]是同数据交换,不受影响.
data[left] = data[right];
count++;
break;
}
}

//自左端向右端查找大于key的值
for (; ; left++)
{
if (data[left] > key || left >= right)
{//左边有大于key的值,直接将该值放到右边,
//此时left位置的空间空出,留作放置从右边比较出的小于key的值,或者用来最后放置key.
//如果左索引已经到达了右边,left==right,data[right] = data[left]是同数据交换,不受影响.
data[right] = data[left];
count++;
break;
}
}
}
data[left] = key;
return right;
}
#endregion

#region RadixSort
/// <summary>
/// 基数排序(此处采用LSD法).
/// 属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,
/// 顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,
/// 基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,
/// 在某些时候,基数排序法的效率高于其它的稳定性排序法。
/// ==基数排序又分MSD和LSD。
/// ==最高位优先(Most Significant Digit first)法,简称MSD法:
/// 先按k1排序分组,同一组中记录,关键码k1相等,再对各组按k2排序分成子组,
/// 之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd对各子组排序后。
/// 再将各组连接起来,便得到一个有序序列。
/// ==最低位优先(Least Significant Digit first)法,简称LSD法:
/// 先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static int RadixSort(int[] data)
{
var bucketList = new List<List<int>>();
for (int i = 0; i < 10; i++)
{
bucketList.Add(new List<int>());
}
int count = 0;
int positionValue = 0;
int maxPosition = 1;
//计算最大位数
for (int i = 0; i < data.Length; i++)
{
var str = data[i].ToString();
if (str.Length > maxPosition)
{
maxPosition = str.Length;
}
}

for (int position = 0; position < maxPosition; position++)
{
//分桶
for (int i = 0; i < data.Length; i++)
{
positionValue = 0;
var str = data[i].ToString();
var index = (str.Length - 1) - position;// 计算出对应位数的字符串的值
if (index >= 0 && str.Length > index)
{
positionValue = int.Parse(str.Substring(index, 1));
}
bucketList[positionValue].Add(data[i]);
count++;
}

//合并桶
int j = 0;
foreach (var bucket in bucketList)
{
foreach (var val in bucket)
{
data[j++] = val;
}
//清空桶
bucket.Clear();
}
}
return count;
}
#endregion

#region MergeSort
/// <summary>
/// 归并排序.
/// 该算法是建立在归并操作上的一种有效的排序算法,
/// 该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
/// 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
/// 若将两个有序表合并成一个有序表,称为二路归并。
/// ===归并过程为:
/// 比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;
/// 否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,
/// 然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。
/// 归并排序的算法我们通常用递归实现,先把待排序区间[s, t]以中点二分,接着把左边子区间排序,
/// 再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s, t]。
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static int MergeSort(int[] data)
{
int count = 0;
MergeSortInner(data, 0, 1, ref count);
return count;
}

/// <summary>
/// 二路归并.
/// 原理:将两个有序表合并成一个有序表
/// </summary>
/// <param name="data"></param>
/// <param name="firstStart">第一个有序表的起始下标</param>
/// <param name="secondStart">第二个有序表的起始下标</param>
/// <param name="secondTail">第二个有序表的结束下标</param>
private static void MergeSortUnit(int[] data, int firstStart, int secondStart, int secondTail, ref int count)
{
int[] newArr = new int[secondTail - firstStart + 1];
int firstIndex = firstStart, secondIndex = secondStart, newIndex = 0;
while (firstIndex < secondStart && secondIndex <= secondTail)
{//自小而大合并到一个序列中
if (data[firstIndex] <= data[secondIndex])
{
newArr[newIndex] = data[firstIndex++];
}
else
{
newArr[newIndex] = data[secondIndex++];
}
newIndex++;
count++;
}

for (; firstIndex < secondStart; firstIndex++, newIndex++)
{
newArr[newIndex] = data[firstIndex];
count++;
}

for (; secondIndex <= secondTail; secondIndex++, newIndex++)
{
newArr[newIndex] = data[secondIndex];
count++;
}
Array.Copy(newArr, 0, data, firstStart, newArr.Length);
}

/// <summary>
/// 归并排序
/// </summary>
/// <param name="data"></param>
/// <param name="firstStart">第一个序列的起始索引</param>
/// <param name="len">每次归并的有序集合的长度</param>
private static void MergeSortInner(int[] data, int firstStart, int len, ref int count)
{
int size = data.Length;

//归并排序具体工作原理如下(假设序列共有n个元素):
//将序列每相邻两个数字进行归并操作(merge),形成floor(n/2)个序列,排序后每个序列包含两个元素
//将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
//将上述序列继续归并,形成floor(n/K)个序列(其中k为2的t次方),直到所有元素排序完毕。
int k = len << 1;

//新标准序列的组数
int groupStandard = size / k;

//除了标准序列外剩余的元素个数
int leftElement = size % k; // size & (k - 1);

//归并到只剩一个有序集合的时候结束算法
if (groupStandard == 0)
{
return;
}

int secondStart = 0;
int secondTail = 0;

//进行一趟归并排序
for (int i = 0; i < groupStandard; i++)
{
firstStart = i * 2 * len;
secondStart = firstStart + len;
secondTail = (len << 1) + firstStart - 1;
MergeSortUnit(data, firstStart, secondStart, secondTail, ref count);
}

//将剩下的数和倒数第一个有序集合归并
if (leftElement != 0)
{
firstStart = size - leftElement - 2 * len;
secondStart = size - leftElement;
secondTail = size - 1;
MergeSortUnit(data, firstStart, secondStart, secondTail, ref count);
}

//递归执行下一趟归并排序
MergeSortInner(data, 0, 2 * len, ref count);
}

#endregion以上各排序算法的解释主要来自于百度,仅供参考。
static void Main(string[] args)
{
var data = new int[] { 123, 45, 21, 456, 98, 40, 32, 1435, 76, 485, 89, 876, 908, 345, 123 };
Console.WriteLine(string.Join(",", data));

//直接排序
Console.WriteLine("=======直接排序======");
var dataTemp = new int[data.Length];
data.CopyTo(dataTemp, 0);
var count = DirectSort(dataTemp);
Console.WriteLine(count + "次=>" + string.Join(",", dataTemp));

//冒泡排序
Console.WriteLine("=======冒泡排序======");
dataTemp = new int[data.Length];
data.CopyTo(dataTemp, 0);
count = BubbleSort(dataTemp);
Console.WriteLine(count + "次=>" + string.Join(",", dataTemp));

//快速排序
Console.WriteLine("=======快速排序======");
dataTemp = new int[data.Length];
data.CopyTo(dataTemp, 0);
count = QuickSort(dataTemp);
Console.WriteLine(count + "次=>" + string.Join(",", dataTemp));

//基数排序
Console.WriteLine("=======基数排序======");
dataTemp = new int[data.Length];
data.CopyTo(dataTemp, 0);
count = RadixSort(dataTemp);
Console.WriteLine(count + "次=>" + string.Join(",", dataTemp));

//归并排序
Console.WriteLine("=======归并排序======");
dataTemp = new int[data.Length];
data.CopyTo(dataTemp, 0);
count = MergeSort(dataTemp);
Console.WriteLine(count + "次=>" + string.Join(",", dataTemp));

Console.ReadLine();
}输出结果如下图



转载请注明出处
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息