C#实现选择排序与插入排序:代码、原理与性能对比
本文深入解析C#中选择排序与插入排序的实现原理,提供带性能计时的标准代码示例。通过对比分析,明确阐述两者在数据量较大或特定有序状态下的表现差异,帮助开发者根据实际场景选择最优排序策略。
选择排序实现与原理
选择排序(Selection Sort)的基本思想是:每次从未排序区间中找到最小(或最大)元素,存放到排序区间的末尾。对于升序排列,第一轮找出整个数组的最小值并与第一个位置交换;第二轮找出剩余元素的最小值与第二个位置交换,依此类推。
该算法的关键特性在于数据移动次数较少。在内循环中只进行比较,确定最小元素的索引后,才在循环外进行一次交换。这意味着对于长度为 N 的数组,无论初始顺序如何,交换总次数固定为 N 次(准确说是 N-1 次),而比较次数固定为 N(N-1)/2 次。这使得选择排序的时间效率完全取决于比较操作,且对已有序数组的处理时间与随机数组无异。
第1步:编写选择排序类
目的:创建一个包含标准选择排序逻辑的 C# 类,并内置性能计时功能以便后续对比。
入口:新建一个名为 SelectionSort.cs 的文件。
动作:实现 IComparable[] 类型的排序方法,使用 Stopwatch 记录运行耗时。
using System;
public class Selection : BaseSort
{
public static long usedTimes = 0;
public Selection()
{
}
public static void Sort(IComparable[] a)
{
Stopwatch timer = new Stopwatch();
timer.Start();
// 外层循环控制已排序区的边界
for (var i = 0; i < a.Length - 1; i++)
{
int minIndex = i;
// 内层循环寻找未排序区中的最小值索引
for (var j = i + 1; j < a.Length; j++)
{
if (Less(a[j], a[minIndex]))
{
minIndex = j;
}
}
// 如果找到的最小值不是当前起始位置,则交换
if (minIndex != i)
{
Exch(a, minIndex, i);
}
}
timer.Stop();
usedTimes = timer.ElapsedMilliseconds;
}
// 辅助方法:比较大小
private static bool Less(IComparable v, IComparable w)
{
return v.CompareTo(w) < 0;
}
// 辅助方法:交换元素
private static void Exch(IComparable[] a, int i, int j)
{
IComparable t = a[i];
a[i] = a[j];
a[j] = t;
}
}
// 假设 BaseSort 包含基础工具类,此处仅为结构示意
public abstract class BaseSort
{
protected static bool Less(IComparable v, IComparable w) { throw new NotImplementedException(); }
protected static void Exch(IComparable[] a, int i, int j) { throw new NotImplementedException(); }
}插入排序实现与原理
插入排序(Insertion Sort)将数组分为“已排序”和“未排序”两部分。初始时,认为第一个元素是已排序的。随后,依次取出未排序部分的第一个元素,在已排序序列中从后向前扫描,找到合适位置并插入。
与选择排序不同,插入排序的性能高度依赖于输入的初始顺序: 1. 最好情况(数组已有序):只需进行 N-1 次比较,0 次交换,时间复杂度为 O(N)。 2. 最坏情况(数组逆序):需要 ~N²/2 次比较和 ~N²/2 次交换,时间复杂度为 O(N²)。 3. 平均情况:需要 ~N²/4 次比较和交换。但在处理“部分有序”数组(如倒置数量较少、每个元素距离最终位置不远)时,效率极高,甚至优于许多高级排序算法。
第2步:编写插入排序类
目的:实现高效的插入排序,展示如何通过减少数组访问次数来优化性能。
入口:新建 InsertionSort.cs 文件。
动作:提供两种常见实现方式——直接交换法(代码简洁但访问频率高)和位移法(代码稍长但访问频率低,通常更快)。
using System;
public class Insertion : BaseSort
{
public static long usedTimes = 0;
public static void Sort(IComparable[] a)
{
Stopwatch timer = new Stopwatch();
timer.Start();
/*
* 方式一:直接交换法
* 逻辑简单,类似冒泡排序的思想,但只向右移动一步。
* 缺点是频繁访问数组,开销较大。
*/
for (var i = 1; i < a.Length; i++)
{
for (var j = i; j > 0 && Less(a[j], a[j - 1]); j--)
{
Exch(a, j, j - 1);
}
}
/*
* 方式二:位移优化法(推荐)
* 将当前元素暂存到临时变量 temp,然后将其左侧较大的元素全部右移一位,
* 最后将 temp 放入正确位置。减少了大量的数组写入操作。
*/
// int N = a.Length;
// for (int i = 1; i < N; i++)
// {
// IComparable temp = a[i];
// int j;
// // 寻找插入位置并右移元素
// for (j = i - 1; j >= 0 && Less(temp, a[j]); j--)
// {
// a[j + 1] = a[j];
// }
// // 放置临时变量到正确位置
// a[j + 1] = temp;
// }
timer.Stop();
usedTimes = timer.ElapsedMilliseconds;
}
}
// 同样需要 Less 和 Exch 辅助方法,若选用方式二可省略 Exch两种算法的对比与总结
在实际应用中,插入排序的表现通常优于选择排序,主要原因如下:
- 自适应能力:插入排序能利用数据的初始有序性。对于几乎有序的数组,其速度接近线性;而选择排序无论输入如何,始终执行平方级的比较。
- 操作开销:虽然两者的最坏时间复杂度均为 O(N²),但插入排序通过“位移”而非频繁的“两两交换”,减少了内存写操作的次数。一般而言,插入排序的运行时间约为选择排序的一半。
- 适用场景:当数据规模较小(N 较小)或数据基本有序时,插入排序是极佳的选择。对于大规模随机数据,通常会结合其他高级排序算法(如快速排序或归并排序)使用,或在递归底层切换回插入排序以优化常数因子。
以上就是C#实现选择排序与插入排序的详细对比与代码实践,更多关于C#算法优化的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













