C#的排序算法

1. 冒泡排序(时间复杂度O(n2))

  分为趟数和次数,元素之间两两比较交换,比较Length-1趟,每趟比较Length-1-i次,每次比较按照升序或降序的规则,交换顺序
趟数:元素个数-1
每趟次数:元素个数-1-排序趟数
数据:9531315612–>1112335569

	int number[10]={9,5,3,1,3,1,5,6,1,2}
	
	for(int i=0;i<number.Length-1;i++)
		{
			for{int j=0;j<number.Length-1-i;i++}
				{
					if(num[j]>num[j+1])//交换顺序
					{
						int temp=0;
						temp=number[j];
						num[j]=num[j+1];
						num[j+1]=temp;
					}
				}
}

2. 选择排序(时间复杂度O(n2))

  每趟拿一个元素与后面的元素比较,找到最大值,记录最大值索引下来,每趟比较结束后在交换原始数据
72 54 59 30 31 78 2 77 82 72–》82 78 77 72 72 59 54 31 30 2

	for(int i=0;i<arr.Length-1;i++)//比较N趟
	{
		int index=i;//记录最大值索引
		for(int j=i+1;j<arr.Length;i++)//每趟比较N次
		{
			if(arr[index]<arr[j])
			{
			index=j;
			}
		}
		//每趟比较完毕后让最大值与arr[i]交换顺序
		int temp=0;
		temp=arr[i];
		arr[i]=arr[index];
		arr[index]=temp;
	}

3.插入排序(时间复杂度O(n2)):

  最好情况的时间复杂度是 O(n),最坏情况的时间复杂度是 O(n2),然而时间复杂度这个指标看的是最坏的情况,而不是最好的情况,所以插入排序的时间复杂度是 O(n2)。
  以升序为例,在数组中依次往后选择,将要插入的数据【未排序】插入到已经排列好的数列中
  思路:取数组第2个数据与第1个数据比较,如果比第1个数据小,则将第2个数据插入到第1个数据的前面,而将原来的第1个数据移到数组的第2个位置,以此类推。
插入排序

	private int[] num = new int[] { 8,2,9,4,3,5,7,1,6};
    public void InsertSort()
    {
        for (int i = 1; i < num.Length; i++)
        {
            int noSortNum = num[i];//记录未排序区的值
            int sortIdx = i-1;//排序区最后一个值得索引
            while (sortIdx >= 0 && num[sortIdx] > noSortNum)//排序区元素 大于 记录的未排序区的元素
            {
                num[sortIdx+1] = num[sortIdx];//满足升序 移动位置
                sortIdx--;
            }
            num[sortIdx+1] = noSortNum;//找到位置 插入未排序的元素 到排序区
        }
    }

4.希尔排序(时间复杂度O(nlogn)):

  希尔排序是插入排序的升级,原理:希尔相对插入增加步长概念,步长将待排序序列分为若干子序列,子序列进行插入排序,实质分组插入排序。
  在极限情况下,可以有效降低普通插入排序的时间复杂度。
希尔

	private int[] num = new int[] { 8,2,9,4,3,5,7,1,6};
    public void HillSort()
    {
        //确认步长 初始步长=数组长度/2  之后每次步长=上次步长基础/2     结束:步长<=0
        for (int setp = num.Length/2; setp >0; setp/=2)
        {
            //执行步长之间的子序列插入排序
            for (int i = setp; i < num.Length; i++)
            {
                int noSortNum = num[i];
                int sortIdx = i-setp;
                while (sortIdx >= 0 && num[sortIdx] > noSortNum)
                {
                    num[sortIdx + setp] = num[sortIdx];//满足升序 移动位置
                    sortIdx -= setp;//一个步长之间的比较
                }
                num[sortIdx + setp] = noSortNum;//找到位置 插入
            }
        }
    }

5.归并排序(时间复杂度O(nlogn)):

  采用分治法,分治策略将原问题划分为n个规模较小而结构与原问题相似的子问题,递归地解决这些子问题,然后在合并其结果。分治模式在每一层递归上都包含三个步骤:分解、解决、合并。
  三个步骤分别为:
    分解:将n个元素分成各含n/2个元素的子序列
    分解:用归并排序法对两个子序列递归的排序
    分解:合并两个已排序的子序列以得到排序结果
  下图是利用归并算法对一个数组进行排序的执行过程
归并排序

  #region 归并排序
    /// <summary>
    /// 左右数组比较合并
    /// </summary>
    public int [] LeftRightSort(int[] leftArr,int [] rightArr)
    {
        //定义新数组 缓存排序后的数据
        int[] arr = new int[leftArr.Length + rightArr.Length];
        int leftIdx = 0;//左数组的索引
        int rightIdx = 0;//右数组的索引
        //比较 填充新数组
        for (int i = 0; i < arr.Length  ; i++)
        {
            if (leftIdx >= leftArr.Length)//左侧数组放完了
            {
                arr[i] = rightArr[rightIdx];
                rightIdx++;
            }
            else if (rightIdx >= rightArr.Length)//右侧数组放完了
            {
                arr[i] = leftArr[leftIdx];
                leftIdx++;
            }
            else if (leftArr[leftIdx] > rightArr[rightIdx])//左侧值 大于 右侧的  将右侧值放入新数组 升序
            {
                arr[i] = rightArr[rightIdx];
                rightIdx++;

            }
            else//右侧值 大于 左侧的  将左侧值放入新数组 升序
            {
                arr[i] = leftArr[leftIdx];
                leftIdx++;
            }
        }
        return arr;
    }

    /// <summary>
    /// 归并排序 递归平分数组,左右比较,递归返回
    /// </summary>
    public int[] RecursionMerge(int [] array)
    {
        if (array.Length < 2)
            return array;

        //将数组分割为 2个数组
        int mid = array.Length / 2;//中间索引
        int[] leftArr = new int[mid];//左边数组
        int[] rightArr = new int[array.Length - mid];//右边数组
        //给左右数组赋值
        for (int i = 0; i < array.Length; i++)
        {
            if (i < mid)
                leftArr[i] = array[i];
            else
                rightArr[i - mid] = array[i];
        }
        //递归再分再排序
        return LeftRightSort(RecursionMerge(leftArr), RecursionMerge(rightArr));
    }
    #endregion

6.快速排序(时间复杂度是 O(nlogn)):

  排序n个元素 要O(nlogn)次比较 最坏的情况需要O(n^2)次计较[不常见] 事实上,快速排序通常比其他O(nlogn)算法快
  算法:
    1.从序列中挑出一个元素,作为基准,通常选第一个或最后一个
    2.把所有比基准小的元素 放在基准前面 比基准大的 放在基准后面 相同数放在任意一边 【分区操作–升序】
    3.对每个分区进行1-2步操作,递归结束条件:区域左边索引>=右边索引 [区域大小0/1] --无法在划分区域
  代码:

using System.Text;
using UnityEngine;

public class Sort : MonoBehaviour
{
    void Start()
    {
        int[] arr = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };
        QuickSort(arr, 0, arr.Length - 1);
        Debug.Log("排序结果");
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < arr.Length; i++)
        {
            sb.Append(arr[i]);
            sb.Append("-");
        }
        Debug.Log(sb.ToString());
    }

    /// <summary>
    /// 快速排序
    /// </summary>
    /// <param name="arr">数组</param>
    /// <param name="left">分区左边索引</param>
    /// <param name="right">分区最后边索引</param>
    public void QuickSort(int [] arr,int left,int right)
    {
        if (left >=right) return;
        int pivot_Idx = Partition(arr, left, right);
        QuickSort(arr, left, pivot_Idx - 1);//对基准左边的分区 再次分区排序
        QuickSort(arr, pivot_Idx +1, right);//对基准右边的分区 再次分区排序
    }

    /// <summary>
    /// 划分区域--升序
    /// </summary>
    private int Partition(int[] arr, int left, int right)
    {
        int pivot = arr[right];//每次都选择最后一个元素作为基准
        int tail = left - 1;//小于基准的数组的 最后有个元素索引

        //遍历基准以外的其他元素
        for (int i = left; i < right; i++)
        {
            if (arr[i] < pivot)//把小于 基准的元素放在前面[按顺序]
            {
                Swap(arr, ++tail, i);
            }
        }
        Swap(arr, tail + 1, right);//把基准 放在 小于基准数组后面 -->实现小于基准的数据往前 大于基准的数据往后
        return tail + 1;//返回基准索引
    }

    /// <summary>
    /// 交换
    /// </summary>
    private void Swap(int[] arr,int i,int j)
    {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

7.堆排序(时间复杂度是O(nlogn)):

  堆排序顾名思义,是利用堆这种数据结构来进行排序的算法,分为大顶堆和小顶堆。
  把大顶堆看成一棵完全二叉树,但是位于堆顶的元素总是整棵树的最大值,每个子节点的值都比父节点小,由于堆要时刻保持这样的规则特性,所以一旦堆里面的数据发生变化,我们必须对堆重新进行一次构建。
  既然堆顶元素永远都是整棵树中的最大值,我们在取值和添加值的时候对堆进行排序。

//堆排序(大顶堆)
public class Heap_Max<T> where T : IHeapItem<T>
{
    /// <summary>
    /// 存储树的元素
    /// </summary>
    private readonly List<T> _items=new List<T>();

    /// <summary>
    /// 当前树的大小
    /// </summary>
    private int _curItemCount;

    /// <summary>
    /// 返回当前元素数量
    /// </summary>
    public int Count => _curItemCount;

    public void Add(T item)
    {
        if (item == null) throw new NullReferenceException("Add item=null");
        //直接放在数组的最后一位
        item.HeapIndex = _curItemCount;
        _items.Add(item);
        _curItemCount++;
        SortUp(item);
    }

    /// <summary>
    /// 获取最大的值
    /// </summary>
    public T GetMax()
    {
        return _curItemCount <= 0 ? default : _items[0];
    }

    /// <summary>
    /// 移除根节点,向下排序
    /// </summary>
    public T RemoveFirst()
    {
        try
        {
            if (_curItemCount <= 0) return default;

            var root = _items[0];//根节点
            //在把最后一个元素移动到第一个元素
            _curItemCount--;
            _items[0] = _items[_curItemCount];
            _items[0].HeapIndex = 0;
            _items.RemoveAt(_curItemCount);

            //自顶向下排序
            if (_curItemCount > 0)
                SortDown(_items[0]);
            return root;
        }
        catch (Exception e)
        {
            throw new Exception($"大顶堆排序 异常:{e.Message}");
        }
    }

    /// <summary>
    /// 自底向上排序
    /// </summary>
    private void SortUp(T item)
    {
        if (_curItemCount <= 1) return; //只有一个元素

        while (true)
        {
            var parentIndex = (int)((item.HeapIndex - 1) * 0.5);
            var parent = _items[parentIndex];
            if (parent == null)
                throw new NullReferenceException($"自底向上排序 item.HeapIndex:{item.HeapIndex} parent=null");

            if (item.CompareTo(parent) > 0) //这个元素的值比父节点大 要和父节点交换位置
                HeapSwapSwap(item, parent);
            else //该元素已经放在合适的位置 跳出循环
                break;
        }
    }

    /// <summary>
    /// 自顶向下排序
    /// </summary>
    private void SortDown(T item)
    {
        if (_curItemCount <= 1) return; //只有一个元素

        while (true)
        {
            var leftIndex = item.HeapIndex << 1 + 1; //左叶子节点 <<1=*2
            var rightIndex = leftIndex + 1; //右叶子节点
            var swapIndex = leftIndex;
            if (leftIndex < _curItemCount) //有左节点
            {
                if (rightIndex < _curItemCount) //有右节点
                {
                    if (_items[leftIndex].CompareTo(_items[rightIndex]) < 0)
                        swapIndex = rightIndex; //得到大的节点
                }

                //和大的节点比较 如果子节点大,交互
                if (item.CompareTo(_items[swapIndex]) < 0)
                    HeapSwapSwap(item, _items[swapIndex]);
                else
                    return;
            }
            else //没有子节点
                return;
        }
    }

    private void HeapSwapSwap(T itemA, T itemB)
    {
        //先将缓存的值交换
        _items[itemA.HeapIndex] = itemB;
        _items[itemB.HeapIndex] = itemA;

        //在将元素自身存储的索引交换
        (itemA.HeapIndex, itemB.HeapIndex) = (itemB.HeapIndex, itemA.HeapIndex);
    }
}

8.计数排序(时间复杂度是 O(n+k)):

  时间复杂度的n为要排序的数的个数,k是要排序的数的最大值
  快速排序适合最大值和最小值差值不大的数值排序,消耗空间复杂度换取快捷的排序方法,一般快于比较排序。

//计数排序
static void CountSort(int[] arr)
{
    //1.得到数列的最大值
    int Max = arr[0];
    for (int i = 1; i < arr.Length; i++)
    {
        if (arr[i]>Max)
            Max = arr[i];
    }
    //2.根据数列最大值确定统计数组的长度
    int[] newArr = new int[Max+1];
    //3.遍历数列,填充统计数组
    for (int i = 0; i < arr.Length; i++)
        newArr[arr[i]]++;
    //4.遍历统计数组,输出结果
    int index = 0;
    for (int i = 0; i < newArr.Length; i++)
    {
        for (int j = 0; j < newArr[i]; j++)
            arr[index++] = i;
    }
}
//小优化
static void CountSort(int[] arr)
{
    //1.得到数列的最大值 和 最小值
    int Max = arr[0];
    int Min = arr[0];

    for (int i = 1; i < arr.Length; i++)
    {
        if (arr[i] > Max) Max = arr[i];
        if (arr[i] < Min) Min = arr[i];
    }
    //2.根据数列最大值确定统计数组的长度
    int[] newArr = new int[Max -Min+ 1];
    //3.遍历数列,填充统计数组 记得加上 偏移量Min
    for (int i = 0; i < arr.Length; i++)
        newArr[arr[i]-Min]++;
    //4.遍历统计数组,输出结果 记得加上 偏移量Min
    int Index = 0;
    for (int i = 0; i < newArr.Length; i++)
    {
        for (int j = 0; j < newArr[i]; j++)
            arr[Index++] = i+Min;
    }
}

9.List<T>排序对象排序–>List里存储的是对象—>接口排序

定义类
public class DDZ_Card
{
	public int value;//牌值
    public string color;//花色
}
一:实现IComparable接口
public class DDZ_Card:IComparable
{
	public int value;//牌值
    public string color;//花色
    //实现接口
    public int CompareTo(object obj)
    {
        DDZ_Card card = obj as DDZ_Card;
        return value.CompareTo(card.value);//
    }
}
二:实现,IComparer泛型接口
public class DDZ_Card:IComparer
{
	public int value;//牌值
    public string color;//花色
    //实现接口
    public int Compare(object x, object y)
    {
        DDZ_Card card1 = x as DDZ_Card;
        DDZ_Card card2 = y as DDZ_Card;
        if (card1.value > card2.value)
            return 1;
        else
            return -1;
    }
}
三:通过List.Sort的 public void Sort(Comparison<T> comparison)方法
 /// <summary>
    /// 卡牌排序
    /// </summary>
    /// <param name="cards"></param>
    /// <param name="asc">是否升序</param>
    public static void CardSort(List<DDZ_Card> cards, bool asc=true)
    {
        cards.Sort((DDZ_Card a,DDZ_Card b) => {
            if (asc)//升序
            {
                return a.value.CompareTo(b.value);
            }
            else//降序
            {
                return -a.value.CompareTo(b.value);
            }
        });
    }

10.List<T>排序–>Linq排序

一:使用Lambda表达式排序:
①:通过筛选条件,找到满足条件的集合

	/// <summary>
    /// 癞子排序
    /// </summary>
    /// <param name="keylist"></param>
    /// <returns></returns>
    private List<int> LaiziSqrt(List<int> keylist)
    {
        List<int> templist = new List<int>(keylist);
        if (MJManager.laizi != -1)//MJManager.laizi是Int类型的值,默认-1
        {
            List<int> templaizi = templist.Where(x => x == MJManager.laizi).ToList();//找到集合中所有赖子,存入新的集合中
            List<int> newlist = templist.Where(x => x != MJManager.laizi).ToList();//找到集合中所有非赖子,存入新集合中
            newlist.Sort();//非赖子集合排序
            templaizi.AddRange(newlist);//在赖子集合末尾添加非赖子集合
            return templaizi;
        }
        return templist;
    }

②:通过OrderBy(升序)和OrderByDescending(降序)对元素集合排序

public List<Card> Sort(List<Card> card)
{
	return card..OrderBy(x => x.value).ToList<Card>();//按照Value升序排序
}

 public List<DDZ_Card> KK(List<DDZ_Card> cc)
 {
     return cc.OrderByDescending(x => x.value).ToList<DDZ_Card>();//通过键值降序排序
 }
多字段主次顺序排序情况,先按no排序,再按name排序
List<Student> stuList= stu.OrderBy(s=> s.stuNO).ThenBy(s=> s.stuName).ToList<Student>();
C# 中常用的排序算法有以下几种: 1. 冒泡排序 冒泡排序是一种简单的排序算法,每次比较相邻两个元素的大小并交换位置,重复进行直到数组有序。具体实现如下: ```csharp public static void BubbleSort(int[] arr) { int n = arr.Length; for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } ``` 2. 插入排序 插入排序是一种简单且有效的排序算法,将未排序的元素依次插入到已排序的序列中,直到所有元素都有序。具体实现如下: ```csharp public static void InsertionSort(int[] arr) { int n = arr.Length; for (int i = 1; i < n; i++) { int key = arr[i]; int j = i - 1; while (j >= 0 && arr[j] > key) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = key; } } ``` 3. 选择排序 选择排序是一种简单的排序算法,每次选择数组中最小的元素并将其放到已排序的序列的末尾,直到所有元素都有序。具体实现如下: ```csharp public static void SelectionSort(int[] arr) { int n = arr.Length; for (int i = 0; i < n - 1; i++) { int minIndex = i; for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIndex]) { minIndex = j; } } int temp = arr[minIndex]; arr[minIndex] = arr[i]; arr[i] = temp; } } ``` 4. 快速排序 快速排序是一种高效的排序算法,它使用分治的思想将数组分成两个子数组,然后递归地对这两个子数组进行排序,最后合并两个有序数组。具体实现如下: ```csharp public static void QuickSort(int[] arr, int left, int right) { if (left < right) { int pivotIndex = Partition(arr, left, right); QuickSort(arr, left, pivotIndex - 1); QuickSort(arr, pivotIndex + 1, right); } } private static int Partition(int[] arr, int left, int right) { int pivot = arr[right]; int i = left - 1; for (int j = left; j < right; j++) { if (arr[j] < pivot) { i++; int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } int temp1 = arr[i + 1]; arr[i + 1] = arr[right]; arr[right] = temp1; return i + 1; } ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值