荷兰国旗问题
问题一:
问题二:荷兰国旗问题
快速排序
经典快排
荷兰国旗问题改进快排
代码如下:同样为递归过程。只用两个变量,最后交换最后一个数和more位置即可,这样会少用一个变量(炫技)
经典快排一次只搞定一个位置的数,容易出现比较极端的情况,复杂度退化为O(N2)。
而荷兰国旗改进的快排一次能搞定所有=x的数,所以改进的快排更快。
如果每次都能刚好找准位置==区域在正中心,那么复杂度为O(N*logN)。
随机快排
因此引入随机快排:
随机找变量P,和最后一个位置交换,以最后一个位置为标志开始划分。
使用长期期望的方式来算出复杂度(涵盖率)。
停留在每个位置的概率相同,计算得出长期期望的复杂度O(N*logN)。因此随机快排好。
绕过数据状况带过来的影响:
- 运用随机打乱数据原本状况
- 哈希函数打乱
随机快排是最常用的排序
基本上能用O(N*logN)的算法就用快排:代码简单,常数项少,额外空间复杂度O(logN)。mergesort还有数组的拷贝,输在常数项,额外空间复杂度O(N)。
空间复杂度O(logN):
记录断点所需要的空间,只有记录下来了才能知道下一个划分区域在哪里。在随机快排中也是概率问题,如果不用随机快排,数据状况差的情况下空间复杂度到O(N)。
工程上不会有递归,都是迭代版本。工程上准备递归函数代价比较高,会记录有关无关的所有信息,常数时间大,而且栈多了不安全。所以基本上都要改为非递归版本。
堆排序
什么是堆
堆:完全二叉树,满二叉树属于完全二叉树。新的一层从左往右依次补齐,完全二叉树。
堆在实际中可用数组实现。
一个数组可以理解成为一颗完全二叉树。
左右孩子节点位置和父节点关系,数值计算,可以数组结构->完全二叉树。脑补二叉树相当于,实际上还是数组。对于零节点,其父节点是其本身。
堆就是完全二叉树
大根堆:
在这颗二叉树的任意子树中,最大值是其头部。
小根堆:
在这颗二叉树的任意子树中,最小值是其头部。
如何建立大根堆?
每次加入一个数,该数如果大于父节点,则往上换,不断进行,从而将整个数组调整为大根堆。再重复加入数组中的数。代码实现:
建立大根堆的时间复杂度O(N),二叉树高度(满二叉树)。
假设数组中有一个值变化了,如何重新调整为大根堆?
heapify,heapsize就比较灵活,可以面对任意大小的堆
不断下沉
堆应用(数据流的中位数)
当一个流,不断吐出数,要求时刻返回吐出序列的中位数。如果常规方法会非常耗费时间,每次都要排序。
堆顶弹出,给小根堆
减堆操作,弹出堆顶,首先和堆的最后一个数交换,然后heapsize - 1,再经过heapify,重新调整为大根堆。
保证较小的N/2个数在大根堆,较大的N/2个数在小根堆。小于等于大根堆的堆顶,则放入大根堆,如果此时跟小根堆的个数差值大于1,则从堆顶弹出,同理处理小根堆。
优先队列–堆,一个高度调整完就结束了,每次弹出,插入都只要承担logN的代价,这个非常小。
堆排序
利用堆结构完成的排序。
堆顶和现在堆最后一个数做交换,heapsize减一,重新调整为大根堆,循环该过程,直到所有数填完当前堆的最后一个位置,那么该数组整体有序。
代码如下:
非常重要,几乎所有贪心都用这个! 堆排序很重要,堆更重要!