数据结构java版之堆

数据结构java版之堆目录一、二叉树的顺序存储1.存储方式2.下标关系二、堆1.概念2.建堆3.向下调整三、堆的应用(优先级队列)1.概念2.内部原理3.操作①入队列②出队列(优先级最高)③返回队首元素(优先级最高)4.堆的其他应用-TopK问题一、二叉树的顺序存储1.存储方式使用数组保存二叉树结构,方式即将二叉树用层序遍历方式放入数组中。一般只适合表示完全二叉树,因为非完全二叉树会有空间的浪费。即用堆来表示就是用完全二叉树来表示。…

数据结构java版之堆

万事胜意 数据结构java版之堆

目录

一、二叉树的顺序存储

1.存储方式

2.下标关系

二、堆

1.概念

2.建堆

3.向下调整

三、堆的应用(优先级队列)

1.概念

2.内部原理

3.操作

①入队列

②出队列(优先级最高)

③返回队首元素(优先级最高)

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


一、二叉树的顺序存储

1.存储方式

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

数据结构java版之堆

由图可见,对于非完全二叉树而言,浪费了很多空间。所以堆的表示用的是完全二叉树的表示方法

2.下标关系

①已知双亲(parent)的下:

左孩子(left)下标 = 2 * parent + 1;

右孩子(right)下标 = 2 * parent + 2;

②已知孩子(不区分左右)(child)下标:

双亲(parent)下标 = (child – 1) / 2

二、堆

1.概念

①堆逻辑上是一棵完全二叉树

②堆物理上是保存在数组中

数据结构java版之堆

③满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆

(注意:在整棵树中中的子树均要满足这个条件)

数据结构java版之堆

数据结构java版之堆

④反之,则是小堆,或者小根堆,或者最小堆(注意:在整棵树中中的子树均要满足这个条件)

数据结构java版之堆

数据结构java版之堆

⑤ 堆的基本作用是,快速找集合中的最值

2.建堆

体现在下面的操作中

3.向下调整

要点:左右子树必须已经是一个堆,才能调整。以大堆举例,最后调整成一棵大根堆的形式

①调整是从最后一棵子树出发的

②每棵子树均是向下调整的

???问题???

①如何找到最后一棵树的子树

 前面有提到:(len-1)可以找到最后一棵子树的下标;且

parent=((len-1)-1)/2

②如何遍历

parent–就可以遍历完每棵子树了

③究竟如何实现

用一个向下调整的函数来 实现

④每棵树调整结束的位置如何判定?

每棵树调整的结束位置,实际上都是一样的len

具体实现思路: 

数据结构java版之堆

代码如下:

public class TestHeap {
    public int []elem;
    //用来记录实际放入数组的元素个数
    public int usedSize;
    public TestHeap(){
        this.elem=new int[10];
    }//向下调整
    //parent:每棵树的根结点
    //len:每棵树调整的结束位置
    public void shiftDown(int parent,int len) {
        int child = parent * 2 + 1;
        //该子树至少有一个左孩子
        while (child < len) {
            if (child + 1 < len && elem[child] < elem[child + 1]) {
                //用来保证取的值是当前左右孩子中较大值的下标
                child++;
            }//情况①,elem[c]>elem[p]且p,c需要继续向下检测
            if (elem[child] > elem[parent]) {
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
                parent = child;
                child = 2 * parent + 1;
            }//情况②,elem[c]<elem[p],其子树也必然为大根堆,所以可以直接退出循环
            else {
                break;
            }
        }
    }
            //创建大根堆
            public void createHeap( int[] array){
                for (int i = 0; i < array.length; i++) {
                    elem[i] = array[i];
                    this.usedSize++;
                }
                for (int parent = (usedSize - 1 - 1) / 2; parent >= 0; parent--) {
                    //向下调整
                    shiftDown(parent, usedSize);
                }
            }
    }

时间复杂度分析:

粗略估算,可以认为是在循环中执行向下调整,为 O(n * log(n)) (了解)实际上是 O(n) 堆排序中建堆过程时间复杂度O(n)怎么来的?

大家可以参考一下这篇文章,这里就不详细解释了

堆排序中建堆过程时间复杂度O(n)怎么来的? – 知乎 (zhihu.com)数据结构java版之堆https://www.zhihu.com/question/20729324

三、堆的应用(优先级队列)

1.概念

在很多应用中,我们通常需要按照优先级情况对待处理对象进行处理,比如首先处理优先级最高的对象,然后处理次 高的对象。最简单的一个例子就是,在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话。在这种情况下,我们的数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。

这种数据结构就是优先级队列(Priority Queue)

2.内部原理

优先级队列的实现方式有很多,但最常见的是使用堆来构建。 

3.操作

数据结构java版之堆

①入队列

解题思路:(实质上为一个向上调整)

①将要入队元素放于该队列的队尾,要是原队列已满的话进行扩容

②找到该节点及其父亲结点,比较两者的值,若elem[child] > elem[parent]即进行交换,反之退出循环

③注意:因为原树已经调节好了,所以此时入队的时候只需要和每棵子树的父亲结点进行比较,循环结束的条件为child<0;

代码:

//向上调整
    private void shiftUp(int child) {
        int parent = (child-1)/2;
        while (child > 0) {
            if(elem[child] > elem[parent]) {
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
                child = parent;
                parent = (child-1)/2;
            }else {
                break;
            }
        }
    }

    public void offer(int val) {
        if(isFull()) {
            //扩容
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        elem[usedSize++] = val;
        //此处传入的是下标值,因此注意这里传入的是usedSize-1
        shiftUp(usedSize-1);
    }

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

②出队列(优先级最高)

注意:每次出队列必须保证出最大的或者最小的

解题思路:

①交换0下标和最后一个元素

②调整0下标这棵树即可(向下调整)

代码如下:

 public int poll(){
        if(isEmpty()){
            throw new RuntimeException("此优先队列为空!");
        }int tmp=elem[0];
        elem[0]=elem[usedSize--];
        elem[usedSize--]=tmp;
        usedSize--;
        shiftDown(0,usedSize);
        return tmp;
    }
    public boolean isEmpty(){
        return usedSize==0;
    }

③返回队首元素(优先级最高)

 public int peek(){
        if(isEmpty()){
            throw new RuntimeException("此优先队列为空!");
        }
        return elem[0];
    }

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

将会在下一节博客中讲述

 数据结构java版之堆                     数据结构java版之堆                   数据结构java版之堆                     数据结构java版之堆

今天的文章数据结构java版之堆分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/24995.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注