对数器:

用于测试一个方法是否正确(使用一个速记数据生成器生成数据,将数据带入方法测试,以及一个简单的,不管时间复杂度的,正确的,功能一样的方法中得出结果并比较)

递归:

master公式:

用于求子问题为等规模的递归的时间复杂度

T(N)为母问题规模

T(N/b)为子问题的规模,a为子问题被调用次数

O(N^d)除调用子问题之外的进程的时间复杂度

归并排序:

整体为一个递归排序,左右独立有序,再让其整体有序,整体有序国产过程中使用了外排序方法

时间复杂度为O(N*log N),空间复杂度为O(n)

求小和:

求数组中各个数的左边比它小的数的和

利用归并排序求小和,但是在比较合并时若右边的数小于等于左边的数,则先存右边数组的数

逆序对:

在一个数组中,若一个数比其右边的数大(两数不一定相邻),则组成一个逆序对

快速排序:

荷兰国旗问题:

一个数组,对于一个给定的值,小于这个值的数放到数组左边,等于这个值的数放到数组中间,大于这个值的数放到数组右边

快排1.0:使用数组中最后一个数将数组大于其的数放在数组右边,小于等于的数放在左边,两边进递归,时间复杂度O(n^2)

快排2.0:使用已知数组中最后一个数将大于其的数放在数组右边,等于的数放在中间,小于的数放在左边,大于和小于的数进递归,时间复杂度O(n^2)

快排3.0:在已知数组中随机选一个数,进行划分排序,时间复杂度为O(n*logn)

堆:

size为堆的长度而非数组长度

大根堆:

堆的根节点为所有节点中的最大的数

小根堆:

堆的根节点为所有节点中的最小的数

创建大根堆:

对于大根堆,插入一个值调整代价O(logN)

对于大根堆,若去除最大值之后其余数继续成堆,将堆中最后一个数放于堆的跟,之后令根的左右孩子与根比较,交换完后若其还有子孩子,再继续比较,直到不符合条件(调整代价O(logN))

index代表从那个位置开始下移

对于大根堆,若随即更改其中一个值,若该值变大则heepInsert上移,反之则heepify下移

调整代价O(logN)

堆排序:

让根与堆中最后一个数做交换,并且heapsize-1,之后对新的根进行heapify下移,之后重复操作,时间复杂度为O(NlogN),额外空间复杂度O(1)(第二张图为更优的建立大根堆方法,可替换第一张图中的for循环)

形成大根堆,可以一个一个加进去,进行heapinsert操作,也可以从下往上,从右往左做heapify操作,后者比前者优,后者复杂度计算:

例题:

可以使用小根堆,取前k+1个数存入小根堆,之后弹出堆根的数,并向后取一个数加入小根堆,直至排序完毕,时间复杂度为O(N*logK)当K相对于数组长度很小,可以认为时间复杂度为O(N)

堆结构注意点:

1.对于堆结构,当容量不够时,就成倍扩容,扩容次数为logN,每次扩容代价O(N),所以总的代价为O(N*logN),均摊到每次后代价为O(N)

2.使用系统给的堆结构时,与其沟通方式为add或者poll,若想改变其中一个,代价会很大,若想要对已形成的堆结构中某个数进行操作时,不能使用系统给的堆,需要自己手写

比较器:

对于自己定义的结构,使用系统给的排序函数,无法进行排序,学要自己写下排序的方法,比较器在c++中为重载比较运算符(注释里为书写比较器的规则)

写完比较器,使用系统的排序方法时,把比较器作为参数传过去

比较器还可以用在有序的结构里,如:大根堆,小根堆

在此之前的排序均为基于比较排序

不基于比较的排序(根据数据状况做排序):

计数排序:

记录每个数的出现次数与该数,时间复杂度为O(N),此排序仅限于出现的数有一定的范围

基数排序(桶排序)

对于一个数组,确定其中数的最高位,准备10个容器看(编号为0-9)即桶(一般是队列,也可以是栈,数组)从最低位开始一直到最高位,根据该位上个数的数字,放进对应的容器里,结束后根据容器编号从0到9依次将容器里的数放到数组中

基数排序(优化后,思想不变)

创建一个与原数组等长的容器help,创建一个10长度的count数组代替原来的10个容器,使用计数排序的思想,从低位到高位,依次记住出现的频次并依次相加存放,让其变为前缀和数组(此处指的是count数组)原数组从右到左依次片段对应位上出现的数在count作相应查找并存放到help数组的对应位置

count数组,对于i位置上的水k含义为:原数组中对应十进制位上小于等于i的数的数量

代码实现:

排序算法总结:

稳定性指的是相同值的数排完之后还能保证原来的相对次序不变

一般排序选择快排,空间复杂度超出堆排就用,需要稳定就用归并

注意:

基于比较的排序暂时无法做到时间复杂度低于O(N*logN)

基于比较的排序暂时无法做到在空间复杂度低于O(N)的情况下做到稳定

常见的坑:

综合排序:

在样本量大的时候使用快排在调度上时间复杂度低的优势,在小样本情况使用插入的常数极小的优势

对于系统给的arr的sort方法,为什么对于基础类型使用快排,自定义类型使用归并?

因为对于基础数据类型,稳定行没有必要,所以使用常数更小的快排,但是对于自定义类型,系统不知道稳定性是否有用,所以会使用归并