using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Helper
{
public class SortHelper<T> where T : IComparable
{
/// <summary>
/// 交换两个变量值
/// </summary>
/// <param name="a">变量1</param>
/// <param name="b">变量2</param>
public static void Swap(ref T a,ref T b) {
T temp = a;
a = b;
b = temp;
}
/// <summary>
/// * * * * 冒泡排序 * * * *
/// <para>排序原理:相邻比较,大的数往上升</para>
/// <para> 1丶相邻的数进行比较,前面的数比后面的数大,则交换,直接所有数比较完毕,最大的数会占据最后面那个位置</para>
/// <para> 2、除了最大的数,其他相邻的数继续比较交换,找第二大的数</para>
/// <para> 3、除了最大,第二大的数,其他相邻的数继续比较交换,找第三大的数</para>
/// <para> 4、除了...的数,其他相邻的数继续比较交换,找...的数</para>
/// <para> 5、直到找到剩下一个需要比较的数为止</para>
/// <para></para>
/// <para>语法解析</para>
/// <para> 1、外层循环是比较的轮数(开始=0 结束=数组长度-1)</para>
/// <para> 2、内层循环是每轮比较的次数(开始=0 结束=数组长度-1-i)</para>
/// <para> 3、相邻的数比较,只跟内层循环的值有关系,即j与j+1的数组元素比较,与外层循环的i无关</para>
/// </summary>
/// <param name="arr">要排序的数组</param>
public static void Bubble(T[] arr) {
for (int i = 0; i < arr.Length-1; i++) {
for (int j = 0; j < arr.Length - 1 - i; j++) {
if (arr[j].CompareTo(arr[j+1])>0) {
Swap(ref arr[j],ref arr[j + 1]);
}
}
}
}
/// <summary>
/// * * * * 选择排序 * * * *
/// <para>排序原理:把数组每个位置进行打擂台,获胜者则能占据位置</para>
/// <para> 1丶把数组第一个位置当成擂台,默认为第一个数组元素为擂主,其他逐个进行打擂,胜者作为擂主,败者下台(数组元素交换)</para>
/// <para> 2、把数组第二个位置当成擂台,剩余的没有占据擂台的人继续打擂...</para>
/// <para> 3、把数组第...个位置当成擂台,剩余的没有占据擂台的人继续打擂...</para>
/// <para> 4、直到找到剩下一个没有占据擂台的数组元素(剩下那个数组位置就是它的了,不用比较)</para>
/// <para></para>
/// <para>语法解析</para>
/// <para> 1、外层循环是打擂的位置编号(开始=0 结束=数组长度-1)</para>
/// <para> 2、内层循环是还没有占据擂台的人(开始=i+1 结束=数组长度)</para>
/// <para> 3、擂台上的人跟其他剩下的人进行比较,所以是i跟j比较</para>
/// </summary>
/// <param name="arr">要排序的数组</param>
public static void Choice(T[] arr) {
for (int i = 0; i < arr.Length - 1; i++) {
for (int j = i + 1; j < arr.Length; j++) {
if (arr[i].CompareTo(arr[j]) > 0) {
Swap(ref arr[i],ref arr[j]);
}
}
}
}
/// <summary>
/// * * * * 插入排序 * * * *
/// <para>排序原理:把数组分成有序序列和无序序列,把无序序列的数逐一跟有序序列的数进行比较插入</para>
/// <para> 1丶把数组的第一个元素放到有序序列中,其他放到无序序列中</para>
/// <para> 2、把无序序列取出第一个数跟有序序列逐一比较,插入其位置,即把该数插入到有序列中</para>
/// <para> 3丶重复第2步,直到无序序列为空为止</para>
/// <para></para>
/// <para>语法解析</para>
/// <para> 1、外层循环是无序序列的元素(开始=1 结束=数组长度)</para>
/// <para> 2、内层循环是有序序列的元素(开始=i-1 结束=j>0)</para>
/// <para> 3、内层每次循环比较符合条件都要把当前有序序列比较的元素进行移动,然后无序元素占领该位置</para>
/// </summary>
///
/// <param name="arr">要排序的数组</param>
public static void Insert(T[] arr) {
for (int i = 1; i < arr.Length; i++) {
//这里用临时变量存储当前需要比较的无序元素的值,因为元素都在移动,下标的变化导致决定元素值也在变化
T temp=arr[i];
//无序元素与有序元素从大到小比较(顺序排序)
for (int j = i - 1; j >= 0&&temp.CompareTo(arr[j])<0; j--) {
//往后移动一格,空出当前格供于无序元素插入(可能会移动多个元素)
arr[j + 1] = arr[j];
//这句跟下面那句代码效果一样
//arr[j]=temp;
}
//把无序元素插入到合适位置(这里j+1是因为上面循环跳出前减了1)
arr[j+1] = temp;
}
}
/// <summary>
/// * * * * 快速排序 * * * *
/// <para>排序原理:把数组按指定标准值分割成左小右大的两部分,分割后的两部分再按照指定标准值进行递归分割,直到不可再分割为止</para>
/// <para> 1丶把分割数组中的第一个元素当成标准值</para>
/// <para> 2、把小于标准值的放到左边,把大于标准值大放到右边,分成两组分割数组</para>
/// <para> 3丶重复1丶2步,直到不能再分割为止(分割数组左边=右边)</para>
/// </summary>
///
/// <param name="arr">要排序的数组</param>
/// <param name="l">分割数组的左边下标</param>
/// <param name="r">分割数组的右边下标</param>
public static void Quick(T[] arr, int l, int r) {
//如果左边>=右边则结束当前递归方法的调用
if (l >= r) return;
//保留当前左边值和右边值,后面需要用到
int ll = l;
int rr = r;
//以左边第一个值作为标准值(最左边是空出的位置)
T temp = arr[l];
//把数组分割成左小右大两部分,分割过程中必须符合递归方法调用的条件
while (l < r) {
//这里轮流找右边与左边符合移动条件的元素,把元素插入到相应的空格中(右插左空,空出右,左插右空,空出左,一直循环操作)
//先从右边开始查找比标准值小的数,每次循环如果比标准值大,r就会-1,标识该下标位置已经找过了
//如果比表标准值小,则结束循环,进行交换
while (l < r && arr[r].CompareTo(temp) >= 0) {
r--;
}
//把从右边找到的比标准值小的值赋值到左边
//当第一次找到符合移动条件时,会占领最左边空出的位置,而符合移动条件的元素位置也空出来了
arr[l] = arr[r];
//从左边开始查找比标准值大的数,每次循环如果比标准值小,l就会+1,标识该下标位置已经找过了
//如果比表标准值大,则结束循环,进行交换
while (l < r && arr[l].CompareTo(temp) <= 0) {
l++;
}
//把从左边边找到的比标准值大的值赋值到右边
//占领右边空出的位置,空出左边的位置
arr[r] = arr[l];
}
//把标准值赋值到两个分割数组中间的位置,这时候l=r
//占领最后左边空出的位置
arr[l] = temp;
//递归分割左边
Quick(arr, ll, l - 1);
//递归分割右边
Quick(arr, r + 1, rr);
}
}
}