【数据结构】堆(创建,调整,插入,删除,运用)

news/2024/5/20 5:58:44 标签: java, 算法, 开发语言, 数据结构, , 二叉树, leetcode

目录

的概念:

的性质:

的存储方式:

的创建 : 

的调整:

向下调整:

向上调整:

的创建:

的时间复杂度:

 向下调整:

向上调整:

的插入与删除:

 的插入:

的删除:

的应用:

1.PriorityQueue的实现

2.排序:

3.Top-k问题 

结语:


的概念:

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在一 个一维数组中,并满足:Ki = K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小(或大)。将根节点最大的叫做最大或大根,根节点最小的叫做最小或小根

的性质:

(1)中某个节点的值总是不大于(大根)或不小于(小根)其父节点的值。

(2)总是一棵完全二叉树

具体如下图。 

 

的存储方式:

 从的概念可知,是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储。

注意:对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低。

将元素存储到数组中后,可以根据二叉树章节的性质对树进行还原。假设i为节点在数组中的下标,则有:

(1)如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2.

(2)如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子。

(3)如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子。

的创建 : 

为了使文章可读性更高下面给出测试类的基本代码,下面文章给出的代码均围绕这上面来。

elem:是一个类里面的数组(方便后序操作)

usedSize:是记录数组里面有多少个元素(不是数组容量)

TestHeap:构造方法为了简便把数组容量设为10,大小可以自己调整

initElem:初始化

java">public class TestHeap {
    public int[] elem;
    public int usedSize;
    public TestHeap(){
        elem = new int[10];
    }
    public void initElem(int[] array){
        for(int i = 0;i < array.length;i++){
            elem[i] = array[i];
            usedSize++;
        }
    }
}

的调整:

向上调整向下调整两种调整方式,在创建时我们采用向下调整方式,这样的时间复杂度比较低。故下面主要讲解向下过程(以大为例) 步骤如下:

向下调整:

1. 让parent标记需要调整的节点,child标记parent的左孩子(注意:parent如果有孩子一定先是有左孩子)。

2. 如果parent的左孩子存在,即:child < size, 进行以下操作,直到parent的左孩子不存在。

(1)parent右孩子是否存在,存在找到左右孩子中最小的孩子,让child进行标记。

(2)将parent与较小的孩子child比较,如果:

a:parent小于较小的孩子child,调整结束。

b:否则:交换parent与较小的孩子child,交换完成之后,parent中大的元素向下移动,可能导致子 树不满足对的性质,因此需要继续向下调整,即parent = child;child = parent*2+1; 然后继续。

 以数组{ 27,15,19,18,28,34,65,49,25,37 }为例调整完变成。

对应的代码如下:

如果想要转换成小的话把大于号小于号改一改即可,故下面不在过多描述。

java">private void siftDown1(int parent,int end){
        int child = parent*2+1;
        while(child < end){
            if(child+1 < usedSize && elem[child] < elem[child+1]){
                child++;
            }
            if(elem[child] > elem[parent]){
                swap(child,parent);
                parent = child;
                child = parent*2+1;
            }else{
                break;
            }
        }
    }

 为了使代码整洁故再实现一个swap方法。

java">private void swap(int i,int j){
        int tmp = elem[i];
        elem[i] = elem[j];
        elem[j] = tmp;
    }

注意:在调整以parent为根的二叉树时,必须要满足parent的左子树和右子树已经是了才可以向下调整。 时间复杂度分析:最坏的情况即图示的情况,从根一路比较到叶子,比较的次数为完全二叉树的高度,即时间复杂度为O(logn)log下标是以2为底的。

向上调整:

具体过程如图(按照插入的80走的路径):

代码如下:

先找新结点的parent的下标再child大于0的情况下循环进行比较交换,一旦发现不满足条件的立刻跳出循环,因为在使用向上调整之前已经排序好了。

java">public void siftUp(int child){
        int parent = (child-1)/2;
        while(child > 0){
            if(elem[child] > elem[parent]){
                swap(child,parent);
                child = parent;
                parent = (child-1)/2;
            }else{
                break;
            }
        }
    }

的创建:

如图所示是从最后一个结点的父亲结点开始遍历每一个结点都调用siftdown1进行向下调整,之后不断减减直到小于0(下标)。

代码如下:

java"> public void createBigHeap(){
        for(int parent = (usedSize-1-1)/2;parent >= 0;parent--){
            siftDown1(parent,usedSize);
        }
    }

运行结果:

通过观察elem的元素我们可以发现向下调整成功。💖

的时间复杂度:

 向下调整:

因为是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是 近似值,多几个节点不影响最终结果):如图:

向上调整:

如图:

经过比较我们选择时间复杂度较低的来进行的创建即向下调整,至于向上调整我们用于的插入与删除。

的插入与删除:

 的插入:

的插入总共需要两个步骤:

(1)先将元素放入到底层空间中(注意:空间不够时需要扩容).

(2)将最后新插入的节点向上调整,直到满足的性质.

代码如下:

isFull用来判断是否需要扩容

java">public boolean isFull(){
        return usedSize == elem.length;
    }

siftUp用来向上调整,先找到新参数的parent结点, 传入siftup的参数为新offer进来的参数。

java"> public void offer(int val){
        //1.判断满
        if(isFull()){
            this.elem = Arrays.copyOf(elem,elem.length*2);

        }
        elem[usedSize] = val;
        usedSize++;
        siftUp(usedSize-1);
    }

运行结果如下:

我们可以发现成功增加数据并完成排序。

的删除:

注意:的删除一定删除的是顶元素。具体如下:

(1) 将顶元素对中最后一个元素交换。

(2) 将中有效数据个数减少一个。

(3)对顶元素进行向下调整 。

 代码如下:

其实就是把最后一个元素的空间腾出来。

java">public int poll(){
        int tmp = elem[0];
        swap(0,usedSize-1);
        usedSize--;
        siftDown1(0,usedSize);
        return tmp;
    }

运行结果如下:

可以看到65被成功删除并且数组的序列没有改变

的应用:

1.PriorityQueue的实现

可以使用来实现优先队列,由于java语言有自带的优先队列故这里不在实现给出它的常用方法直接调用即可。

函数名功能介绍
boolean offer(E e)插入元素e,插入成功返回true,如果e对象为空,抛出NullPointerException异常,注意:空间不够时候会进行扩容
E peek()获取优先级最高的元素,如果优先级队列为空,返回null
E poll()移除优先级最高的元素并返回,如果优先级队列为空,返回null
int size()获取有效元素的个数
void clear()清空
boolean isEmpty()检测优先级队列是否为空,空返回true

2.排序:

排序即利用的思想来进行排序,总共分为两个步骤:

1. 建

升序:建大

降序:建小

2. 利用删除思想来进行排序

删除中都用到了向下调整,因此掌握了向下调整,就可以完成排序。

具体过程如下:

代码后序会将8大排序整理成一篇排序专项。

3.Top-k问题 

TOP-K问题:即求数据集合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。 对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都 不能一下子全部加载到内存中)。最佳的方式就是用来解决,基本思路如下:

1. 用数据集合中前K个元素来建

(1)前k个最大的元素,则建小.

(2)前k个最小的元素,则建大

2. 用剩余的N-K个元素依次与顶元素来比较,不满足则替换顶元素

将剩余N-K个元素依次与顶元素比完之后,中剩余的K个元素就是所求的前K个最小或者最大的元素。

不要用Arrays.sort来做这道题,因为这是一道面试题,用sort就可以快速结束面试,回家等通知了。

top-k问题

使用的AC优化代码:

java">class Imp implements Comparator<Integer>{
    @Override
    public int compare(Integer o1,Integer o2){
        return o2.compareTo(o1);
    }

}
class Solution {
    public int[] smallestK(int[] arr, int k) {
        Imp imp = new Imp();
        PriorityQueue<Integer> priorityqueue = new PriorityQueue<>(imp);
        int[] ans = new int[k];
        if(k == 0){
            return ans;
        }
        for(int i = 0;i < k; i++){
            priorityqueue.offer(arr[i]);
        }
        for(int i = k;i < arr.length; i++){
            int tmp = priorityqueue.peek();
            if(arr[i] < tmp){
                priorityqueue.poll();
                priorityqueue.offer(arr[i]);
            }
        }
        for(int i = 0;i < k; i++){
            ans[i] = priorityqueue.poll();
        }
        return ans;

    }
}

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固自己的知识点,和一个学习的总结,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进,如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

 


http://www.niftyadmin.cn/n/5371340.html

相关文章

Ribbon全方位解析:构建弹性的Java微服务

第1章 引言 大家好,我是小黑,咱们今天聊聊Ribbon,这货是个客户端负载均衡工具,用在Spring Cloud里面能让咱们的服务调用更加灵活和健壮。负载均衡,听起来挺高大上的,其实就是把外界的请求平摊到多个服务器上,避免某个服务器压力太大,其他的却在那儿闲着。 Ribbon的牛…

C++面试宝典第27题:完全平方数之和

题目 给定正整数 n,找到若干个完全平方数(比如:1、4、9、16、...),使得它们的和等于n。你需要让组成和的完全平方数的个数最少。 示例1: 输入:n = 12 输出:3 解释:12 = 4 + 4 + 4。 示例2: 输入:n = 13 输出:2 解释:13 = 4 + 9。 解析 这道题主要考察应聘者对于…

水题中的稀奇古怪trick合集

状态转移问题&#xff0c;一个状态的改变还会牵涉到此状态之前的状态时&#xff0c;很难利用简单的动态规划解决&#xff0c;可以考虑利用BFS队列优化&#xff0c;把更新过的状态存进队列中&#xff0c;队列空时停止 例题&#xff1a;2024牛客寒假集训2D-Tokitsukaze and Slash…

复制和粘贴文本时剥离格式的5种方法(MacWindows)

您可能每天复制和粘贴多次。虽然它是一个非常方便的功能&#xff0c;但最大的烦恼之一就是带来了特殊的格式。从网络上获取一些文本&#xff0c;您经常会发现粘贴到文档中时&#xff0c;它保持原始样式。 我们将展示如何使用一些简单的技巧在不格式化的情况下复制和粘贴。 1.…

【MIMO】

MIMO技术入门 1.简介 MIMO(多入多出):多天线技术。 注意&#xff1a;此处的多天线&#xff0c;并不是有多个天线板&#xff0c;对基站来讲指天线有多套振子&#xff08;每一套振子都可以看成一个独立的天线&#xff09;。 4G 8天线&#xff1b;5G 64T64R&#xff1b;不仅基站…

Aethir和Well-Link Tech携手革新云游戏,释放人工智能(AI)潜力

​Aethir将为Well-Link Tech的2亿用户提供先进的GPU计算能力&#xff0c;大幅提升他们的游戏体验。 新加坡&#xff0c;2024年2月7日 - 先驱性的去中心化GPU网络Aethir与实时云渲染技术领导者Well-Link Tech携手共创云游戏和元宇宙发展的新时代。 借助Well-Link Tech对领先游戏…

node.js 读目录.txt文件,用 xml2js 转换为json数据,生成jstree所需的文件

请参阅&#xff1a;java : pdfbox 读取 PDF文件内书签 请注意&#xff1a;书的目录.txt 编码&#xff1a;UTF-8&#xff0c;推荐用 Notepad 转换编码。 npm install elementtree ; npm install xml2js ; node.js 用 elementtree读目录.txt文件&#xff0c;用 xml2js 转换为…

HttpClient | 支持 HTTP 协议的客户端编程工具包

目录 1、简介 2、应用场景 3、导入 4、API 5、示例 5.1、GET请求 5.2、POST请求 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅长web应用开发、数据结构和算法&#xff0c;初…