1 堆排序
//heap
/*根据树的性质,树节点前一半一定是分支节点(有孩子),故从这些节点开始调整出初始堆,,len(a)/2-1是第一个非叶子
*/
func constructHeap(heap []int) {
for i:=len(heap)/2-1;i>=0;i-- {
adjustHeap(heap,i,len(heap))
}
}
/*每次调整使得满足堆定义
*/
func adjustHeap(heap []int,start,length int) {
var child,i int
i=start
for {
child=i*2+1
if child>length-1 {
//检查是否越界
break
}
if child+1<=length-1&&heap[child]<heap[child+1] {
child+=1
}//find bigger child
if heap[i]<heap[child] {
heap[child],heap[i]=heap[i],heap[child]
/*交换后,以child为根的子树不一定满足堆定义,所以从child处开始调整*/
i=child
}else {
break
}
}
}
func heapSort(heap []int) {
// 构建大顶堆
constructHeap(heap)
for i:=len(heap)-1;i>=0;i-- {
/*把根节点跟最后一个元素交换位置,调整剩下的n-1个节点,即可排好序*/
heap[0],heap[i]=heap[i],heap[0]
adjustHeap(heap,0,i)
}
}
堆的应用:
1 找到数组中第K的最值
数组中的第K的个最大值(小顶堆),有序矩阵的第K小的元素,最低加油次数,有序矩阵的第k的最小数组和
2 TOPK
前K个高频元素,查找和最小的K对数字,前K个高频单词且按顺序返回,滑动窗口最大值
3 堆排序
4 一个数组,可能包含连续相同的,将不同字母或数字重新排列使得连元素之间两两不同(输入:[1,1,1,2,2,2] 输出:[2,1,2,1,2,1])
5 利用堆求中位数:用一个最大堆存放比中位数小(或等于)的元素,用一个最小堆存放比中位数大(或等于)的元素。这里关键的方法是插入,每当要插入一个元素时,根据判断条件将它插入最大堆或是最小堆,并更新最大堆和最小堆(更新的逻辑很重要),使得最大堆和最小堆中元素的个数之差不超过1,这样中位数就是最大堆或最小堆的堆顶元素。具体而言,当最大堆和最小堆中元素个数不同(个数相差为1)时,元素个数多的那个堆的堆顶元素即为中位数;如果两者元素个数相同,那么中位数可以是最大堆和最小堆的堆顶元素的值取平均。
插入流程(以大堆为基准):
①当最大堆中的元素个数为0(即为空)时,将元素放入其中。(即将第一个元素放入到最大堆)
②当big_heap.size() == small_heap.size()时,比较最大堆的堆顶元素与将放入的元素大小,如果小于其堆顶的元素,则直接放入到最大堆中,否则放入到最小堆中。
③当big_heap.size() > small_heap.size()时,还是将放入的元素与最大堆的堆顶元素进行比较,如果大于堆顶元素,直接放入最小堆,否则,将最大堆的堆顶元素push到最小堆中,再将其弹出最大堆,最后将要放入的元素放入到最大堆中。
④当big_heap.size() < small_heap.size()时,则比较最小堆的堆顶元素与放入元素的大小,如果小于最小堆的堆顶元素,则直接放入到最大堆中,否则,将最小堆的堆顶元素放入到最大堆中,再将堆顶元素弹出,将要放入的元素放入到最小堆中。