heap堆在LeetCode中的应用

Heap堆

堆的本质是用Array来实现的完全二叉树,这个树的根节点代表整个堆里面的最大值或者最小值,根据是最大值还是最小值分为大根堆和小根堆,根节点的左右子树同样是大根堆或者小根堆。
在这里插入图片描述

一般来说在,堆的常用api有以下三个:

  1. peek() 查看堆顶元素 O1
  2. poll()拿出堆顶元素,O(logN)
  3. offer 添加元素 O(logN)

优先队列和堆

在Java 中有一个集合为PriorityQueue,它底层使用的就是堆
在这里插入图片描述
以下讨论大根堆的建立过程。
插入操作

  1. 在堆的最后建立一个节点
  2. 将数值赋予新节点
  3. 将这个节点与其父节点进行比较
  4. 如果新节点的数值比父节点值大,调换父子节点位置
  5. 重复3、4步骤,知道满足最大堆的特性。

poll出队列操作

  1. 移除根节点
  2. 将最后一个元素移到根节点
  3. 左右子节点的最大值与父亲节点比较
  4. 如果父节点数值比子节点小,交换父子节点
  5. 重复3、4步骤,知道满足要求

用数组来实现最大堆

public class MaxHeap{
	private int capacity;
	private int size = 0;
	private int[] array;
	public MaxHeap(int capacity){
		this.capacity = capacity;
		this.array = new int[capacity];
	}
	
	//获取左节点索引
    private int getLeftIndex(int parentIndex){
        return 2 * parentIndex + 1;
    }
    //获取右节点索引
    private int getRightIndex(int parentIndex){
        return 2 * parentIndex + 2;
    }
    //获取父节点索引
    private int getParentIndex(int childIndex){
        return (childIndex - 1) / 2;
    }
    //是否有左节点
    public boolean hasLeft(int index){
        return getLeftIndex(index) < size;
    }
    //是否有右节点
    public boolean hasRight(int index){
        return getRightIndex(index) < size;
    }
    //是否有父节点
    public boolean hasParent(int index){
        return getParentIndex(index) >= 0;
    }
    //获取左节点数据
    private int leftChild(int parentIndex){
        return array[getLeftIndex(parentIndex)];
    }
    //获取右节点数据
    private int rightChild(int parentIndex){
        return array[getRightIndex(parentIndex)];
    }
    //获取父节点数据
    private int getParent(int childIndex){
        return array[getParentIndex(childIndex)];
    }
}

插入操作

//插入操作
    public void insert(int value){
        if(size == capacity){
            array = Arrays.copyOf(array,capacity * 2);
            capacity = capacity << 1;
        }
        array[size] = value;
        size++;
        heapUp();
    }
    //上浮操作
    private void heapUp(){
        int index = size - 1;
        while (hasParent(index) && getParent(index) < array[index]){
            swap(getParentIndex(index),index);
            index = getParentIndex(index);
        }
    }

    //交换两个index上的数据
    public void swap(int left,int right){
        int temp = array[left];
        array[left] = array[right];
        array[right] = temp;
    }

出队列操作poll()

//出队列poll操作
    private int poll(){
        if(size == 0){
            throw new NoSuchElementException("队列为空");
        }
        int value = array[0];
        array[0] = array[size - 1];//把最后一个元素放到上面
        size --;
        heapDown();
        return value;
        
    }
     private void heapDown(){
        int index = 0;
        while (hasLeft(index)){
            int largerChildIndex = getLeftIndex(index);
            if(hasRight(index) && rightChild(index) > leftChild(index)){
                largerChildIndex = getRightIndex(index);
            }
            if(array[index] < array[largerChildIndex]){
                swap(index,largerChildIndex);
            }else{
                break;
            }
            index = largerChildIndex;
        }
    }

堆在LeetCode中解题应用

案例1 LeetCode 215 top K问题

在一个数组中找到第k大的元素
思考:可以建立一个小顶堆,然后遍历元素,如果遍历到的元素大于堆顶元素,就把当前堆顶元素删除,并这个元素添加到堆顶。最后的堆顶元素就是要找的值
如果让求第K小的数字,就建立大根堆,如果元素比堆顶小就加入在这里插入图片描述

public int findKthLargest(int[] nums, int k) {
        Queue<Integer> pq = new PriorityQueue<>();
        for (int num : nums) {
            if(pq.size() < k){
                pq.add(num);
            }
            else if(pq.peek() < num){
                pq.remove();
                pq.add(num);
            }
        }
        return pq.peek();
    }

案例2LeetCode 23合并k个升序链表

在这里插入图片描述

可以建立一个小根堆,每个链表的头结点入队,然后每次出队一个最小的节点,然后将对应链表的下一个节点入队,直到队列为空。

public ListNode mergeKLists(ListNode[] lists) {
         if (lists.length == 0) {
            return null;
        }
        ListNode dummyHead = new ListNode(0);
        ListNode curr = dummyHead;
        PriorityQueue<ListNode> pq = new PriorityQueue<>((o1,o2) ->{
            return o1.val - o2.val;
        });

        for (ListNode list : lists) {
            if (list == null) {
                continue;
            }
            pq.add(list);
        }

        while (!pq.isEmpty()) {
            ListNode nextNode = pq.poll();
            curr.next = nextNode;
            curr = curr.next;
            if (nextNode.next != null) {
                pq.add(nextNode.next);
            }
        }
        return dummyHead.next;
    }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值