1.冒泡排序
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
class BubbleSort
{
public static void Sort(int[] arr)
{
int n = arr.Length;
//完成n次冒泡排序
for (int i = 0; i < n; i++)
{
//对两个相邻元素进行比较,将大的元素放在后面,直到完成整个冒泡操作,冒泡出一个最大值。
//小于n-1 只考察到倒数第二个位置,最后一个元素不能向下比较
//减去i 不需要对已经排好序的元素再次进行比较
for (int j = 0; j < n - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
Swap(arr, j, j + 1);
}
}
}
//索引为i的元素和j的元素进行交换
private static void Swap(int[] arr, int i, int j)
{
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
测试:
class Program
{
static void Main(string[] args)
{
int[] a = { 4, 3, 5, 2, 1, 0 };
BubbleSort.Sort(a);
for (int i = 0; i < a.Length; i++)
{
Console.WriteLine(a[i]);
}
Console.Read();
}
}
2.泛型冒泡排序
class BubbleSortGeneric
{
public static void Sort<E>(E[] arr) where E : IComparable<E>
{
int n = arr.Length;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n - 1 - i; j++)
{
if (arr[j].CompareTo(arr[j + 1]) > 0)
Swap(arr, j, j + 1);
}
}
}
private static void Swap<E>(E[] arr, int i, int j)
{
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
测试:
class Program
{
static void Main(string[] args)
{
int[] a = { 4, 3, 5, 2, 1, 0 };
char[] a2 = { 'C', 'A', 'D','B', 'G', 'F', 'E' };
float[] a3 = { 0.21f, 0.10f, 0.78f, 0.15f, 0.17f };
BubbleSortGeneric.Sort(a2);
for (int i = 0; i < a2.Length; i++)
Console.WriteLine(a2[i]);
Console.Read();
}
}
创建日期类,存储年月日以及节日:
class Date:IComparable<Date>
{
private int year;
private int month;
private int day;
private string happen;
public Date(int year, int month, int day, string happen)
{
this.year = year;
this.month = month;
this.day = day;
this.happen = happen;
}
public int CompareTo(Date other)
{
if (this.year > other.year) return 1; //年份大的排在后面
if (this.year < other.year) return -1; //年份小的排在前面
if (this.month > other.month) return 1;
if (this.month < other.month) return -1;
if (this.day > other.day) return 1;
if (this.day < other.day) return -1;
return 0; //都相同,排在一起
}
public override string ToString()
{
return year + "/" + month + "/" + day + ": " + happen;
}
}
测试:
class Program
{
static void Main(string[] args)
{
Date[] dates =
{
new Date(2020,7,7,"七夕节"),
new Date(2020,8,15,"中秋节"),
new Date(2020,1,1,"元旦节"),
new Date(2020,3,8,"妇女节"),
new Date(2020,4,4,"清明节"),
new Date(2020,5,1,"劳动节"),
new Date(2020,9,10,"教师节"),
new Date(2020,1,25,"春节"),
new Date(2020,2,14,"情人节"),
new Date(2020,10,1,"国庆节"),
new Date(2020,12,25,"圣诞节"),
new Date(2020,6,1,"儿童节")
};
BubbleSortGeneric.Sort(dates);
for (int i = 0; i < dates.Length; i++)
Console.WriteLine(dates[i]);
Console.Read();
}
}
3.选择排序
- 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
- 再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。
- 以此类推,直到所有元素均排序完毕。
class SelectSort
{
public static void Sort(int[] arr)
{
int n = arr.Length;
for (int i = 0; i < n; i++)
{
int min = i;
for (int j = i + 1; j < n; j++)
{
if (arr[j] < arr[min])
min = j;
}
Swap(arr, i, min);
}
}
public static void Swap(int[] arr, int i, int j)
{
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
测试:
class Program
{
static void Main(string[] args)
{
int[] a = { 4, 3, 5, 2, 1, 0 };
SelectSort.Sort(a);
for (int i = 0; i < a.Length; i++)
Console.WriteLine(a[i]);
Console.Read();
}
}
4.排序算法测试辅助工具类
//排序算法测试辅助工具类
class TestHelper
{
//生成有n个元素的随机数组,每个元素的随机范围为[0,maxValue]
public static int[] RandomArray(int n, int maxValue)
{
Random r = new Random();
int[] arr = new int[n];
for (int i = 0; i < n; i++)
arr[i] = r.Next(maxValue + 1);
return arr;
}
//生成有n个元素的近乎有序数组
public static int[] NearlyOrderedArray(int n, int swapTimes)
{
int[] arr = new int[n];
for (int i = 0; i < n; i++)
arr[i] = i;
Random r = new Random();
for (int i = 0; i < swapTimes; i++)
{
int a = r.Next(n + 1);
int b = r.Next(n + 1);
int t = arr[a];
arr[a] = arr[b];
arr[b] = t;
}
return arr;
}
//判断arr 数组是否有序(从小到大)
private static void IsSorted(int[] arr)
{
for (int i = 0; i < arr.Length - 1; i++)
{
if (arr[i] > arr[i + 1])
throw new ArgumentException("排序失败");
}
}
//拷贝arr数组的所有内容得到相同的数组并返回
public static int[] CopyArray(int[] arr)
{
int n = arr.Length;
int[] temp = new int[n];
for (int i = 0; i < n; i++)
temp[i] = arr[i];
return temp;
}
// 测试sortClassName所对应的排序算法排序arr数组所得到结果的正确性和算法运行时间
//C#的反射机制是高阶的知识点,课程重点是讲解算法,学习要抓大放小,不要纠结它。
public static void TestSort(string sortClassName, int[] arr)
{
Type type = Type.GetType("Sorting." + sortClassName);
MethodInfo sortMethod = type.GetMethod("Sort");
object[] paramsarr = new object[] { arr };
Stopwatch sw = new Stopwatch();
sw.Start();
sortMethod.Invoke(null, paramsarr);
sw.Stop();
IsSorted(arr);
Console.WriteLine(type.Name + " : " + sw.ElapsedMilliseconds + "ms");
}
}
测试5万个随机,随机数的范围为0到5万,对比冒泡排序和选择排序运行速度:
class Program
{
static void Main(string[] args)
{
int N = 50000;
int[] a = TestHelper.RandomArray(N, N);
int[] b = TestHelper.CopyArray(a);
TestHelper.TestSort("BubbleSort", a);
TestHelper.TestSort("SelectSort", b);
Console.Read();
}
}
5.插入排序
假设前面 n-1(其中 n>=2)个数已经是排好顺序的,现将第 n 个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。
class InsertSort
{
public static void Sort(int[] arr)
{
int n = arr.Length;
for (int i = 1; i < n; i++)
{
int e = arr[i];
int j;
//e的合适位置
//不能把变量j写在for循环里面(int j= i),因为for循环结束后还需要使用这个j
for (j = i; j > 0; j--)
{
if (e < arr[j - 1])
arr[j] = arr[j - 1];
else
break;
}
arr[j] = e;
}
}
}
测试10万个随机,随机数的范围为0到10万,对比选择排序和插入排序运行速度:
class Program
{
static void Main(string[] args)
{
int N = 100000;
int[] a = TestHelper.RandomArray(N, N);
int[] b = TestHelper.CopyArray(a);
TestHelper.TestSort("SelectSort", a);
TestHelper.TestSort("InsertSort", b);
Console.Read();
}
}
测试10万个近乎有序数组,数的范围为0到10万,交换次数为100次(随机选择100对元素进行交换,生成的数组当中只有200个元素的位置不正确),对比选择排序和插入排序运行速度:
class Program
{
static void Main(string[] args)
{
int N = 100000;
int[] a = TestHelper.NearlyOrderedArray(N, 100);
int[] b = TestHelper.CopyArray(a);
TestHelper.TestSort("SelectSort", a);
TestHelper.TestSort("InsertSort", b);
Console.Read();
}
}
插入排序执行近乎有序数组排序时,for循环中大部分都break掉了,此时的时间复杂度近似可以看做O(n)。