堆是完全二叉树
i 左 2*i+1 右2*i+2 父(i-1)/2
上浮
//在堆中插入一个数 思路结构 一个一个插入堆时的操作 也可以说是上浮 public static void heapInsert(int[] arr, int index) { while (arr[index] > arr[(index - 1) / 2]) { swap(arr, index, (index - 1) / 2); index = (index - 1) / 2; }
如果比子比父位置大那么子和夫交换 直到子<父
下沉
//下沉操作 public static void heapify(int[] arr, int index, int heapSize) { //index从哪个位置开始往下操作 heapSize判断左右两个孩子是否越界 int left = index * 2 + 1;//左孩子下标 while (left < heapSize) { //下方有孩子 把左右孩子两个孩子大的给largest int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left; //父和较大孩子之间谁的值大 下标给largest largest = arr[largest] > arr[index] ? largest : index; if (largest == index) { break; } swap(arr, largest, index); index = largest; left = index * 2 + 1; } }
index是从何位置往下堆化 heapSize是堆大小 主要用来判断是否越界
首先判断 是否当前位置存在子,如果存在子我们再进行操作 循环操作是 如果存在右子并且右子比左子大那么largest记录右子的下标索引 否则记录左子(此时largest记录的是子中较大那个的索引值) 接下来比较较大子值和索引值哪个更大(此时largest记录的是索引对应值和子值较大的那个)如果此时父值大 那么已经形成大根堆,如果子大那么交换子父的值和索引直到父大为之
如何用堆进行排序
public static void heapSort(int[] arr) { if (arr == null || arr.length < 2) { return; } for (int i = 0; i < arr.length; i++) { heapInsert(arr, i); } for (int i = arr.length - 1; i >= 0; i--) { heapify(arr, i, arr.length); } int heapSize = arr.length; swap(arr, 0, --heapSize); while (heapSize > 0) { heapify(arr, 0, heapSize); swap(arr, 0, --heapSize); } }
首先把数组搞成大根堆 然后 0位置和最后一个位置交换 然后0位置的数堆化 再交换 依次
当然如果进行大根堆调整 可以从右往左依次让所有子书变成大真堆 再向上变成大根堆 第二个if循环即次
应用 对于一个几乎有序的数组 把数组排好序,每个元素距离不超过k,最好的排序算法是
我们首先让数组的前k+1项变成小根堆 此时 该数组最小的数就应该在0索引 接着我们弹出最小值把后一个值放入小根堆,不断弹出最小值即可
public void sortedArrDistanceLessK(int[]arr,int k){ PriorityQueue<Integer> heap = new PriorityQueue<>(); int index = 0; for(;index<Math.min(arr.length,k);index++){ heap.add(arr[index]); } int i =0; for(;index<arr.length;i++,index++){ heap.add(arr[index]); arr[i]=heap.poll(); } while(!heap.isEmpty()){ arr[i++]=heap.poll(); } }
java中的优先队列底层本质就是一个小根堆,有些情境下必须手写堆,比如需要对堆的内部进行插入操作时。