Java 数据结构之堆的概念与应用

目录
  • 什么是堆
  • 堆的类型
    • 小根堆
    • 大根堆
  • 堆的基本操作:创建堆
  • 堆的时间复杂度和空间复杂度
  • 堆的应用-优先级队列
    • 概念
    • 优先级队列基本操作
      • 入优先级队列
      • 出优先级队列首元素
    • java的优先级队列
    • 堆的常见面试题
      • 最后一块石头的重量
      • 找到K个最接近的元素
      • 查找和最小的K对数字

java数据结构的堆

什么是堆

堆指的是使用数组保存完全二叉树结构,以层次遍历的方式放入数组中。
如图:

注意:堆方式适合于完全二叉树,对于非完全二叉树若使用堆则会造成空间的浪费
对于根节点与其左右孩子在数组中的下标关系可表示为:left=2parent+1,right=2parent+2,parent=(child-1)/2

堆的类型

对于堆来说一共有两种类型:一为大根堆,还有一个为小根堆

小根堆

小根堆指的是:所有的根结点的值小于左右孩子结点的值
如图:

大根堆

大根堆指的是:所有根结点的值大于左右孩子结点的值。
如图:

当使用堆进行从小到大进行排序时应该使用大堆进行排序

堆的基本操作:创建堆

以创建大堆为例:我们先给定一个数组,该数组在逻辑上可以视为一颗完全二叉树,但目前并不是堆,但我们可以通过一定的算法将其变化为堆,通常我们从倒数第一个结点进行调整,一直调整到根结点的数,这样就调整为堆了;
如示例:

//建堆前
int array[]={1,5,3,8,7,6};
//建堆后
int array[]={ 8,7,6,5,1,3 };

调整方式:
第一步:先将数组还原成一个完全二叉树
如图:

第二步:如果倒数第一个叶子结点有兄弟结点则先与兄弟结点比较大小然后再取大的结点与父结点比较大小,如果没有兄弟结点则直接与父结点比较大小,如果值比父结点值大则交换值,一直这样调整到根节点的树,就可以调整成堆。
操作如图:

其核心代码如下:

 public static void shiftDown(int[] array,int parent){
        int child=2*parent+1;
        while (child<array.length){
            if(child+1<array.length){
                if (array[child]<array[child+1]){
                    child++;
                }
            }
            if(array[child]>array[parent]){
                int tmp=array[child];
                array[child]=array[parent];
                array[parent]=tmp;
                parent=child;
                child=parent*2+1;
            }
            else {
                break;
            }
        }
    }
    public static void createHeap(int[] array){
        for (int i = (array.length-1-1)/2; i >=0; i--) {
            shiftDown(array,i);
        }
    }
    public static void main(String[] args) {
        int array[]={1,5,3,8,7,6};
        createHeap(array);
        System.out.println(Arrays.toString(array));
    }

堆的时间复杂度和空间复杂度

建堆时没有使用额外的空间因此其空间复杂度为O(1);
注意:该函数shiftDown(int[] array,int parent)时间复杂度为O(logn),建堆的时间复杂度为O(n*logn),但是建堆的时间复杂度为O(n)其推导如下:

堆的应用-优先级队列

概念

我们通常需要按照优先级情况对待处理对象进行处理,比如首先处理优先级最高的对象,然后处理次高的对象.一个简单的例子:一天晚上,你正在看电视,这时你的父母叫你去厨房帮忙,那么这时你应该处理最重要的事情:去厨房帮妈妈的忙在这种情况下,我们的数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)。
注意:实现优先级队列的方式有很多种,一般来说我们一般使用堆来构建优先级队列

优先级队列基本操作

入优先级队列

以大堆为例:

  • 首先按尾插方式放入数组
  • 比较其和其双亲的值的大小,如果双亲的值大,则满足堆的性质,插入结束
  • 否则,交换其和双亲位置的值,重新进行 2、3 步骤
  • 直到根结点

如图:

核心代码如下:

public class TestHeap {
    public int[] elem;
    public int usedSize;

    public TestHeap() {
        this.elem = new int[10];//先创建长度为10的数组
    }
    public  boolean isFull() {
        return this.usedSize == this.elem.length;
    }
    public void push(int val) {
    //先判断队列是否已经满,如果以满则扩容
        if (isFull()){
            Arrays.copyOf(this.elem,this.elem.length*2);
        }
        this.elem[this.usedSize++]=val;
        shiftUp(this.usedSize-1);
    }
    public void shiftUp(int child) {
        int parent=(child-1)/2;
        while (parent>=0){
            if(this.elem[child]>this.elem[parent]){
                int tmp=this.elem[child];
                this.elem[child]=this.elem[parent];
                this.elem[parent]=tmp;
                child=parent;
                parent=(child-1)/2;
            }
            else{
                break;
            }
        }
    }

}

出优先级队列首元素

注意:为了防止破坏堆的结构,删除时并不是直接将堆顶元素删除,而是用数组的最后一个元素替换堆顶元素,然后通过向
下调整方式重新调整成堆

核心代码如下:

public class TestHeap {
    public int[] elem;
    public int usedSize;

    public TestHeap() {
        this.elem = new int[10];//10个0
    }
    public  boolean isFull() {
        return this.usedSize == this.elem.length;
    }
    public  boolean isEmpty() {
        return this.usedSize == 0;
    }
    public int poll() {
        //先判断队列是否为空,如果为空则抛出异常
        if (isEmpty()){
            throw new RuntimeException("优先级队列为空");
        }
        int tmp=this.elem[0];
        this.elem[0]=this.elem[this.usedSize-1];
        this.usedSize--;
        shiftDown(0);
        return tmp;
    }
    public void shiftDown(int parent) {
        int child = 2*parent+1;
        //进入这个循环 说明最起码你有左孩子
        while (child < this.usedSize) {
        	//该条件进入是判断其是否有右兄弟
            if(child+1 < this.usedSize &&
             this.elem[child] < this.elem[child+1]) {
                child++;
            }
            //child所保存的下标,就是左右孩子的最大值
            if(this.elem[child] > this.elem[parent]) {
                int tmp = this.elem[child];
                this.elem[child] = this.elem[parent];
                this.elem[parent] = tmp;
                parent = child;
                child = 2*parent+1;
            }else {
                break;//如果孩子节点比父亲节点小 直接结束了
            }
        }
    }
}

java的优先级队列

在java中,我们不必单独创建一个堆用于实现优先级对列
可以使用PriorityQueue
例如:

PriorityQueue<Integer> queue=new PriorityQueue<>();

java中的优先级对列其实是小堆若想使用大堆方法则需要从写比较方法
方法如下(方法不唯一)

PriorityQueue<Integer> queue=new PriorityQueue<>(new Comparator(Integer)){
public int compare(Integer o1,Integer o2){return o2-o1}
};

优先级的使用方法:

错误处理 抛出异常 返回特殊值
入队列 add(e) offer(e)
出队列 remove() poll()
队首元素 element() peek()

堆的常见面试题

最后一块石头的重量

最后一块石头的重量题

解题思路:该题可以使用变化过的优先级队列进行解答,即将默认小堆的优先级队列改为大堆模式的优先级队列,则将每块石块的重量使用循环放入优先级队列中其自动会把最重的石块放入队首,而后,将队列的头两个元素依次取出记为max1,max2,并将sum=max1-max2;如果sum大于0则又放入队列中不是则继续重复上诉操作

class Solution {
 public int lastStoneWeight(int[] stones) {
PriorityQueue<Integer> queue = new PriorityQueue<>((i1, i2) -> i2 - i1);//改为大堆
        for(int i=0;i<stones.length;i++){
            queue.offer(stones[i]);
        }
            while(queue.size()>=2){
            int max1=queue.poll();
            int max2=queue.poll();
            int sum=max1-max2;
            if(sum>0){
                queue.offer(sum);
            }
        }
        if(queue.size()>0){
            return queue.poll();
        }
        return 0;
    }
}

找到K个最接近的元素

找到K个最接近的元素

题解主要思路:使用优先级队列,先判别k是否大于数组长度,大于则直接将数组存放到List,相反则先依次存放k个数,之后将想要存放到优先级队列中的数-x的绝对值记为sum1,队列中第一个元素-x的绝对值记为sum2,如果sum1小于sum2则将队列中第一个元素删除,将其他数放入队列中,最后将队列中元素存放到list中

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
    PriorityQueue<Integer> queue=new PriorityQueue<>();
        List<Integer> list=new ArrayList<>();
        if(k>arr.length){
            for (int num:arr) {
                list.add(num);
            }
        }
        else {
            for (int i = 0; i < arr.length; i++) {
                if(i<k){
                    queue.offer(arr[i]);
                }
                else {
                    int sum1=Math.abs(arr[i]-x);
                    int sum2=Math.abs(queue.peek()-x);
                    if(sum1<sum2){
                        queue.poll();
                        queue.offer(arr[i]);
                    }
                }
            }
            while (!queue.isEmpty()){
                list.add(queue.poll());
            }
        }
        return list;
    }
}

查找和最小的K对数字

查找和最小的K对数字

主体解题思路:使用优先级队列将其先改变为大堆模式,使用循环先存放k个元素,之后想要存入队列的元素与队头元素比较,如果比队头元素小则删除队头元素,存放该元素,相反则继续上诉操作最后放入数组中

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        PriorityQueue<List<Integer>> queue=new PriorityQueue<>(k,(o1,o2)->{
            return o2.get(0)+o2.get(1)-o1.get(0)-o1.get(1);
        });
        for (int i=0;i<Math.min(nums1.length,k);i++)
        {
            for (int j = 0; j < Math.min(nums2.length, k); j++) {
                List<Integer> list=new ArrayList<>();
                if (queue.size()<k){
                    list.add(nums1[i]);
                    list.add(nums2[j]);
                    queue.offer(list);
                }
                else {
                    int tmp=queue.peek().get(0)+queue.peek().get(1);
                    if(nums1[i]+nums2[j]<tmp){
                        queue.poll();
                        list.add(nums1[i]);
                        list.add(nums2[j]);
                        queue.offer(list);
                    }
                }
            }
        }
        List<List<Integer>> list=new ArrayList<>();
        for (int i = 0; i < k&&!queue.isEmpty(); i++) {
            list.add(queue.poll());
        }
        return list;
    }
}

到此这篇关于Java 数据结构之堆的概念与应用的文章就介绍到这了,更多相关Java 数据结构内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java基础之堆内存溢出的解决

    一.实战-内存溢出 堆内存溢出 栈内存溢出 方法区溢出 直接内存溢出 二.实战-堆内存溢出 演示堆内存溢出代码,并且定位问题 总结堆内存溢出的场景与解决方案 分析商城项目中可能存在堆内存溢出的代码并且解决 三.堆内存溢出演示代码 public class HeapOOMTest { private List<String> oomList = new ArrayList<>(); public static void main(String[] args) { HeapOOMTes

  • Java数组与堆栈相关知识总结

    一.数组创建 1.1 声明并赋值 int[] a = {1,2,3}; 1.2 声明数组名开辟空间并且赋值 int[] a; a = new int[]{1,2,3}; 1.3 声明数组时指定元素个数然后赋值 int[] a= new int[3]; 这里Java会默认数组元素值为0 1.4 在以上的基础上创建多维数组 int[][] a = {{1,2,3},{4,5,6},{7,8,9}}; //每个子数组元素个数不要求均相同 int[][] a = new int[m][n]; //其中n

  • JAVA十大排序算法之堆排序详解

    目录 堆排序 知识补充 二叉树 满二叉树 完全二叉树 二叉堆 代码实现 时间复杂度 算法稳定性 思考 总结 堆排序 这里的堆并不是JVM中堆栈的堆,而是一种特殊的二叉树,通常也叫作二叉堆.它具有以下特点: 它是完全二叉树 堆中某个结点的值总是不大于或不小于其父结点的值 知识补充 二叉树 树中节点的子节点不超过2的有序树 满二叉树 二叉树中除了叶子节点,每个节点的子节点都为2,则此二叉树为满二叉树. 完全二叉树 如果对满二叉树的结点进行编号,约定编号从根结点起,自上而下,自左而右.则深度为k的,有

  • java数据结构-堆实现优先队列

    目录 一.二叉树的顺序存储 1.堆的存储方式 2.下标关系 二.堆(heap) 1.概念 2.大/小 根堆 2.1小根堆 2.2大根堆 3.建堆操作 3.1向下调整 4.入队操作 4.1向上调整 4.2push 入队的完整代码展示 5.出队操作 5.1pop 出队代码完全展示 6.查看堆顶元素 7.TOK 问题 7.1TOPK 8.堆排序 文章内容介绍大纲 一.二叉树的顺序存储 1.堆的存储方式 使用数组保存二叉树结构,方式即将二叉树用层序遍历方式放入数组中. 一般只适合表示完全二叉树,因为非完

  • 详解Java如何实现小顶堆和大顶堆

    大顶堆 每个结点的值都大于或等于其左右孩子结点的值 小顶堆 每个结点的值都小于或等于其左右孩子结点的值 对比图 实现代码 public class HeapNode{ private int size;//堆大小 private int[] heap;//保存堆数组 //初始化堆 public HeapNode(int n) { heap = new int[n]; size = 0; } //小顶堆建堆 public void minInsert(int key){ int i = this.

  • Java实现堆排序和图解

    目录 堆排序基本介绍 堆排序基本思想 堆排序图解 步骤一 步骤二 代码实现 总结 堆排序基本介绍 1.堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序. 2.堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系. 3.每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆 4.大顶堆举例说明 大顶堆特点:arr[i]

  • Java 数据结构之堆的概念与应用

    目录 什么是堆 堆的类型 小根堆 大根堆 堆的基本操作:创建堆 堆的时间复杂度和空间复杂度 堆的应用-优先级队列 概念 优先级队列基本操作 入优先级队列 出优先级队列首元素 java的优先级队列 堆的常见面试题 最后一块石头的重量 找到K个最接近的元素 查找和最小的K对数字 java数据结构的堆 什么是堆 堆指的是使用数组保存完全二叉树结构,以层次遍历的方式放入数组中. 如图: 注意:堆方式适合于完全二叉树,对于非完全二叉树若使用堆则会造成空间的浪费 对于根节点与其左右孩子在数组中的下标关系可表

  • java数据结构之树基本概念解析及代码示例

    Java中树的存储结构实现 一.树 树与线性表.栈.队列等线性结构不同,树是一...节点与节点之间的父子关系,可以为每个节点增加一个parent域,用以记录该节点的父点 树是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合.它是由n(n>0)个有限节点组成一个具有层次关系的集合.把 它叫做"树"是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的. 树定义和基本术语 定义 树(Tree)是n(n≥0)个结点的有限集T,并且当

  • Java数据结构之堆(优先队列)的实现

    堆(优先队列)是一种典型的数据结构,其形状是一棵完全二叉树,一般用于求解topk问题.根据双亲节点大于等于孩子节点或双亲节点小于等于孩子节点,可分为大顶堆和小顶堆,本文实现大顶堆. 根据大顶堆的定义,大顶堆的双亲节点大于等于其孩子节点,堆顶元素最大,对于每一个子树都是一个大顶堆,则从最后一个双亲节点进行调整为大顶堆,一直到根节点,则可构建一个大顶堆. 我们这里采用数组去存储,以heap={3,2,1,5,6,4}为例,需要一个init(int[] heap)初始化方法,从最后一个双亲节点开始将h

  • Java数据结构之堆(优先队列)详解

    目录 堆的性质 堆的分类 堆的向下调整 堆的建立 堆得向上调整 堆的常用操作 入队列 出队列 获取队首元素 TopK 问题 例子 数组排序 堆的性质 堆逻辑上是一棵完全二叉树,堆物理上是保存在数组中 . 总结:一颗完全二叉树以层序遍历方式放入数组中存储,这种方式的主要用法就是堆的表示. 并且 如果已知父亲(parent) 的下标, 则: 左孩子(left) 下标 = 2 * parent + 1; 右孩子(right) 下标 = 2 * parent + 2; 已知孩子(不区分左右)(child

  • Java数据结构之优先级队列(堆)图文详解

    目录 一.堆的概念 二.向下调整 1.建初堆 2.建堆 三.优先级队列 1.什么是优先队列? 2.入队列 3.出队列 4.返回队首元素 5.堆的其他TopK问题 总结: 总结 一.堆的概念 堆的定义:n个元素的序列{k1 , k2 , … , kn}称之为堆,当且仅当满足以下条件时: (1)ki >= k2i 且 ki >= k(2i+1) ——大根堆 (2) ki <= k2i 且 ki <= k(2i+1) ——小根堆 简单来说: 堆是具有以下性质的完全二叉树:(1)每个结点的

  • Java 数据结构与算法系列精讲之二叉堆

    目录 概述 优先队列 二叉堆 二叉堆实现 获取索引 添加元素 siftUp 完整代码 概述 从今天开始, 小白我将带大家开启 Java 数据结构 & 算法的新篇章. 优先队列 优先队列 (Priority Queue) 和队列一样, 是一种先进先出的数据结构. 优先队列中的每个元素有各自的优先级, 优先级最高的元素最先得到服务. 如图: 二叉堆 二叉堆 (Binary Heap) 是一种特殊的堆, 二叉堆具有堆的性质和二叉树的性质. 二叉堆中的任意一节点的值总是大于等于其孩子节点值. 如图: 二

  • Java 精炼解读数据结构的链表的概念与实现

    目录 前言: 一.什么是链表 链表的概念 链表的结构 链表如何存储数据 链表的实现   穷举法创建链表 打印链表 查找是否包含关键字key是否在单链表当中  得到单链表的长度: 头插法 尾插法 任意位置插入,第一个数据节点为0号下标 删除第一次出现关键字为key的节点 删除所有值为key的节点 总结: 前言: 顺序表的问题及思考 1. 顺序表中间/头部的插入删除,时间复杂度为O(N) 2. 增容需要申请新空间,拷贝数据,释放旧空间.会有不小的消耗. 3. 增容一般是呈2倍的增长,势必会有一定的空

  • Java数据结构之最小堆和最大堆的原理及实现详解

    目录 一.前言 二.堆的数据结构 三.堆的代码实现 1. 实现介绍 2. 入堆实现 3. 出堆实现 4. 小堆实现 5. 大堆实现 一.前言 堆的历史 堆的数据结构有很多种体现形式,包括:2-3堆.B堆.斐波那契堆,而在 Java API 中最常用的是用于实现优先队列的二叉堆,它是由 JWJ Williams 在 1964 年引入的,作为堆排序算法的数据结构.另外在 Dijkstra 算法等几种高效的图算法中,堆也是非常重要的. 二.堆的数据结构 在计算机科学中,堆(heap) 的实现是一种基于

  • Java数据结构之图的基础概念和数据模型详解

    目录 图的实际应用 图的定义及分类 图的相关术语 图的存储结构 邻接矩阵 邻接表 图的实现 图的API设计 代码实现 图的实际应用 在现实生活中,有许多应用场景会包含很多点以及点点之间的连接,而这些应用场景我们都可以用即将要学习的图这种数据结构去解决. 地图: 我们生活中经常使用的地图,基本上是由城市以及连接城市的道路组成,如果我们把城市看做是一个一个的点,把道路看做是一条一条的连接,那么地图就是我们将要学习的图这种数据结构. 图的定义及分类 定义: 图是由一组顶点和一组能够将两个顶点相连的边组

随机推荐