快排
- 额外空间复杂度:好情况O(logN),差情况O(N)
完全二叉树
左孩子 : 2 * i + 1
右孩子 : 2 * i + 2
父节点 : (i - 1) / 2有N个节点时,高度是logN级别
heapInsert时,只关心父节点的路径上走多少高度,所以用户新加一个数字时,调整的代价是O(logN)级别
用户想删掉一个数,并让剩下的数重新调整堆的代价也是O(logN)级别
堆
堆结构就是用数组实现的完全二叉树结构
完全二叉树分为大根堆、小根堆
- 大根堆 : 每个子树最大值都是头节点
- 小根堆 : 每个子树最小值都是头节点
- 堆结构的heapInsert和heapify操作
- 维持大根堆 : 将每次放进数组的值跟它的父节点的值做比较并调整,这个过程称为heapInsert
- heapInsert :
1 | public static void heapInsert(int[] arr, int index) |
- 假设用户需要返回最大的数,这个数就是arr[0]
- 假设用户需要删除最大的数,做法是 :
交换第一个和最后一个数,同时heapsize–,不断比较两个孩子中最大的一个与父节点的大小,并调整,这个过程称为heapify - heapify :
1 | public static void heapify(int[] arr, int index, index heapSize) |
- 堆结构的增大和减小
- heapSort
1 | public static void heapSort(int[] arr) |
- 用户加数O(NlogN),heapify时O(NlogN),整体O(NlogN),额外空间复杂度O(1)
- mergeSort额外空间复杂度O(N),快排O(logN)
- 如果用户一股脑给出所有数组,要求弄成大根堆,可以从后往前做heapify,从倒数第二层开始,都只用往下进行一次heapify
- 这时时间复杂度 :
(如果数组中有N个数,想象成满二叉树,这时最底层节点N/2个)
T(N) = N/2 + N/4 * 2 + N/8 * 3 + N/16 * 4 + …
2T(N) = N/2 * 2 + N/2 * 2 + N/4 * 3 + …
T(N) = N + N/2 + N/4 + N/8 + … = O(N)
1 | public static void heapSort(int[] arr) |
优先级队列结构,就是堆结构
堆排序扩展题目
- 已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序
1 | import java.util.PriorityQueue; |
比较器
- 实现Comparator
接口,重载int compare()方法,可写成内部类 - 对于sort排序
如果返回负数,认为第一个参数应该放在前面
如果返回正数,认为第二个参数应该放在前面
如果返回0,认为谁在前面都行 - 对于new PriorityQueue<>(new AComp())的创建
如果返回负数,认为第一个参数应该放在上面
如果返回正数,认为第二个参数应该放在上面
如果返回0,认为谁在上面都行
1 | public class HeapTest |
不基于比较的排序
- 不基于比较的排序 必须分析数据状况来定制
- 计数排序
- 员工的年龄,建一个数组age[200],遍历一般,将下标对应成年龄来计数,如 1岁 arg[1]++。O(N)
- 基数排序
- [17 13 25 100 72]排序,先看最大的数字是几位
按十进制数看
[017 013 025 100 072]
准备容器(可以是数组/队列/栈),叫做桶
这里假设桶是队列,准备10个桶
先根据各位数字来 017放到7号桶 013放到3号桶 …
把桶中的所有数字依次倒出来
[100 072 013 025 017]
再按十位数决定进哪个桶 100进0号桶 072进7号桶 …
把桶中的所有数字依次倒出来
[100 013 017 025 072]
最后按百位数字决定进哪个桶 100进1号桶 013 017 025 072进0号桶
把桶中的所有数字依次倒出来
[013 017 025 072 100]
- [17 13 25 100 72]排序,先看最大的数字是几位
- 基数排序代码
1 | public static void radixSort(int[] arr) |