6.排序学习(学习笔记)

6.排序学习(学习笔记)_第1张图片

2.6.1如何分析一个“排序算法”

①排序算法的执行效率

    从三方面来看:最好情况,最坏情况,平均情况时间复杂度;时间复杂度的系数,常数,低阶;比较次数和交换次数。

②排序方法的内存消耗

    算法的内存消耗可以通过空间复杂度来分析,排序算法也不例外,针对排序算法的时间复杂度,引入了一个新的概念:原地排序即为空间复杂度为O(1)的排序算法。

③算法排序的稳定性

    稳定性:这个概念是说,如果待排序的序列中存在值相等的元素,经过排序之后,先后顺序不发生变化,比如说,2,9,3,4,8,3这组数据,如果排序之后两个三前后顺序没有发生改变,称之为稳定的排序算法,反之则称为不稳定的排序算法。

    假如是一个很复杂的业务,比如有10万条订单数据,要求先按照订单金额进行排序,在订单金额相同的情况下,再根据下单时间进行排序。可以先用稳定的排序方法先对数据进行时间排序,在进行金额排序,排序之后,还是我们想要的数据。

6.排序学习(学习笔记)_第2张图片

2.6.2冒泡排序

    冒泡排序只会操作相近的两个数据,每次冒泡都会对相邻的两个数据进行比较,看是否满足大小关系要求,如果不满足就让他俩互换,一次冒泡至少会让一个元素移动到他所应该在的位置。重复n次,就完成了n个数据的排序工程。

6.排序学习(学习笔记)_第3张图片
I’m
6.排序学习(学习笔记)_第4张图片

    冒泡排序是原地排序算法,是稳定的排序算法,最好情况的时间复杂度是O(n),最坏情况时间复杂度为O(n²)

6.排序学习(学习笔记)_第5张图片

    平均时间复杂度的分析引入一种新的“不严格”的分析方法,有序度逆序度,有序度指数组中具有有序关系的个数,对于一个倒序排列的数组,比如说6,5,4,3,2,1他的有序度就是0,而1,2,3,4,5,6他的有序度就是 n*(n-1)/2也就是15,叫做满有序度,而最好情况有序度就是n*(n-1)/2,最差情况的有序度是0,平均就是n*(n-1)/4,而复杂度上上限是O(n²),所以平均情况下的时间复杂度就是O(n²)。

6.排序学习(学习笔记)_第6张图片

2.6.3插入排序

    插入排序含有两种操作,一种是元素的比较,一种是元素的移动,移动次数就等于逆有序度。根据其原理,我们把该无序数列看成两个部分,首先我们把第一位4看成是有序数列,剩下的作为无序数列,因为要把后面的无序数列中的数插入到前面有序数列,所以依次把后面的数抽出,在前面找到合适位置插入。如图,先抽出5,与前面比较,比4大放在4后面,再抽出6,与前面比较,比5大,所以插入到5后面。依次类推,直到最后的一个数也插入到前面的有序数列中。

6.排序学习(学习笔记)_第7张图片
6.排序学习(学习笔记)_第8张图片

    插入排序是原地排序算法,是稳定的排序算法,如果数组是有序的,最好情况时间复杂度是O(n),最坏情况是倒序的,所以最坏情况时间复杂度是O(n²),平均时间复杂度是O(n²)。

2.6.4选择排序

选择排序的实现思路有点类似于插入排序,也分排序区间和未排序区间,但是选择排序每次都会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。

6.排序学习(学习笔记)_第9张图片
6.排序学习(学习笔记)_第10张图片
6.排序学习(学习笔记)_第11张图片

    选择排序的空间复杂度是O(1),是一种原地排序算法。最好情况、最坏情况、平均情况时间复杂度都是O(n²),但是选择排序是不稳定的排序算法,比如说一组数:5,8,2,5,9。第一次找到的是最小数据2,然后和5交换位置,这样的话第一个5和第二个5的位置就变化了,就不稳定了。

2.6.5三种排序的总结

    选择排序是一种不稳定的排序算法,相比于冒泡排序和插入排序,这都是他的弱点所在,而冒泡排序和插入排序相比,虽然移动(插入)次数相同,时间复杂度也都相同,但是冒泡要赋值三个,而选择排序只需要一个。

    以上这三种算法,都有一个共同点,将待排序的序列分为已排序和未排序两部分,都是在未排序的部分找到一个最值,放到已排序序列的恰当位置。具体到代码里面,分两层循环,外层循环用于分割已排序和未排序部分,内层循环用于在未排序的部分中查找。

2.6.6归并排序

    归并排序采用一种分治思想(将一个大问题分解成小的问题来解决,小的问题解决了,大问题也就解决了)。先把一个无序数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的部分合并起来,这样这个数组就有序了。

6.排序学习(学习笔记)_第12张图片

    分治和递归思想有点像,分治一般是通过递归来实现的,分治是一种解决问题的处理思想,递归是一种编程技巧。

6.排序学习(学习笔记)_第13张图片
6.排序学习(学习笔记)_第14张图片
6.排序学习(学习笔记)_第15张图片

    性能分析:首先归并排序是稳定性的排序算法,时间复杂度的分析:归并排序的涉及递归,假设解决问题的时间复杂度是T(a),那么分解成两个问题,就是T(a)=T(b)+T(c)+k,其中k等于合并两段数据所消耗的时间。假设n个元素进行归并排序所需要消耗的时间是T(n),那么分解成两部分就是T(n/2)*2加上合并所需要的时间复杂度n。

6.排序学习(学习笔记)_第16张图片

    最终分析得到T(n)=2^k*T(n/2^K)+k*n,假设n=1,当T(n/2^k)=T(1)时,也就是n/2^k=1,得到k=log2n,带入到公式里面得到T(n)=Cn+nlog2n,用大O标记法来表示,他的时间复杂度就是O(nlogn),从空间复杂度来看,归并排序有一个致命的缺点就是,他不是原地排序的排序算法,他的空间复杂度是O(n)

2.6.7快速排序

    以下快排的实现思路是,从数组中取一个值作为基准(以下取得是第一个值),然后从左向右开始遍历找到那个比他大的值,从右向左开始遍历,找到那个比他小的值。然后将两个数的位置进行置换,然后把基准的位置和小的那个值位置互换。然后以此下标为基准,进行分区分治。


6.排序学习(学习笔记)_第17张图片
6.排序学习(学习笔记)_第18张图片

    再谈一种快排的思路,大体实现是相同的,以数组中的一个值为基准(以最后一个数为基准),遍历数据,把小于pivot的放在左边,大于pivot的放在右边,pivot就在中间。,然后对两边进行分区分治。最后实现快排。

6.排序学习(学习笔记)_第19张图片
6.排序学习(学习笔记)_第20张图片
6.排序学习(学习笔记)_第21张图片

    快排分析:快排是一种原地不稳定的排序算法,快排的时间复杂度分析方法和归并排序一样,也是O(nlogn),但是在极端情况下,比如1,2,3,4,5如果选择最后一个元素作为pivot,那么每次分区得到的都是不均等的,需要大约进行n此操作,才能完成快排过程,这种情况下,快排的时间复杂度就从O(nlogn)退化成了O(n²)。

    问题分析1:O(n)时间复杂度内求无序数组中第K大元素(使用第二种解题思想来实现的)

    选择数组A[0...n-1]来进行分析,以最后一个数为基准来判断,对数组进行原地分区,这样就分成了三部分,A[0...p-1]、A[p]、A  [p+1...n-1]。如果要找到第K大的数,那么如果p+1=K。那么p就是那个数,如果K

(本文是个人听课笔记,不少东西摘取于王争老师的原文,原文链接http://gk.link/a/10aMZ)

2.6.8线性排序

桶排序,计数排序,基数排序,因为这些排序算法的时间复杂度都是线性的,所以把这类排序算法称为线性排序。这三个算法都是基于比较的排序算法,都不涉及元素之间的比较操作。

2.6.9桶排序

 

6.排序学习(学习笔记)_第22张图片

    桶排序的核心思想就是将要排序的数据分到几个有序的桶里,然后每个桶里的数据在进行单独的排序,桶内拍完序之后再把每个桶里的数据按照顺序依次的取出,组成的序列就是有序的了。桶排序的时间复杂度是O(n)。如果要排序的数据是n条,平均的分配到m个桶里,然后对每个桶里进行快排,每个桶里有k=n/m个元素,每个桶的时间复杂度就是O(klogk),m个桶的时间复杂度就是O(m*k*logk),当桶的数量无限接近于元素的数量的时候,logk就会变成一个非常小的常量,这时桶排序的时间复杂度就变成了O(n)。

    这样看起来桶排序很好啊,那是不是其他的排序就没有必要了,当然不是。桶排序对于数据的要求太高了,首先要排序的数据要很轻易地能划分进m个桶,并且桶与桶之间要有天然的大小顺序,其次数据要在各个桶之间是分布均匀的,如果数据经过桶的划分之后,有的桶数据非常多,有的桶数据非常少,很不平均,那样的话桶内的时间复杂度就不是常量级了,在极端情况下,如果所有的数据全部都分布到一个桶里,那样的话,时间复杂度就又变成O(nlogn)了。

    说一个比较适合桶排序的场景。桶排序比较适合用在外部排序中,就是数据存储在外部磁盘中,数据量比较大,内存有限,无法将数据全部加载到内存中。比如说有10GB的数据,希望按照订单金额(假设都是正整数)进行排序,但是内存有限,只要几百MB,没办法一次性全部加载到内存中,应该怎么办:

    用桶排序来解决这个问题,先扫描一遍文件,看金额所处的数据范围,假设最低是1,最大10万,将数据划分进100个桶里,第一个桶是1-1000,第二个1001-2000...并且按照金额范围的大小顺序对编号命名(00,01,02...)。最理想的情况下,如果订单金额在1元-10万元内均匀分布,那订单会被分布到100个桶里,每个桶的大小也就是100MB,对桶里的数据进行快排,只需要按照桶的编号从头开始读取数据并存取到一个文件中即可。

    以上是理想的情况,但是怎么可能呢!有可能有的区域数据多,那样的话就会超出100MB,那样的话针对于这些比较大的文件(桶)就继续划分出更小的区间...直到能够一次性读入内存为止。

2.6.10计数排序

    计数排序像是桶排序的一种特殊情况,加入有n个数据,最大值是k,就分配k个桶,将n个数据分配到k个桶里,每个桶里的数据都是相同的。比如说高考分数排序,假设50万人的分数,分到0-900分这901个桶里。桶内都是相同的数据,所以并不需要进行排序,因为只涉及扫描操作,所以时间复杂度是O(n)。

    不过,为什么叫“计数”排序呢,假设8个考生,成绩2,5,3,0,2,3,0,3,存放到数组A中,然后按照分数排序放到有序数组中:然后推断出每个分数在有序数组中的存储位置:



    首先是从其那到后到前依次扫描数组A,比如:当扫描到3时,看数组C的下标为3的值,7表示小于等于3的共有7个数,然后把3作为第七个元素放入B数组的第七个位置,与此同时C数组中3下标的值减一变为6。这是一种利用另一个数组来计数的方式,所以叫计数排序。

    实现代码如下:


6.排序学习(学习笔记)_第23张图片
6.排序学习(学习笔记)_第24张图片
6.排序学习(学习笔记)_第25张图片

    计数排序要在数据范围k和数据量n相差不大的情况下进行排序,如果n比k大太多,就不适合计数排序了,而且计数排序只能给非负整数排序,如果要排序的是其他的数据类型,就要先传换为非负整数。(比如,如说分数排序,分数后面有小数,就得要先乘10在进行排序)

2.6.11基数排序

    假如要排序10万个手机号码,希望将这10万个手机号码进行排序,要采取一种什么排序方法。可以采用基数排序,先按照最后一位排序手机号码,再按照倒数第二位,以此类推,最后到第一位重新排序,经过11次排序之后吗,手机号码就有顺序了。对每一位进行的排序可以运用桶排序或者计数排序,一共有11位,所以时间复杂度就是O(n)。

    荷兰国旗问题:

6.排序学习(学习笔记)_第26张图片
6.排序学习(学习笔记)_第27张图片

(本文是个人听课笔记,不少东西摘取于王争老师的原文,原文链接http://gk.link/a/10aMZ)

你可能感兴趣的:(6.排序学习(学习笔记))