版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_32502811/article/details/81057778
冒泡排序
略····
选择排序
- 一个数组,从0~N-1选择最小的和0位置值交换;1~N-1选择最小的和1位置值交换;以此类推。每次都是选择最小的放在前面。
插入排序:
- 0~i-1位置是排好的,对于新来的第i个数,相当于把这个数按大小插到原来排好的牌里,对于计算机来说,插进去的过程就是一路交换。这个i-1位置,是从0开始一直往后扩的,一直扩到数组最后一个,这样这个数组就排好了。
- 整体为O(N^2)算法,但是和数据状况有关系。
-
递归行为和递归时间复杂度的估算
- 递归函数就是系统在帮你压栈
- 估计递归行为复杂度的通式:T(N) = aT(n/b)+O(n^d)
归并排序(merge)
- 递归
- 先左边有序,再右边有序,再外排。外排就是准备两个指针,分别指着左右两边的第一个数,再准备一个空数组,左右比较,小的那个放空数组里,指针++;如果一个指针到头了,另一个整体拷贝进数组。
- 时间复杂度O(N*logN),空间复杂度O(N)
- mid = L +(R-L) >> 1(安全,不会产生溢出)
- 归并算法的应用:
- 小和问题:在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和,求一个数组的小和。
- 在找两个待merge的数组中,右边有多少个比前面指向的大,就产生多少个小和。
- 逆序对问题:如果左边的数比右边的数大,则两个数构成一个逆序对。请打印所有的逆序对。
- 在两个待merge的数组中,右边有多少个数比左边的小.
- 小和问题:在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和,求一个数组的小和。
- 归并算法应用,题目里如果出现了左,右根据大小做一些什么事情的时候,这个时候就很有可能利用的是归并的思想。
快排
- partition问题:按照给定的num,小于等于放在数组左边,大于等于放在数组右边(额外空间复杂度O(1),时间复杂度O(N)):
- 小于等于区域的指针x,从-1位置一直往下扩。cur从前向后遍历,
- 当前数如果大于num,跳过;
- 如果小于等于num,把这个数和x+1位置数交换,x++。
- 荷兰国旗问题:小于放一边,中间放一边,大于放右边。(O(1),O(N)):
- 准备两个表示区域的指针,小于区域less(-1)和大于区域more(N)。cur从前向后遍历,
- 如果当前数等于num,跳过,cur++;
- 如果小于num,当前数和less+1位置的数交换,less++,cur++;
- 如果大于num,当前数和more-1位置的数交换,more–,cur停在原地,考察换过来的值和num的大小。
- 停止条件为cur和more撞上。
- 经典快排
- 数组最后一个为x,小于等于x的放左边,大于的放右边。左右再分别这么做。
- 一次划分,只搞定一个数
- 改进的快排
- 用最后一个数做划分,小于的放左边,等于的放中间,大于的放右边。左右再划分,中间不用管。参考荷兰国旗问题。
- 一次划分,搞定了等于这一个区域的数。
- 随机快排
- 经典快排的问题:用最后一个数去划分,很有可能造成左右两边的数据规模差距大,使算法变成O(N^2)
- 随机选择一个数做划分,用平均期望去估计复杂度。(O(NlogN),额外空间复杂度O(logN))
堆排序
- 堆:完全二叉树
- 子节点和父节点的关系:假设父结点在数组中的位置为i,左孩子在数组中的位置为:2*i+1;右孩子为2*i+2(不越界条件下,越界相当于不存在);假设知道孩子结点的位置为i(无论左右),则父节点的位置为:(i-1)/2
- 大根堆:在这棵完全二叉树中,任何子树的最大值均为其根节点。
- 给定一个数组,构建大根堆(HeapInsert):(经历一个新来的数插入到已经有的大根堆的过程)
- 认为从0位置到i位置都是一个完全二叉树,i=0~N-1.在每一个完全二叉树中构建大根堆,也就是来一个数,跟他的父节点比,大就交换。然后大根堆的范围不断往下扩。
- 时间复杂度:O(N)
- heapify(数组中(大根堆)中有个数变小了,如何调整(下沉)使再次成为大根堆)
- 找到它的左右两个孩子,其中最大的交换上去。也就是说变小之后的数下沉的过程。
- 应用:随时找到一个流中吐出数的中位数。
- 准备两个数组,一个构建大根堆,一个构建小根堆
- 流吐出来的第一个数放到大根堆里
- 新来的数如果比大根堆的堆顶大,放小根堆去,否则放大根堆,每放进去一次,都得进行一次heapInsert的操作
- 比较大根堆和小根堆的heapSize,如果大根堆比小根堆size大超过一,则把大根堆的堆顶弹出,放到小根堆;否则,小根堆的堆顶弹出,放大根堆。
- 这里有一个弹出堆顶的操作,实现:将堆顶和最后一个数交换,heapSize减1,然后剩下的堆来一次heapify操作。
- 重复上述过程,始终保持两个堆的size差不超过1
- 堆排序
- 来了一个数组,先进行heapInsert操作,构建大根堆(这一步并不能保证数组一定有序,中间可能有乱的,但是堆顶一定是数组中的最大数)
- 把堆顶和数组最后一个数交换,heapSize–
- Heapify(每次搞定一个数,并且给放到数组末尾)
- 重复上述操作
排序算法的稳定性
工程中的综合排序算法
- 先判断数据是五种基础类型还是用户定义的类型
- 基础类型:快排
- 自己定义的:归并排序
- 数组长度很短(<60):插排:常数项极低
有关排序问题的补充
- 归并排序的额外空间复杂度可以达到O(1),但是非常难,“内部缓存法”
- 快排可以做到稳定性问题,但是非常难。“01 stable sort”