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

这篇具有很好参考价值的文章主要介绍了Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
  

Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数),Java 数据结构与算法篇,java,数据结构,开发语言,leetcode,算法

Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数),Java 数据结构与算法篇,java,数据结构,开发语言,leetcode,算法

 

文章目录

        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 个数),Java 数据结构与算法篇,java,数据结构,开发语言,leetcode,算法

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

交换的结果为:

Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数),Java 数据结构与算法篇,java,数据结构,开发语言,leetcode,算法

        交换完之后,当前索引为 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 < size && arr[left] > arr[max]) {
            max = left;
        }
        if (right < size && arr[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[j] 处的元素存放在 arr[i] 中,接着需要继续往上走, i = j ,j = (i - 1) / 2 直到 i == 0 或者 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] < value) {
            arr[i] = arr[j];
            i = j;
            j = (i - 1) / 2;
        }
        arr[i] = value;
        size++;
        return true;
    }

        需要注意:添加元素前,需要先判断该数组是否满了。还有添加完之后,需要进行 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] < value) {
            arr[i] = arr[j];
            i = j;
            j = (i - 1) / 2;
        }
        arr[i] = value;
        size++;
        return true;
    }

    //建堆
    public void heapify() {

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

    //下潜
    public void down(int i) {
        int left = 2 * i + 1;
        int right = left + 1;
        int max = i;
        if (left < size && arr[left] > arr[max]) {
            max = left;
        }
        if (right < size && arr[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 <= len(arr) <= 100000

0 <= k <= min(100000, len(arr))

OJ 链接:

面试题 17.14. 最小K个数

        4.1 实现最小 k 个数的思路

        具体思路为:结合大顶堆的数据结构的特点,根节点的元素永远比孩子节点的元素大。先将给定的 arr 数组的前 k 个元素直接通过 heap.offer() 方法添加到大顶堆上,然后 arr 数组剩下的元素需要跟堆顶元素相对比,若堆顶元素大于 arr[i] 中的元素,则需要进行交换,将 arr[i] 的元素替换到堆顶,接着还不能结束,有可能替换完的元素就不符合大顶堆的规则了,因此还需要将堆顶元素下潜处理调整,找到合适的位置存放该元素;若堆顶元素不大于 arr[i] 中的元素,则不需要交换。一直将 arr 数组中的元素遍历结束,则循环停止。最后堆上存储的 k 个元素就是该数组 arr 中最小的元素了。

        4.2 代码实现最小 k 个数

public class Solution {
    public int[] smallestK(int[] arr, int k) {
        MaxHeap heap = new MaxHeap(k);
        for(int i = 0; i < k ; i++) {
            heap.offer(arr[i]);
        }
        for(int i = k; i < arr.length; i++) {
            if(heap.peek() > arr[i]) {
                heap.arr[0] = arr[i];
                heap.down(0);
            }
        }
        return heap.arr;
    }

}

//实现一个大顶堆
class MaxHeap {
    int[] arr;
    int size;

    public MaxHeap(int capacity) {
        arr = new int[capacity];
    }

    public MaxHeap(int[] smallestK) {
        this.arr = smallestK;
        this.size = smallestK.length;
    }

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

    //删除堆顶元素
    public int poll() {
        if(isEmpty()) {
            return 0;
        }
        int ret = arr[0];
        arr[0] = arr[size - 1];
        size--;
        down(0);
        return ret;
    }
    //下潜
    public void down(int i) {
        int left = 2 * i + 1;
        int right = left + 1;
        int max = i;
        if(left < size && arr[left] > arr[max]) {
            max = left;
        }
        if(right < size && arr[right] > 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 个数),Java 数据结构与算法篇,java,数据结构,开发语言,leetcode,算法文章来源地址https://www.toymoban.com/news/detail-764238.html

到了这里,关于Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构---堆的实现

    前言 一、什么是堆? 二、 堆的实现 1. 堆的结构  2. 接口实现 总结 堆(Heap)是计算机科学中一类特殊的数据结构,是最高效的优先级队列。堆通常是一个可以被看做一棵完全二叉树的数组对象。 现实中我们通常把 堆(一种二叉树) 使用 顺序结构的数组 来存储, 大根堆 :根节

    2024年02月02日
    浏览(29)
  • 数据结构之堆的结构与实现

    目录 一、堆的概念及结构 1.1堆的概念  1.2堆的性质 1.3堆的结构 二、堆的实现 2.1堆向下调整算法(父亲与孩子做比较)  2.2堆的向上调整算法(孩子与父亲做比较) 2.3堆的创建(向下建堆)  2.4向下建堆的时间复杂度 2.5堆的插入 2.6堆的删除 2.7堆的完整代码实现 三、堆的应

    2024年02月08日
    浏览(35)
  • 数据结构—小堆的实现

    前言:前面我们已经学习了二叉树,今天我们来学习堆,堆也是一个二叉树,堆有大堆有小堆,大堆父节点大于子节点,小堆父节点总小于子节点,我们在学习C语言的时候也有一个堆的概念,那个堆是操作系统中的堆,与我们今天所学的堆全然不同。我们就来实现下小堆。

    2024年02月05日
    浏览(28)
  • 数据结构:堆的实现(C实现)

    个人主页 : 个人主页 个人专栏 : 《数据结构》 《C语言》 当一颗完全二叉树用顺序表来存储时,其被称为堆。 堆总是一棵完全二叉树 堆的某个节点的值总是不大于(大堆)或不小于(小堆)其父节点的值 其可以被用来解决top k 问题 或 堆排序 下面就是要实现的堆的功能 重点在

    2024年02月13日
    浏览(30)
  • 【数据结构之堆的实现】

    前言: 前篇学习了 数据结构之树和二叉树的基本概念知识,那么这篇继续学习怎么实现基本的操作。所以先建议看完上篇知识点,才有助于消化知识和理解。 / 知识点汇总 / 概念 :堆(Heap)是计算机科学中一类特殊的数据结构,是最高效的优先级队列。堆通常是一个可以被看

    2024年01月19日
    浏览(37)
  • 数据结构——二叉树(堆的实现)

    目录   树概念及结构 树的相关概念 树的表示  二叉树的概念及结构   堆 堆的实现   结构体建立 初始化   添加元素  打印堆  删除堆首元素  返回首元素  判断是否为空 空间销毁  刷题找工作的好网站——牛客网 牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推,

    2024年02月11日
    浏览(32)
  • 【数据结构】堆的实现及应用

    简单不先于复杂,而是在复杂之后 。 1.1 二叉树的顺序结构 普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。 而完全二叉树更适合使用顺序结构存储。 现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系

    2024年02月21日
    浏览(38)
  • 【数据结构】结构实现:顺序存储模式实现堆的相关操作

    🚩 纸上得来终觉浅, 绝知此事要躬行。 🌟主页:June-Frost 🚀专栏:数据结构 🔥该文章着重讲解了使用顺序结构实现堆的插入和删除等操作。  二叉树的顺序存储是指将二叉树中的所有节点按照一定的顺序(一层一层)存储到一个数组中。  我们可以通过数组下标来表示

    2024年02月08日
    浏览(32)
  • Java 数据结构篇-实现单链表核心API

    🔥博客主页:  小扳_-CSDN博客 ❤感谢大家点赞👍收藏⭐评论✍     文章目录         1.0 单链表的说明         2.0 单链表的创建         2.1 单链表 - 头插节点         2.2 单链表 - 遍历         2.2.1 使用简单的 for/while 循环         2.2.2 实现 forEach 方法         2.2.

    2024年02月05日
    浏览(36)
  • 【数据结构】——二叉树堆的实现

     大佬们点点关注,点点赞?! 在上篇博客中我们已经介绍了树和二叉树的相关概念,相信大家都已经清楚了树和二叉树的基本思想,下面我们就来着重看看二叉树堆的实现。 在看堆的实现,我们先看看二叉树的顺序存储 二叉树的顺序存储就是以顺序表来实现的,也就是把

    2024年04月13日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包