详解堆(heap)的实现

本文介绍了Java中的堆数据结构,包括小顶堆和大顶堆的概念、特性、操作方法(如push、pop、peek和size等),以及如何使用PriorityQueue和自定义MyHeap类实现堆。重点展示了堆在优先队列和堆排序中的应用。
摘要由CSDN通过智能技术生成
package com.datastructure;

import java.util.*;

/**
 * 堆(heap):一种满足特定条件的完全二叉树
 * <p>
 * 1.分两种类型:
 * 小顶堆(min heap):任意节点的值<= 其子节点的值
 * 大顶堆(max heap):任意节点的值>=其子节点的值
 * 2.堆的特性:
 * 最底层节点靠左填充,其他层的节点都被填满;
 * 二叉树的根节点称为堆顶,将底层最靠右的节点称为堆底
 * 对于大顶堆(小顶堆),堆顶元素(根节点)的值是最大(最小)的
 * <p>
 * 3.堆通常用于实现优先队列,大顶堆相当于元素按照从大到小的顺序出队的优先队列。
 * <p>
 * 4.堆常用操作:
 * 方法名          描述                         时间复杂度
 * push()   元素入堆                           O(logn)
 * pop()   堆顶元素出堆                          O(logn)
 * peek()  访问堆顶元素(对于大/小顶堆分别为最大/小值) O(1)
 * size()  获取堆的元素数量                      O(1)
 * isEmpty() 判断堆是否为空                      O(1)
 * <p>
 * 5.堆的实现:底层存储使用数组实现;是自下而上的构建
 * <p>
 * 6.堆化(heapify):修复从插入节点到根节点的路径上的各个节点
 *
 * 7.堆的常见应用:
 *     优先队列
 *     堆排序
 *     获取最大的k个元素  eg:选择热度前 10 的新闻作为微博热搜,选取销量前10的商品
 */
public class Heap1 {
    public static void main(String[] args) {
        /**
         * 初始化堆
         */
        //初始化小顶堆 (默认从小到大)
        Queue<Integer> minHeap = new PriorityQueue<>();
        //初始化大顶堆(使用lambda表达修改Comparator) (从大到小)
        Queue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);

        /**
         * 元素入堆
         */
        maxHeap.offer(1);
        maxHeap.offer(3);
        maxHeap.offer(2);
        maxHeap.offer(5);
        maxHeap.offer(4);

        /**
         * 获取堆顶元素
         */
        int peek = maxHeap.peek();
        System.out.println("peek = " + peek);

        /**
         *堆顶元素出堆
         */
        //出堆元素会形成一个从大到小的序列
        peek = maxHeap.poll();
        System.out.println("peek = " + peek);
        peek = maxHeap.poll();
        System.out.println("peek = " + peek);
        peek = maxHeap.poll();
        System.out.println("peek = " + peek);
        peek = maxHeap.poll();
        System.out.println("peek = " + peek);
        peek = maxHeap.poll();
        System.out.println("peek = " + peek);


        /**
         * 获取堆大小
         */
        int size = maxHeap.size();
        System.out.println("size = " + size);

        /**
         * 判断堆是否为空
         */
        boolean isEmpty = maxHeap.isEmpty();
        System.out.println("isEmpty = " + isEmpty);

        /**
         * 输入列表并建堆
         */
        minHeap = new PriorityQueue<>(Arrays.asList(1, 3, 2, 5, 4));
        System.out.println("minHeap = " + minHeap.poll());
    }
}


class MyHeap {
    /**
     * 使用列表而非数组,这样无须考虑扩容问题
     */
    private List<Integer> maxHeap;

    /**
     * 根据输入构建堆
     *
     * @param nums
     */
    public MyHeap(List<Integer> nums) {
        // 将列表元素原封不动添加进堆
        maxHeap = new ArrayList<>(nums);
        // 堆化除叶节点以外的其他所有节点
        for (int i = parent(size() - 1); i >= 0; i--) {
            siftDown(i);
        }

    }

    /**
     * 获取左子节点的索引
     */
    public int left(int i) {
        return 2 * i + 1;
    }

    /**
     * 获取右子节点的索引
     */
    public int right(int i) {
        return 2 * i + 2;
    }

    /**
     * 获取父节点的索引
     */
    public int parent(int i) {
        return (i - 1) / 2;//向下整除
    }

    /**
     * 访问堆顶元素:即二叉树的根节点,也就是列表的首个元素
     *
     * @return
     */
    public int peek() {
        return maxHeap.get(0);
    }

    /**
     * 交换元素
     */
    private void swap(int i, int j) {
        int tmp = maxHeap.get(i);
        maxHeap.set(i, maxHeap.get(j));
        maxHeap.set(j, tmp);
    }

    /**
     * 获取堆大小
     */
    public int size() {
        return maxHeap.size();
    }

    /**
     * 判断堆是否为空
     */
    public boolean isEmpty() {
        return size() == 0;
    }

    /**
     * 元素入堆
     */
    public void push(int val) {
        //添加节点
        maxHeap.add(val);
        // 从底至顶堆化
        siftUp(size() - 1);
    }
    /**
     * 从节点 i 开始,从底至顶堆化
     */
    private void siftUp(int i) {
        while (true){
            //获取节点i的父节点
            int p=parent(i);
            //当“越过根节点”或”节点无须修复“时,结束堆化
            if(p<0||maxHeap.get(i)<=maxHeap.get(p))
                break;
            //交换两个节点
            swap(i,p);
            //循环向上堆化
            i=p;
        }
    }

    /**
     * 元素出堆
      * @return
     */
public int pop(){
    //判空处理
    if(isEmpty()){
        throw new IndexOutOfBoundsException();
    }
    //交换根节点与最右叶节点(交换首元素与尾元素)
    swap(0,size()-1);
    //删除节点
    int val=maxHeap.remove(size()-1);
    //从顶至底堆化
    siftDown(0);
    //返回堆顶元素
    return val;
}

    /**
     * 从节点 i 开始,从顶至底堆化
     */
    private void siftDown(int i) {
    while (true){
        //判断节点i,l,r中值最大的节点,记作ma
        int l=left(i);
        int r=right(i);
        int ma=i;
        if(l<size()&&maxHeap.get(l)>maxHeap.get(ma))
            ma=l;
        if(r<size()&&maxHeap.get(r)>maxHeap.get(ma))
            ma=r;
        if(ma==i)// 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
        break;
        //交换两节点
        swap(i,ma);
        //循环向下堆化
        i=ma;
    }
    }
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值