【算法&数据结构体系篇class06】:堆、大根堆、小根堆、优先队列

文章详细介绍了堆结构,包括大根堆和小根堆的实现,以及如何通过堆实现优先级队列。堆排序的两种方法和时间复杂度被探讨,同时提供了手写堆排序的代码示例。此外,还讨论了在特定条件下(元素移动不超过k)的堆排序策略。最后,文章展示了最大线段重合问题的堆解法。

一、堆结构

1)堆结构就是用数组实现的完全二叉树结构
2)完全二叉树中如果每棵子树的最大值都在顶部就是大根堆
3)完全二叉树中如果每棵子树的最小值都在顶部就是小根堆
4)堆结构的heapInsertheapify操作
5)堆结构的增大add和减少poll
6)优先级队列结构,就是堆结构
  • heapInsert:

 入堆排序操作,从数组最后一个位置插入,然后再与其父节点(i-1)/2比较大小,大则交换上去,接着往其爷节点持续走..直到顶,或小于当前节点的父节点则停止,完成排序

  • heapify:

堆下沉排序操作 剔除元素后,需要将交换到根部的元素往下沉判断排序,如果大于左右节点就不用动,小于则与左右较大节点交换,并下沉继续判断,直到底部或者大于左右子节点

代码演示:

package class06;

import java.util.Comparator;
import java.util.PriorityQueue;

public class Heap {
    //大根堆 每个子树根节点比左右节点大
    public static class MyMaxHeap {
        private int[] heap;
        private int heapSize;
        private final int limit;

        public MyMaxHeap(int limit) {
            heap = new int[limit];
            heapSize = 0;
            this.limit = limit;
        }

        public boolean isEmpty() {
            return heapSize == 0;
        }

        public boolean isFull() {
            return limit == heapSize;
        }

        //入堆,同时保持堆的大根堆 有序
        public void push(int value) {
            if (isFull()) {
                throw new RuntimeException("堆已满,无法添加元素!");
            }
            //没满就赋值追加到数组后
            heap[heapSize] = value;
            //依次与该元素的父节点比较大小,大则交换两元素,然后接着往上走,直到顶或者不大于父节点,同时最后要把size+1
            heapInsert(heap, heapSize++);
        }

        //入堆操作,从数组最后一个位置插入,然后再与其父节点(i-1)/2比较大小,大则交换上去,接着往其爷节点持续走..直到顶,或小于当前节点的父节点则停止,完成排序
        private void heapInsert(int[] heap, int i) {
            //这个条件判断了两种情况,一个是大于父节点,一个是还没到顶节点(假如i来到顶部0 那么(i-1)/2也等0 为自己,是等于) 则继续循环。
            while (heap[i] > heap[(i - 1) / 2]) {
                swap(heap, i, (i - 1) / 2);
                i = (i - 1) / 2;
            }
        }

        //出堆,弹出最大值,顶部,然后保证当前堆仍有序 是大根堆
        public int pop() {
            if (isEmpty()) {
                throw new RuntimeException("堆已空,无法弹出元素!");
            }
            //弹出首元素,最大值
            int ans = heap[0];
            //然后把元素剔除两步  1.将首元素,与尾元素(heapSize是长度,尾元素是heapSize-1)交换,因为弹出操作,需要将heapSize 元素个数-1,两个操作只需要用--heapSize就能符合
            swap(heap, 0, --heapSize);
            //2.交换后表示将根节点元素剔除,然后需要确保现有堆的顺序
            heapify(heap, 0, heapSize);
            return ans;
        }

        //堆下沉排序操作 剔除元素后,需要将交换到根部的元素往下沉判断排序,如果大于左右节点就不用动,小于则与左右较大节点交换,并下沉继续判断,直到底部或者大于左右子节点
        private void heapify(int[] heap, int i, int heapSize) {
            //首先判断是否存在左子节点,左节点索引是i*2+1,不能超过heapSize-1尾索引,如果超过那肯定就到最后一个元素,右节点是比左节点大1 也更不会存在
            while (i * 2 + 1 < heapSize) {
                //此时确定有左节点,但需要判断是否有右节点i*2+2 如果有 并且大于左节点,那么左右节点较大值就是右节点,否则就是左节点
                int largest = i*2+2 < heapSize && heap[i*2+2]>heap[i*2+1]?i*2+2:i*2+1;
                //然后把较大的节点与父节点比较,谁大则重新赋值 largest最大值
                largest = heap[largest] > heap[i]?largest:i;
                if(largest == i) break; //如果判断后这个最大值位置就是父节点位置,相当于父节点都大于子节点,那么就不用交换,再下沉,此时已经完成排序,大的仍旧在前面,小的在下面,直接退出循环
                //子节点大于当前节点,那么与其较大的节点交换,交换完之后,当前节点i要来到较大节点largest位置 循环下沉
                swap(heap,i,largest);
                i = largest;
            }
        }
        private void swap(int[] heap, int i, int j) {
            int temp = heap[i];
            heap[i] = heap[j];
            heap[j] = temp;
        }
    }

    public static class RightMaxHeap {
        private int[] arr;
        private final int limit;
        private int size;

        public RightMaxHeap(int limit) {
            arr = new int[limit];
            this.limit = limit;
            size = 0;
        }

        public boolean isEmpty() {
            return size == 0;
        }

        public boolean isFull() {
            return size == limit;
        }

        public void push(int value) {
            if (size == limit) {
                throw new RuntimeException("heap is full");
            }
            arr[size++] = value;
        }

        public int pop() {
            int maxIndex = 0;
            for (int i = 1; i < size; i++) {
                if (arr[i] > arr[maxIndex]) {
                    maxIndex = i;
                }
            }
            int ans = arr[m
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值