C# 排序算法

冒泡排序

冒泡排序的思想:冒泡排序是一种简单的排序算法,它重复的走访过要排序的序列,一次比较两个相邻两个元素。如果他们的顺序错误就讲他们的顺序交换过来,这个算法的名字是因为越小的元素会慢慢的“冒”到序列的最前端,就像水中冒出的气泡一样。

时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定(排序的稳定性是指如果在排序的序列中,存在前后相同的两个元素的话,排序前 和排序后他们的相对位置不发生变化))
冒泡排序示例:

这里写图片描述

示例代码如下:

/*冒泡排序
 *比较两个相邻的元素,如果第一个元素大于第二个,那么就把两个元素交换。
 *对每一对相邻的元素都做同样的工作,从开始第一个到对尾最后一个。
 *对数列的每一次走访后,数列的最后一个数都是走访得到的最大数。这样下一次走访就不要在与这次得到的最大的数比较了。
 *持续对以上越来越少的元素的比较工作,直至最后一个。
*/ 
private void sort(int[] number)
{
    int tempMax;
    int length=number.length;
    for(int i = 0;i < length-1;i++)
    {
        for(int j=0;j<length-1-i;j++)
        {
            if(number[i]>number[i+1])
            {
                tempMax=number[i];
                number[i]=number[i+1];
                number[i+1]=tempMax;
            }
        }
    }
}

快速排序

快速排序的基本思想:通过将数列分割成两个部分,其中一部分的元素都比另一部分的元素小,在分别两部分继续进行同样的排序,直到整个数列的有序。

时间复杂度:O(nlogn)
空间复杂度:O(1)
快速排序算法是不稳定的。

//low是默认中轴,high是数列最右边的位置
//左边的都比这个位置小,右边的都大
public static int getMiddle(int[] numbers, int low,int high)
{
    int temp = numbers[low]; //数组的第一个作为中轴
    while(low < high)
    {
        while(low < high && numbers[high] >= temp)
        {
            high--;
        }
        numbers[low] = numbers[high];//比中轴小的记录移到低端
        while(low < high && numbers[low] <= temp)
        {
            low++;
        }
        numbers[high] = numbers[low] ; //比中轴大的记录移到高端
        }
        numbers[low] = temp ; 
        return low ; // 返回中轴的位置
}

递归分算两部分的排序:

public static void quickSort(int[] number)
{
    int low = 0;
    int high = number.length-1;
    if(low < high)
    {
      int middle = getMiddle(numbers,low,high); //将numbers数组进行一分为二
      quickSort(numbers, low, middle-1);   //对低字段表进行递归排序
      quickSort(numbers, middle+1, high); //对高字段表进行递归排序
    }

}

选择排序

选择排序的的基本思想:在数列中选出最小的元素与第一个位置的元素交换,然后在剩下的数中找到最小的元素与第二个位置的元素交换位置,直到倒数第二个和最后一个元素进行比较。

时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:不稳定

代码实现:

private void SelectSort(int[] number)
{
    for(int i=0;i<number.length;i++)
    {
        for(int j=number.length-1;j>i:j--)
        {
            if(number[j]<number[i])
            {
                int temp = number[j];
                number[j] = number[i];
                number[i] = temp;
            }
        }
    }
}

总结:每一趟排序将会选择出最小的(或者最大的)值,顺序放在已排好序的数列的前面(或后面)。

插入排序

思想:默认将一个数列的第一个元素看做一个有序表,将后面的元素按照大小一次插入到合适的位置。

时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定

代码实现

private void InsertSort(int[] number)
{
    for(int i=1;i<number.length;i++)
    {
        int temp = number[i];
        for(int j=i;j>0&&temp<number[j-1];j--)
        {
            number[j]=number[j-1];
        }
        number[j]=temp;
    }
}

折半插入排序

折半插入排序是基于插入排序写的,可以减少“移动”和“比较”的次数。

时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定

        private void BInsertSort(int[] number)
        {
            for (int i = 1; i < number.Length; i++) {

                int temp = number [i];
                int j = 0;
                int low = 0, high = i - 1;
                while (low<=high) {
                    int m = (low + high) / 2;  //找到折半点
                    if (temp < number [m]) {
                        high = m - 1;
                    } else {
                        low = m + 1;
                    }       
                }

                for (j = i; j >high+1; j--) {
                    number [j] = number [j - 1];
                }
                number [j] = temp;
            }
        }

希尔排序

思想:希尔排序也是插入排序的一种,是直接针对插入排序进行改进的。它是将一个数列分成若干个数列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,在缩小增量继续排序,直到增量足够小后,再对数列整体做一次排序。

希尔排序示例:

来自百度百科

代码示例:

private static void ShellSort(int[] number)
{
    int gap;
    int i = 0;
    for ( gap = number.Length/2; gap >0;gap/=2) {
        for (i = gap; i <number.Length; i++) {
            if (number [i] < number [i - gap]) {
                int temp = number [i];
                int k = i - gap;
                while (k >= 0&&temp < number [k]) {             
                    number [k + gap] = number [k];
                    k -= gap;
                }
                number [k + gap] = temp;
            }
        }   
    }

归并排序

基本思想:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

操作:首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。

时间复杂度:O(nlogn)
空间复杂度:O(n)
稳定性:稳定

示例:

来自百度百科

代码示例:

        private static void MergeSort(int[] number,int low,int high)
        {
            if (low < high) {
                int middle = (low + high) / 2;
                MergeSort (number,low,middle); //左边
                MergeSort (number,middle+1,high);//右边
                Merge (number,low,middle,high);
            }

        }

        private static void Merge(int[] number,int low,int middle,int high)
        {
            int[] tempArry = new int[high-low+1];
            int i = low;
            int j = middle + 1;
            int k = 0;
            while (i <= middle && j <= high) {

                if (number [i] < number [j]) {

                    tempArry [k++] = number [i++];
                } else {

                    tempArry [k++] = number [j++];
                }
            }

            while (i <= middle) {

                tempArry [k++] = number [i++];
            }

            while (j <= high) {

                tempArry [k++] = number [j++];
            }

            for (int k2 = 0; k2 < tempArry.Length; k2++) {

                number [k2 + low] = tempArry [k2];
            }
        }           
    }

堆与堆排序

基本思想:堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。
在了解堆排序之前,我们先学习一下数据结构中的二叉堆。

二叉堆的定义
堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。同理,小根堆就是每个父节点的值都小于其子节点的值,即A[PARENT[i]] <= A[i]。由此可知,最小的值一定在堆顶。

大根堆排序算法的基本操作:
①建堆,建堆是不断调整堆的过程,从len/2处开始调整,一直到第一个节点,此处len是堆中元素的个数。建堆的过程是线性的过程,从len/2到0处一直调用调整堆的过程,相当于o(h1)+o(h2)…+o(hlen/2) 其中h表示节点的深度,len/2表示节点的个数,这是一个求和的过程,结果是线性的O(n)。
②调整堆:调整堆在构建堆的过程中会用到,而且在堆排序过程中也会用到。利用的思想是比较节点i和它的孩子节点left(i),right(i),选出三者最大(或者最小)者,如果最大(小)值不是节点i而是它的一个孩子节点,那边交互节点i和该节点,然后再调用调整堆过程,这是一个递归的过程。调整堆的过程时间复杂度与堆的深度有关系,是lgn的操作,因为是沿着深度方向进行调整的。
③堆排序:堆排序是利用上面的两个过程来进行的。首先是根据元素构建堆。然后将堆的根节点取出(一般是与最后一个节点进行交换),将前面len-1个节点继续进行堆调整的过程,然后再将根节点取出,这样一直到所有节点都取出。堆排序过程的时间复杂度是O(nlgn)。因为建堆的时间复杂度是O(n)(调用一次);调整堆的时间复杂度是lgn,调用了n-1次,所以堆排序的时间复杂度是O(nlgn)

代码实现:

//构建大根堆
//当前节点i的父节点是(i-1)/2,左右子节点是2*i+1,2*i+2
        public static void HeadAdjust(int[] number ,int parent,int length)
        {
            int temp = number [parent];
            int child = 2 * parent + 1;
            while (child < length) {
                if (child + 1 < length && number [child] < number [child + 1])//比较左右节点的大小
                    child++;

                if (temp >= number [child]) {
                    break;
                }

                number [parent] = number [child];
                parent = child;
                child = 2 * parent + 1;
            }
            number [parent] = temp;
        }

        public static int[] HeadSort(int[] number)
        {

            int[] tempNode=new int[number.Length];
            for (int i = number.Length / 2 - 1; i >= 0; i--) {
                HeadAdjust (number,i,number.Length);
            }
            //堆排序
            for (int i = number.Length - 1; i >= 0; i--) {
                int temp = number [0];
                number [0] = number [i];
                number [i] = temp;

                tempNode [i] = temp;

                HeadAdjust (number,0,i);
            }
            return tempNode;
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值