java详解数据结构-----堆-----Heap

声明:关于文章的任何问题,以及知识点相关问题,可以私信博主,博主虽能力一般,但定尽全力为大家解决

文章前言:

上篇文章我们讨论过二叉树,在上篇文章中我们采用的是链式存储一颗二叉树,那么在本篇文章中将介绍一种新的存储方式,使用顺序存储方式去存储一颗二叉树(这里的二叉树一般指的是完全二叉树),这种存储方式我们管它叫堆,下面我们开始堆的学习


文章目录:

1、二叉树的顺序存储

2、堆

3、堆的应用—优先级队列

4、堆的其他应用-TopK 问题

5、相关堆的面试题

6、堆的其他应用-堆排序

7、本章全部代码


1、二叉树的顺序存储

1.1、存储方式

使用数组保存二叉树结构,方式即将二叉树用层序遍历方式放入数组中。一般只适合表示完全二叉树,因为非完全二叉树会有空间的浪费。这种方式的主要用法就是堆的表示。

①、完全二叉树编号是连续的,所以可以充分的利用数组的空间进行存储,减小空间的浪费
在这里插入图片描述
②、非完全二叉树由于层次遍历时会遇到空结点,在数组中也会存储此空间结点,会造成空间的浪费,空间利用率低

在这里插入图片描述

1.2、下标关系

在这里插入图片描述


2、堆

2.1、概念

在这里插入图片描述
关于大/小根堆的详解:
在这里插入图片描述

2.2、模拟实现堆

1、首先我们先需要知道堆对应java中的哪种集合(堆对应的是优先级队列)

在这里插入图片描述
2、先看怎样利用数组去构建一棵完全二叉树,此时按照数组的下标层次构建的仅仅只是一棵完全二叉树而已
在这里插入图片描述
3、调堆,既然需要建立一个堆那么就要把完全二叉树调整成大/小根堆
在这里插入图片描述
4、完成堆的建立还需要考虑以下问题:

在这里插入图片描述
5、既然已经知道了堆的底层是一个数组那么,首先定义的就是一个数组和记录数组元素个数的变量
在这里插入图片描述
6、有了这样基本的框架后先使用外界的数组对堆底下的数组进行赋值
在这里插入图片描述
7、数组存在数据后就是这样一棵完全二叉树,我们还需要把此完全二叉树调整为大/小根堆后才是真正的堆,这里是把完全二叉树调整为大根堆

在这里插入图片描述
①、具体向下调整思路:

在这里插入图片描述
②、代码实现:

在这里插入图片描述
③、建堆的时间复杂度
在这里插入图片描述

④、整体代码:

private void shiftDown(int parent,int len){
        int child = 2 * parent + 1;
        while(child < len){
            if(child + 1 < len && elem[child] < elem[child+1]){
                child = child + 1;
            }
            if(parent < child){
                swap(parent,child);
                parent = child;
                child = parent * 2 + 1;
            }else {
                break;
            }

        }
    }
    public void createHeap(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            elem[i] = arr[i];
            usedSize++;
        }
        for (int parent = (elem.length - 1 - 1) / 2; parent >= 0 ; parent--) {
            shiftDown(parent,usedSize);
        }
    }

⑤、成果展示
在这里插入图片描述
此时的完全二叉树已经被调整为一个大根堆,建堆完成

3、堆的应用—优先级队列

3.1、入队操作

在这里插入图片描述
模拟实现入队操作:

在这里插入图片描述

public void offer(int val){
        //先判断满没满 满了进行扩容
        if(isFull()){
            elem = Arrays.copyOf(elem,elem.length*2);
        }
        elem[usedSize++] = val;
        shiftUp(usedSize-1);
    }
    private boolean isFull(){
        return elem.length == usedSize;
    }
3.2、出队操作

在这里插入图片描述
模拟实现出队操作:

在这里插入图片描述

public void poll(){
        if(isEmpty()){
            throw new RuntimeException("堆中已经没有元素");
        }
        swap(0,usedSize-1);
        usedSize--;
        shiftDown(0,usedSize);
    }
    private boolean isEmpty(){
        return usedSize == 0;
    }
3.3、返回队首元素

在这里插入图片描述

3.4、java中的优先级队列

几个基本操作,以及底层是小堆

在这里插入图片描述

4、堆的其他应用-TopK 问题

面试中,TopK,是问得比较多的几个问题之一
在这里插入图片描述
思想一、将整个数组进行排序,求得前五个最大的数值即可

此方法过于简单不赘述

思想二、将数组中的所有数值都存储一个优先级队列中,出前5个满足要求的元素即可

在这里插入图片描述
思想三、前k个元素先入队,如果求前k个最大元素就建立一个小根堆,反之亦然,后面的数组元素依次和堆顶有元素进行比较,如果比堆顶元素大入队,重新调整成小根堆,一直到数组结束,剩下的K个元素就是数组中前K个最大的元素
在这里插入图片描述
在这里插入图片描述

5、相关堆的面试题

查找和最小的K对数字
在这里插入图片描述
实例:
在这里插入图片描述
解题思路:

在这里插入图片描述

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
          PriorityQueue<int[]> heap = new PriorityQueue<>(Comparator.comparingInt(a -> 
          nums1[a[0]] + nums2[a[1]]));
        for (int i = 0; i < Math.min(nums1.length,k); i++) {
            //把数组的下标入队
            heap.offer(new int[]{i,0});
        }
        List<List<Integer>> tmp = new ArrayList<>();
        while(k-- > 0 && !heap.isEmpty()){
            int[] pos = heap.poll();
            tmp.add(Arrays.asList(nums1[pos[0]],nums2[pos[1]]));
            if(++pos[1] < nums2.length){
                heap.offer(pos);
            }
        }
        return tmp;
    }
}

6、堆的其他应用-堆排序

堆排序注意点:需要建立一个大根堆,依次堆顶元素和堆尾元素交换,剩下的元素重新向下调整,直到调整到最后一个元素

在这里插入图片描述
在这里插入图片描述

7、本章全部代码

import java.util.Arrays;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Lenovo
 * Date: 2022-02-23
 * Time: 16:33
 */
class TestHeap {
    public int[] elem;
    public int usedSize;
    public TestHeap(){
        this.elem = new int[10];
    }
    private void swap(int i ,int j){
            int tmp = elem[j];
            elem[j] = elem[i];
            elem[i] = tmp;
    }
    private void shiftDown(int parent,int len){
        int child = 2 * parent + 1;
        while(child < len){
            if(child + 1 < len && elem[child] < elem[child+1]){
                child = child + 1;
            }
            if(parent < child){
                swap(parent,child);
                parent = child;
                child = parent * 2 + 1;
            }else {
                break;
            }

        }
    }
    private void shiftUp(int child){
        int parent = (child - 1)/2;
        while(child > 0){
            if(elem[parent] < elem[child]){
                swap(parent,child);
                child = parent;
                parent = (child - 1)/2;
            }else {
                break;
            }
        }
    }
    public void createHeap(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            elem[i] = arr[i];
            usedSize++;
        }
        for (int parent = (elem.length - 1 - 1) / 2; parent >= 0 ; parent--) {
            shiftDown(parent,usedSize);
        }
    }
    public void offer(int val){
        //先判断满没满 满了进行扩容
        if(isFull()){
            elem = Arrays.copyOf(elem,elem.length*2);
        }
        elem[usedSize++] = val;
        shiftUp(usedSize-1);
    }
    private boolean isFull(){
        return elem.length == usedSize;
    }
    public void poll(){
        if(isEmpty()){
            throw new RuntimeException("堆中已经没有元素");
        }
        swap(0,usedSize-1);
        usedSize--;
        shiftDown(0,usedSize);
    }
    private boolean isEmpty(){
        return usedSize == 0;
    }
    public int peek(){
        if(isEmpty()) {
            throw new RuntimeException("优先级队列为空!");
        }
        return elem[0];
    }
    //堆排序
    public void heapSort(){
        int end = usedSize - 1;
        while(end > 0){
            swap(end,0);
            shiftDown(0,end);
            end--;
        }
    }
}

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦の澜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值