C# 排序算法

来源:互联网 发布:linux bash是什么 编辑:程序博客网 时间:2024/05/17 00:04

冒泡排序

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

时间复杂度: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;        }
原创粉丝点击