堆结构(重点)——一种特殊完全二叉树
O(logN)
完全二叉树:或者是满的,在不满的最后一层也是从左往右依次变满的。
堆逻辑上就是完全二叉树
实现完全二叉树方法:把数组必须从零出发的连续一段可以对应成完全二叉树
size:从零出发的连续一段。
size=7表示要把数组从0到7七个数作为数组对应
所以根据这样,就可以把从零出发连续的一段,脑补成一段完全二叉树。
所以:怎么把数组连续出发的一段,实现成一个堆?
如下图,当6插入,它要找5比较大小(即和其父节点比较大小),找到父节点的方法在上图(i-1)/2
和父节点比较后,如果比父节点大,要与其交换。产生结果如下二图
同理,继续插入7,不断地往上换,和父节点比较。通过这样的调整方式,我可以数组每插入一个数,就将数组调整为大根堆。
这个过程叫heapInsert过程,code如下
public static void heapInsert(int[] arr,int index)
{
while(arr[index] > arr[(index-)/2])
{
swap(arr,index,(index-1)/2);
index = (index-1)/2;
}
}
index = (index-1)/2
index也要指向父节点位置。
现在已经可以保证插入数据,依旧保持堆结构了,现在用户需要返回最大值,并且保证剩下的数字仍然是堆结构。最大值毫无疑问是第一个数字,但是把,第一个数字返回以后arr[0]就空缺了,后面也无法组成大根堆。
这个时候操作是将最后一个数字copy到空位上,然后heapsize–(相当于有效区间缩小,最后一个数字就被抹除了,参考下图)
这个时候整个数组还不是堆,从头结点开始调整。和其左右孩子的最大值比较,头结点小就交换
heapify(堆化,非常重要的方法,从一个位置出发往下动保证堆结构):
public static void heapify(int[] arr,int index,int size)
{
int left = 2*index + 1;
while(left < size)//;eft+1代表右孩子
{
int largest = left + 1 < size && arr[left] < arr[left+1] ? left+1:left;//选出左右孩子大的那个
largest = arr[index] < arr[largest] ? largest:index;//选出父节点和子节点中最大那个。
//找出父节点和左右孩子节点中最大的数的下标
//如果父节点最大,不用交换
if(largest == index) break;//?
//子节点大,交换子节点和父节点
swap(arr,index,largest);
//当前指向父节点的index指向信道最大的子节点
index = largest;
//新的左孩子继续比较
left = 2*index+1;
}
}
堆排序:相当于选择排序,找最大值。时间复杂度O(N*logN)
堆结构远远比堆排序重要,优先级队列就是堆,只是换了一个称呼。
堆排序代码
public static void heapSort(int[] arr)
{
if(arr == null || arr.length < 2)
return;
for(int i = 0; i < arr.length;i ++)
heapInsert(arr,i);
int heapSize = arr.length;
swap(arr,0,--heapSize);//交换第一个与最后一个的value,因为第一个是最大值,然后heapsize-1才指向最后一个数字,所以要先--
while(heapSize > 0)
{
heapify(arr,0,heapSize);//将剩下的数据堆化,找到最大那个与其交换
swap(arr,0,--heapSize);
}
}
O(N*logk)
注意:系统给你提供的堆结构==>黑盒方法。沟通方式就是add添加,poll弹出,如下图。
但是黑盒方法不支持已经形成堆的东西,自己人为给 它变完之后,用很轻的代价重新调整为堆结构,不支持。自己手写的堆结构是支持的。有相应的需求,要自己手写堆来高效。
非常重要!
package class02;
import java.util.PriorityQueue;
public class Code04_SortArrayDistanceLessK {
public void sortedArrDistanceLessK(int[] arr, int k) {
//优先级队列,底层就是堆,默认整型小根堆,直接调用
PriorityQueue<Integer> heap = new PriorityQueue<>();//生成一个小根堆
int index = 0;
for (; index <= Math.min(arr.length, k); index++) {
//把前k+1个数字放到小根堆里面去
heap.add(arr[index]);
}
int i = 0;
for (; index < arr.length; i++, index++) {
heap.add(arr[index]);//新加一个数字放到小根堆里去
arr[i] = heap.poll();//每次弹一个数放到i位置
}
while (!heap.isEmpty()) {
//最后没有数字了,就把堆里的数一个个弹出放到位置上去
arr[i++] = heap.poll();
}
}
}
堆排序+堆排序扩展题目全code
package class02;
import java.util.Arrays;
import java.util.PriorityQueue;
public class Code09_All_HeapSorted
{
public static void heapsort(int[] arr)
{
if(arr == null || arr.length < 2)
return;
int heapsize = arr.length;
for(int i = 0; i < arr.length;i ++)
{
heapInsert(arr,i);
}
swap(arr,0,--heapsize);
while(heapsize > 0)
{