Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数)

2024-02-26 1488阅读

温馨提示:这篇文章已超过437天没有更新,请注意相关的内容是否还可用!

🔥博客主页: 【小扳_-CSDN博客】

❤感谢大家点赞👍收藏⭐评论✍  

Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数)

Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数)

 

文章目录

        1.0 堆的说明

        2.0 堆的成员变量及其构造方法 

        3.0 实现堆的核心方法

        3.1 实现堆的核心方法 - 获取堆顶元素 peek()

        3.2 实现堆的核心方法 - 下潜 down(int i)

        3.3 实现堆的核心方法 - 交换元素 swap(int i,int j)

        3.4 实现堆核心方法 - 删除堆顶元素 poll()

        3.5 实现堆的核心方法 - 替换堆顶元素 replace(int i)

        3.6 实现堆的核心方法 - 添加元素 offer(int value)

        3.7 实现堆的核心方法 - 建堆 heapify()

        3.8 实现堆的核心方法完整代码

        4.0 TOP - K 问题:最小的 K 个数

        4.1 实现最小 k 个数的思路

        4.2 代码实现最小 k 个数


        1.0 堆的说明

        堆(Heap)是一种基于树的数据结构,通常用于动态分配内存空间。堆可以被看作是一棵完全二叉树,其中每个节点都满足堆的性质,即父节点的值大于或等于子节点的值(大根堆),或父节点的值小于或等于子节点的值(小根堆)。在堆中,根节点的值是最大或最小的,因此也被称为最大堆或最小堆。

        堆的实现通常使用数组来存储堆中的元素,通过计算数组下标来实现节点之间的关系。堆的时间复杂度为 O(log n),其中 n 是堆中元素的数量。

        堆的操作包括插入、删除和查找等。插入操作将一个新元素插入到堆中,删除操作将堆中的最大或最小元素删除,查找操作可以在堆中查找特定元素的位置。

        2.0 堆的成员变量及其构造方法 

        主要的成员变量为:int[] arr 数组:用来存放元素的容器;int size :代表当前的元素个数。

        构造方法:指定数组大小带参数的构造器、参数为数组的构造器。

代码如下:

public class MyHeap {
    public int[] arr;
    public int size;
    public MyHeap(int capacity) {
        arr = new int[capacity];
    }
    public MyHeap(int[] arr) {
        this.arr = arr;
        this.size = arr.length;
        heapify();
    }
}

        

        3.0 实现堆的核心方法

        获取堆顶元素、下潜、交换元素、添加元素、替换元素、删除元素、建堆。

        3.1 实现堆的核心方法 - 获取堆顶元素 peek()

        用数组实现堆,在获取堆顶元之前,先需要判断该数组是否为空,若不为空,则直接返回数组索引为 0 的元素;若数组为空,则返回 0 或者抛出异常也可以。

代码如下:

    //获取栈顶元素
    public int peek() {
        if (isEmpty()) {
            return -1;
        }
        return arr[0];
    }

        3.2 实现堆的核心方法 - 下潜 down(int i)

        该方法主要用于删除栈顶元素、替换元素等核心方法。下潜的意思就是将当前的元素所在的位置不符合大顶堆或者小顶堆的规则,因此需要向下调整。找到合适的位置来存放当前的元素。

 具体下潜的思路:

假设需要满足大顶堆的规则:

Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数)

        由以上的图来看,当前的索引为 0 处的元素 7 小于该左孩子的元素,因此当前不满足大顶堆的规则。需要将两者进行交换。

交换的结果为:

Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数)

        交换完之后,当前索引为 2 处的元素 7 小于该右孩子的元素,所以索引 2 与 索引 5 需要继续交换。若当前为 i 该右孩子的索引 left = 2 * i + 1;该左孩子的索引 right = 2 * i + 2 (也可以表示为 right = left + 1 )一开始默认当前的最大元素的索引 max = i ,接着来判断该左右孩子的元素是否大于当前索引 max ,若大于当前索引 max 时,需要进行交换 max = left 或者 max = right 。若不大于当前索引为 max 处的元素,则不需要交换。由于每一次都是子问题过程,所以可以利用递归来实现,当且仅当 max != i 时,说明 max 已经被交换过了,需要继续向下递出,直到 max == i 时,结束递出,开始回溯。当然,这里不需要回带任何值或者变量,即该递归函数的返回类型为 viod 。

代码如下:

    //下潜
    public void down(int i) {
        int left = 2 * i + 1;
        int right = left + 1;
        int max = i;
        if (left  arr[max]) {
            max = left;
        }
        if (right  arr[max]) {
            max = right;
        }
        if (max != i) {
            //交换
            swap(i,max);
            //继续下潜
            down(max);
        }
    }

        3.3 实现堆的核心方法 - 交换元素 swap(int i,int j)

        交换数组索引中 i 与 j 处的元素。

代码如下:

    //交换
    public void swap(int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

        3.4 实现堆核心方法 - 删除堆顶元素 poll()

        具体实现思路:为了更高效率的删除堆顶元素后保持原来大顶堆或者小顶堆的规则。

        步骤一:先将堆顶元素与最后一个元素进行交换。即 arr[0] = arr[size - 1] 。

        步骤二:将 size-- 。

        步骤三:交换后的堆,可能会不满足大顶堆或者小顶堆的规则,则需要将堆顶元素进行下潜调整,找到合适的位置存放该元素。最后需要返回删除的元素。

代码如下:

    //删除堆顶元素
    public int poll() {
        if (isEmpty()) {
            return 0;
        }
        int t = arr[0];
        arr[0] = arr[size - 1];
        size--;
        //下潜
        down(0);
        return t;
    }

        注意:在删除堆顶元素之前,需要先判断当前的数组是否为空数组。

        同理,若需要删除指定堆中的元素索引,实现思路是一样的。

代码如下:

    //指定删除元素
    public int poll(int i) {
        if (i > size) {
            return 0;
        }
        int temp = arr[i];
        arr[i] = arr[size - 1];
        size--;
        //下潜
        down(i);
        return temp;
    }

        先判断索引是否合法,若不合法,则返回 0 或者抛出异常也可以。

        3.5 实现堆的核心方法 - 替换堆顶元素 replace(int i)

        具体思路为:先判断该数组是否为空数组,若不为空数组,则直接替换堆顶元素 arr[0] = i;之后可能会不满足大顶堆或者小顶堆的规则,所以索引为 0 处需要下潜调整,找到合适的位置存放元素。

代码实现:

    //替换堆顶元素
    public void replace(int i) {
        if (isEmpty()) {
            return;
        }
        arr[0] = i;
        down(0);
    }

        3.6 实现堆的核心方法 - 添加元素 offer(int value)

        具体实现思路:先判断当前索引为 i = size 处的双亲节点为 j = (i - 1) / 2 ,判断 arr[j] 与 value 的大小,若为大顶堆规则,则当 arr[j] > value 时,不需要继续往上走了,在 i 处存放 value 即可 arr[i] = value ;当 arr[j] value 时,退出循环。在 arr[i] 处存放 value 。

代码如下:

    //添加元素
    public boolean offer(int value) {
        if (isFull()) {
            return false;
        }
        int i = size;
        int j = (size - 1) / 2;
        while (i > 0 && arr[j]  
 

        需要注意:添加元素前,需要先判断该数组是否满了。还有添加完之后,需要进行 size++ 。

        3.7 实现堆的核心方法 - 建堆 heapify()

        该方法实现的意义,若随机给出一个数组,需要实现由大顶堆或者小顶堆的结构存放元素。因此就会用到该方法。

        实现思路为:需要找到最后一个非叶子节点,从后往前,将当前的元素进行下潜处理即可完成建堆。

代码如下:

    //建堆
    public void heapify() {
        //先找到最后非叶子节点
        int lastNonLeafNodes = size / 2 - 1;
        for (int i = lastNonLeafNodes; i >= 0 ; i--) {
            //下潜
            down(i);
        }
    }

        3.8 实现堆的核心方法完整代码

public class MyHeap {
    public int[] arr;
    public int size;
    public MyHeap(int capacity) {
        arr = new int[capacity];
    }
    public MyHeap(int[] arr) {
        this.arr = arr;
        this.size = arr.length;
        heapify();
    }
    //获取栈顶元素
    public int peek() {
        if (isEmpty()) {
            return -1;
        }
        return arr[0];
    }
    //删除堆顶元素
    public int poll() {
        if (isEmpty()) {
            return 0;
        }
        int t = arr[0];
        arr[0] = arr[size - 1];
        size--;
        //下潜
        down(0);
        return t;
    }
    //指定删除元素
    public int poll(int i) {
        if (i > size) {
            return 0;
        }
        int temp = arr[i];
        arr[i] = arr[size - 1];
        size--;
        //下潜
        down(i);
        return temp;
    }
    //替换堆顶元素
    public void replace(int i) {
        if (isEmpty()) {
            return;
        }
        arr[0] = i;
        down(0);
    }
    //添加元素
    public boolean offer(int value) {
        if (isFull()) {
            return false;
        }
        int i = size;
        int j = (size - 1) / 2;
        while (i > 0 && arr[j] = 0 ; i--) {
            //下潜
            down(i);
        }
    }
    //下潜
    public void down(int i) {
        int left = 2 * i + 1;
        int right = left + 1;
        int max = i;
        if (left  arr[max]) {
            max = left;
        }
        if (right  arr[max]) {
            max = right;
        }
        if (max != i) {
            //交换
            swap(i,max);
            //继续下潜
            down(max);
        }
    }
    //交换
    public void swap(int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    //判断是否为空数组
    public boolean isEmpty() {
        return size == 0;
    }
    //判断是否为满数组
    public boolean isFull() {
        return  size == arr.length;
    }
}

        4.0 TOP - K 问题:最小的 K 个数

题目:

        设计一个算法,找出数组中最小的k个数。以任意顺序返回这 k 个数均可。

示例:

输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

提示:

0 arr[max]) { max = right; } if(max != i) { swap(max,i); down(max); } } //交换 public void swap(int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } //获取堆顶元素 public int peek() { if(isEmpty()) { return 0; } return arr[0]; } //判断是否为空 public boolean isEmpty() { return size == 0; } //判断是否为满 public boolean isFull() { return size == arr.length; } }

Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数)

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]