经典排序算法 — C# 版(上)

提起排序,与咱们的息息相关,平时开发的代码少不了排序。算法

经典的排序算法又很是多,咱们怎么评价一个排序算法的好坏呢?编程

其实能够这样想,要细致的比较排序算法好坏,那咱们就从多方面尽量详细的对比数组

 

1、效率方面

一、排序算法的执行效率:最好、最坏、平均编程语言

二、 咱们以前舍弃的时间复杂度的系数、常量、低阶,在这里须要拿回来spa

三、排序,免不了比较和移动3d

 

2、内存消耗方面

没错就是 算法的空间复杂度,不过对于排序的空间复杂度来讲,又赋予了新的名词 — 原地排序。code

顾名思义是 原地排序的确定是消耗内存少,反之须要往外走几步那就须要临时申请内存了。对象

原地排序 = O(1)blog

 

3、算法稳定性

字面意义就是不论怎么摆弄,这个算法稳定,不会对顺序有影响。排序

上面这句话应该加上一个定语:对于拥有相同值的元素的先后顺序不会发生改变。

举个例子:有两个对象,其中的金额字段同样,按照金额排序,通过算法一顿折腾后,相同金额的对象前后顺序不能发生改变。

 

讲完评估排序算法的优劣的几个方面,那就直接看看咱们平时常见的几个经典算法:

一、冒泡排序

图例演示

> C#

 1          //排序 — 冒泡排序
 2         private static void BubbleSort(int[] source)
 3         {
 4             if (source.Length <= 1)
 5                 return;
 6 
 7             bool isChanged = false;
 8             for (int i = 0; i < source.Length; i++)
 9             {
10                 for (int j = 0; j < source.Length - i - 1; j++)
11                 {
12                     var left = source[j];
13                     var right = source[j + 1];
14                     Console.WriteLine("【比较】");
15                     if (left <= right)
16                         continue;
17 
18                     source[j] = right;
19                     source[j + 1] = left;
20                     isChanged = true;
21                     Console.WriteLine("{交换}");
22                 }
23                 if (!isChanged)
24                     break;
25             }
26             Printf(source);
27         }

 

Q:冒泡排序的时间算法复杂度

A:最坏时间复杂度 — O(n^2):循环 n*n次

   最好时间复杂度 — O(n)    :循环 n次便可

   平均时间复杂度 — O(?)

           这里咱们使用几率来分析平均复杂度,状况比较复杂。

   咱们使用一种新的概念来分析平均复杂度,这个就是 有序度。

           有序度:看做是向量,左<= 右

      逆序度:正好相反,左 >= 右

   满有序度 = n*(n-1) / 2

   逆序度 = 满有序度 - 有序度

 

对于 n 个数据来讲,最坏状况时间复杂度的有序度是0,要交换 n*(n-1)/2次才能正确输出。

对于最好状况复杂度的有序度是n*(n-1)/2,须要交换0次就能达到彻底有序。

最坏 n*(n-1)/2次,最好0次,取个中间值来表示中间状况,也能够看做是平均状况 n*(n-1) /4

因此平均下来 要作 n*(n-1) / 4 次才能有序,由于冒泡排序的时间复杂度的上限是 O(n^2)

因此平均状况时间复杂度为 O(n^2)

虽然这样推论平均个状况并不严格,可是比起几率推论来讲,这样简单且有效。

 

Q:冒泡排序是否是原地排序

A:是,临时变量为了交换数据,常量级别的临时空间申请,因此空间复杂度为O(1)

 

Q:冒泡排序是否是稳定排序

A:是,由于没有改变相同元素的前后顺序。

 

 二、插入排序

假定,咱们将排序串分为两个区:已排序区,未排序区

一个元素要找到正确的的位置进行插入,那么须要去已排序区域找到本身的位置后,

将这个位置的元素们向后移动,空出位置,而后新元素入坑。

从以上这个思路来看,插入排序也是涉及到了元素的比较和移动。

给咱们一个无序数组,哪块是已排序区?哪里是未排序区?

 

好比:9, 0, 1, 5, 2, 3, 6

初始时,9 就是已排序区域;

 0开始去已排序区域挨个比较,即 i=1,0<9,9向后挪动,空出位置,0入坑;

1开始去 [ 0,9 ] 已排序区域比较,1 < 9,9向后移动腾位置,1入坑,1 > 0 无需操做;

依次重复以上操做,便可达成有序。

 

图例演示

 

> C#

 1         //排序 — 插入排序
 2         private static void InsertionSort(int[] source)
 3         {
 4             if (source == null || source.Length <= 0)
 5                 return;
 6 
 7             for (int i = 1; i < source.Length; i++)
 8             {// 未排序区
 9                 var sorting = source[i];
10                 int j = i - 1;
11 
12                 for (; j >= 0; j--)
13                 {// 已排序区
14 
15                     // 比较
16                     if (sorting >= source[j])
17                     {
18                         break;
19                     }
20 
21                     // 后移
22                     source[j + 1] = source[j]; 23                 }
24 
25                 // 入坑
26                 source[j + 1] = sorting; 27             }
28             Printf(source);
29         }

 

Q:插入排序的时间算法复杂度

A:最坏时间复杂度 — O(n^2):彻底倒序,循环n次,比较n次

   最好时间复杂度 — O(n):彻底有序,循环n次跳出

   平均时间复杂度 — O(n^2):循环 n次数据,在一个数组中插入数据的平均状况时间复杂度为O(n),因此是 O(n^2)

 

Q:插入排序是否是原地排序

A:是,没有临时变量申请,因此空间复杂度为O(1)

 

Q:插入排序是否是稳定排序

A:是, if (sorting >= source[j]) 这个判断保证了相同元素的前后顺序不变,

         去掉等于号也能够发生改变。能够实现稳定排序因此说是稳定排序

 

开始咱们也说了,这么多排序算法,咱们要对比一下,择优选择。

排序 最好状况 最坏状况 平均状况 是否稳定 是否原地
冒泡 O(n) O(n^2) O(n^2)
插入 O(n) O(n^2) O(n^2)

 

 

 

 

那么问题来了平均都是 O(n^2),为何倾向于使用插入排序呢?

这两种排序咱们将常量都放进来会发现,冒泡使用的常量数比排序多,因此在数据量上来后  常量*n 会有很大的差距。

咱们的编程语言中的排序算法不少都会倾向于插入排序算法。

 

三、选择排序

其实操做相似于插入排序,只不过是换了换操做方式。

因此也分为 已排序区和未排序区,操做方式是在未排序区间找到最小的,而后放到已排序区间最后。

图例:

 

> C#

        private static void SelectionSort(int[] source)
        {
            if (source.Length <= 1)
                return;

            for (int i = 0; i < source.Length - 1; i++)
            {// 已排序
                var minIndex = i;
                
                for (int j = i+1; j < source.Length; j++)
                {//未排序

                    if (source[minIndex] > source[j])
                    {
                        minIndex = j;
                    }
                }
                if (i != minIndex)
                {
                    int tmp = source[i];
                    source[i] = source[minIndex];
                    source[minIndex] = tmp;
                }
            }

            Printf(source);
        }

 

Q:选择排序的时间算法复杂度

A:最坏时间复杂度 — O(n^2)

   最好时间复杂度 — O(n^2)

   平均时间复杂度 — O(n^2)

 

Q:选择排序是否是原地排序

A:是,没有临时变量申请,因此空间复杂度为O(1)

 

Q:选择排序是否是稳定排序

A:不是

 

四、对比 随机生成1000个元素的 int 数组

分别执行时间以下:

相关文章
相关标签/搜索