ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

二叉堆

2021-12-04 21:59:02  阅读:148  来源: 互联网

标签:index elements int 元素 二叉 length size


 

 

最大堆_二叉堆:

最小堆_二叉堆

 堆的特征:

add:

 

 这个过程的时间复杂度是根树的高度有关

 是logn级别

 add代码:

  private void elementsNullCheck(E e){
        if(e==null){
            throw new IllegalArgumentException("element must not be null");
        }
    }
    @Override
    public void add(E element) {
        elementsNullCheck(element);
        //检查容量是否够再添加一个元素
        ensureCapacity(size+1);
        elements[size++]=element;
        siftUp(size-1);
    }
    /**
     *当添加之后 我们要上溢最后一个元素
     * 即是索引为size-1处的元素
     */
    private void siftUp(int index){
        E indexelement=elements[index];
        while (index>0){
            int parentindex=(index-1)/2;
            E parentelment=elements[parentindex];
            if(compare(parentelment,indexelement)>=0){
                return;
            }
            //交换
            E temp=elements[index];
            elements[index]=elements[parentindex];
            elements[parentindex]=temp;
            //重新赋值index
            index=parentindex;
        }
    }

 add优化:

也即是对上溢操作的优化:

 

 /**
     * 优化swift:
     * 到循环结束的时候再把最开始的时候元素加上 避免了大量的交换
     */
    private void siftUpWell(int index){
        E indexelememt=elements[index];
        while (index>0){
            int parentindex=(index-1)/2;
            E parentelement=elements[parentindex];
            if(compare(parentelement,indexelememt)>=0){
                break;
            }
            elements[index]=elements[parentindex];
            index=parentindex;
        }
        elements[index]=indexelememt;
    }

 

 

删除:每一次都是删除堆顶的元素

 

 

以下这两段代码等价:

第二段进行了优化。。。。。。。。。。。

 

 关键:

关键:

找到第一个叶子节点的索引  

这个值也就是非叶子节点的数量。。。。。并且我们默认是向下取整 直接带入公式即可

 

 remove代码:


    @Override
    public E remove() {
       emptyCheck();
       E root=elements[0];
        /**
         * 1、用最后一个节点元素覆盖顶点 之后删除最后一个节点元素
         */
       root=elements[size-1];
       elements[size-1]=null;
       size--;
       siftDown(0);
       return root;
    }

    /**
     *当我们进行交换之后 把最后一个元素值交换到堆顶
     * 很有可能已经违背了我们的大顶堆的原则:父节点的元素值大于子节点(们)的元素值
     * 另外给定几个公式:
     * 1.假设有n个节点  那么非叶子节点的个数为floor(n/2)
     * 2.假设一个节点索引为i  那么左子:2*i+1  右子:2*i+1+1   父节点:(i-1)/2
     */
    private void siftDown(int index) {
        E element=elements[0];
        int half=size>>1;//非叶子节点的数量
        while (index<half){
            //默认是左边的子节点与父节点比较 那么这里childindex默认为左子的索引下标
            int childindex=2*index+1;
            E childelement=elements[childindex];
            int childindexright=2*index+1+1;//即是childindexleft+1
            //右子节点与左进行比较一下
            //1.判断右子的索引范围
            // 2.如果相等那么就默认取左边即可
            if(childindexright<half&&compare(elements[childindexright],childelement)>0){
                childelement=elements[childindexright];
                childindex=childindexright;
            }
            if(compare(childelement,element)<=0){
                break;//退出循环
            }
            index=childindex;
        }
        elements[index]=element;
    }

 repalce:

删除一个堆顶元素并且再增加一个元素

操作:直接用这个增加的元素代替堆顶元素 

然后再把这个元素进行下滤操作

 

  /**
     *删除堆顶元素并且增加一个元素
     * 方法:把这个增加的元素直接代替堆顶元素
     * 然后把堆顶元素直接进行下溢操作
     */
    @Override
    public E replace(Object element){
        E root=null;
        if(size==0){
           elements[0]= (E) element;
            size++;
        } else {
            root=elements[0];
            elements[0]= (E) element;
            siftDown(0);
        }
        return root;
    }

 

1.

 这种操作每一次都把一部分的元素变成最大堆

如图画红圈的都是每一次变化之后对应的最大堆的形式。。。。

 总结:每完成一次上滤操作 就会让最大堆变得大一点 

最终让整个堆都变成最大堆。。。

这两种做法的效率对比:

 由图可见: 

自上而下的上滤操作  下面的基数比较大

因此:进行的操作比较多,,,,,,

因此后者更优。。。。。。。。。。。

 

总结:

前者时间复杂度为:O(nlogn)  自上而下的上滤类似于添加add

后者为O(n) 这种自下而上的下滤操作类似于remove删除操作

 代码:

private E[] elements;
    public BinaryHeap(Comparator<E> comparator,E[] elements){
        super(comparator);//调用父类的比较器即可_super表示调用父类的属性操作
        if(elements==null||elements.length==0){
            this.elements=(E[])new Object[DEFAULT_CAPACITY];
        } else {
            int capacity=DEFAULT_CAPACITY>elements.length?DEFAULT_CAPACITY:elements.length;
            this.elements=(E[])new Object[capacity];
            /**
             * 进行深拷贝。。。。。
             */
            for (int i = 0; i < elements.length; i++) {
                this.elements[i]=elements[i];
            }
            heapify();
        }
    }

    private void heapify() {
      /*  //自上而下的上滤
        for (int i = 0; i < elements.length; i++) {
            siftUp(i);
        }*/
        //自下而上的下滤效率较高
        for (int i =(size>>1-1); i>=0; i--) {
            siftDown(i);
        }
    }

 细节:

当我们外部new一个对象时  传参数传过去一个数组到我们的构造器那里

我们要进行深拷贝。。。。

因为有可能外部进行修改数组的内容会改变

深拷贝会在内存中申请另外一块独立的空间  避免被外部操作而修改

测试类:::::::::::::::::::::::::如下::—leomessi—

 

 如何搞出小顶堆?

重写这个比较器即可,,,,,,,匿名内部类:::

 Top K问题:

代码:

private E[] elements;
    public BinaryHeap(Comparator<E> comparator,E[] elements){
        super(comparator);//调用父类的比较器即可_super表示调用父类的属性操作
        if(elements==null||elements.length==0){
            this.elements=(E[])new Object[DEFAULT_CAPACITY];
        } else {
            int capacity=DEFAULT_CAPACITY>elements.length?DEFAULT_CAPACITY:elements.length;
            this.elements=(E[])new Object[capacity];
            /**
             * 进行深拷贝。。。。。
             */
            for (int i = 0; i < elements.length; i++) {
                this.elements[i]=elements[i];
            }
            heapify();
        }
    }

    private void heapify() {
      /*  //自上而下的上滤
        for (int i = 0; i < elements.length; i++) {
            siftUp(i);
        }*/
        //自下而上的下滤效率较高
        for (int i =(size>>1-1); i>=0; i--) {
            siftDown(i);
        }
    }

 思路:

1.首先先模拟出一个小顶堆  测试类中重写比较策略即可。。。。

 2.把前k个元素放入这个小顶堆中

3.运用heap.get() 每一次拿出堆顶的元素与从第k+1个元素开始进行比较

如果第k+1个元素开始  一旦这个新来的元素大于堆顶元素

那么进行replace操作

何为replace?

把堆顶元素替换删除并且下滤堆顶元素

4.每一次的操作都会使这个堆的元素进一步的整体范围增大。。。。

5.最终在堆中的k个元素就是我们要找的k个元素。。。。。

时间复杂度:

O(nlogk)相对于快速排序的O(nlogn)更优秀了多。。。。

 

 

 

标签:index,elements,int,元素,二叉,length,size
来源: https://blog.csdn.net/m0_61784000/article/details/121716265

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有