数据结构-堆(附算法题以及堆实现程序)

声明:
1.转载请注明文章出处
2.本文讲述的是二叉堆

堆的概念

堆的本质上是一种完全二叉树,分为:

最小堆(小根堆):树中每个非叶子结点都不大于其左右孩子结点的值,也就是根节点最小的堆,如下图:
在这里插入图片描述

最大堆(大根堆):树中每个非叶子结点都不小于其左右孩子结点的值,也就是根节点最大的堆,如下图:
在这里插入图片描述

堆的存储方式

堆的本质虽然是一颗二叉树,但其存储方式确比较简单,直接用数组进行存储:如上面两个堆的元素在数组中的存储详细为:

大根堆:
在这里插入图片描述

小根堆:
在这里插入图片描述
那我们如何获取到父节点以及左右孩子节点呢?这里分为两种情况(i为当前节点):

当索引从0开始时:
父节点: (i-1) / 2
左孩子: 2 * i + 1
右孩子: 2 * i + 2

当索引从1开始时:
父节点:i / 2
左孩子:2 * i
右孩子:2 * i + 1


堆的操作

1.插入(大根堆)

当往堆中插入一个元素时,便将此元素放在堆中的最后一个位置,即数组的最后一个元素,然后将此元素与他的父节点比较(如果有父节点),如果比父节点大,就与父节点进行交换,以此类推,直到小于等于父节点或者成为根节点。

在这里插入图片描述

2.删除根节点(大根堆)

删除根节点后,直接将堆中的最后一个元素移到根节点,然后将根节点与他的左右孩子进行对比,如果只有左孩子,就只跟左孩子对比(前提要有子节点),如果根节点比他的子节点要小,则进行交换,有两个子节点,则与较大字节点交换,以此类推,直到目标节点比他的子节点都大或者目标节点成为叶子节点。

在这里插入图片描述

堆的构造

给定一个无序的数组,我们如何将其构造成一个大根堆或者小根堆呢?

答案:从最后一个非叶子节点开始遍历,依次遍历每个非叶子节点,每当遍历到非叶子节点的时候,就将目标节点与他的子节点进行比较(这里目标节点肯定有子节点),符合条件就进行交换,交换后继续与他的子节点进行比较(前提要有子节点),直到目标节点通过比较后不用进行交换或者成为叶子节点。

不同情况下最后一个非叶子子节点的索引:
当索引从0开始时:堆中元素个数 / 2 - 1。
当索引从1开始时:堆中元素个数 / 2。

下面将其构造成大根堆:

在这里插入图片描述

在这里插入图片描述

堆排序

步骤:

1.将无序数组构造成堆结构(大根堆对应升序,小根堆对应降序)
2.将堆中的根节点和最后一个节点进行交换,然后将被换到末尾的原本是根节点的末尾节点进行固定(即相当于将它移除,不用去管它),再将目前的根节点进行堆调整(方法和删除根节点的调整一样)。
3.调整完毕后,除了固定的节点,其他节点都符合堆结构。再此执行步骤2,直到堆中节点全部固定完。

下面是给定数组:
在这里插入图片描述
步骤1:堆的构造(见上方)
步骤2 and 步骤3 如下图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

算法题(来源力扣)

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。

class MedianFinder {
    Queue<Integer> A, B;
    public MedianFinder() {
        A = new PriorityQueue<>(); // 小顶堆,保存较大的一半
        B = new PriorityQueue<>((x, y) -> (y - x)); // 大顶堆,保存较小的一半
    }
    public void addNum(int num) {
        if(A.size() != B.size()) {
            A.add(num);
            B.add(A.poll());
        } else {
            B.add(num);
            A.add(B.poll());
        }
    }
    public double findMedian() {
        return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0;
    }
}

作者:jyd

堆实现程序

下面附上手动实现的堆类。

import java.util.Arrays;
import java.util.Random;


public class Demo08 {
    public static void main(String[] args) {
        Heap heap = new Heap();
        //随机添加150个Integer
        for(int i=0;i<150;++i){
            heap.add(new Random().nextInt(10000));
        }
        //输出
        heap.getString();
        //返回堆排序结果
        Integer[] result = heap.sort();
        //输出堆排序结果
        for(int i=0;i<result.length;++i){
            System.out.print(result[i]+" ");
        }
        System.out.println();
        //移除堆顶75次
        for(int i=0;i<75;++i){
            heap.removeTop();
        }
        //再次返回堆排序结果
        result = heap.sort();
        //再次输出堆排序结果
        for(int i=0;i<result.length;++i){
            System.out.print(result[i]+" ");
        }
        System.out.println();

        System.out.println("=====================");

        Heap heap1 = new Heap(true);
        //随机添加150个Integer
        for(int i=0;i<150;++i){
            heap1.add(new Random().nextInt(10000));
        }
        //输出
        heap1.getString();
        //返回堆排序结果
        Integer[] result1 = heap1.sort();
        //输出堆排序结果
        for(int i=0;i<result1.length;++i){
            System.out.print(result1[i]+" ");
        }
        System.out.println();
        //移除堆顶75次
        for(int i=0;i<75;++i){
            heap1.removeTop();
        }
        //再次返回堆排序结果
        result1 = heap1.sort();
        //再次输出堆排序结果
        for(int i=0;i<result1.length;++i){
            System.out.print(result1[i]+" ");
        }
        System.out.println();

        System.out.println("=====================");

        Integer[] arr = new Integer[10];
        //随机添加150个元素到数组中
        for(int i=0;i<10;++i){
            arr[i] = new Random().nextInt(10000);
        }
        //将数组构造成大顶堆
        heap.arrHeap(arr);
        //输出数组
        for(int i=0;i<10;++i){
            System.out.print(arr[i]+" ");
        }
        System.out.println();
        //将数组构造成小顶堆
        heap1.arrHeap(arr);
        //输出数组
        for(int i=0;i<10;++i){
            System.out.print(arr[i]+" ");
        }
        System.out.println();

    }
}
class Heap{
    //存储堆中元素的数组
    private Integer[] integers;
    //堆中元素的个数
    private int size = 0;
    //堆的默认大小
    private final static int DELAULT_SIZE = 100;
    //指定堆的特征(大根堆:false,小根堆:true)
    private Boolean type = false;

    //普通的构造方法
    public Heap(){
        integers = new Integer[size];
    }

    //根据参数type选择创建大根堆还是小根堆
    public Heap(boolean type){
        this(DELAULT_SIZE,type);
    }

    //根据参数size创建指定大小的堆
    public Heap(int size){
        integers = new Integer[size];
    }

    //根据参数size和type创建指定大小以及堆特性的堆
    public Heap(int size, Boolean type){
        integers = new Integer[size];
        this.type = type;
    }


    //当堆中元素超过堆的大小,进行扩容
    private void grow(int minCapacity){
        //扩大为原来的两倍
        int newCapacity = minCapacity * 2;
        integers = Arrays.copyOf(integers,newCapacity);
    }

    //根据元素以及其索引进行上调
    private void siftup(Integer[] integers,int i,Integer e){
        int parent;
        Integer x;
        while(i > 0){
            parent = (i - 1) / 2;
            x = integers[parent];
            if(compare(e,x) <= 0){
                break;
            }
            integers[i] = x;
            i = parent;
        }
        integers[i] = e;
    }

    //根据元素,索引以及堆的范围进行下浮
    private void siftdown(Integer[] integers,int k,Integer e,int length){
        int nowIndex = k,childIndex;
        Integer bigger;
        while(true){
            //是否有两个子节点存在
            if((nowIndex*2+2)<=length){
                if(compare(integers[nowIndex*2+1],integers[nowIndex*2+2]) >= 0){
                    bigger = integers[nowIndex*2+1];
                    childIndex = nowIndex * 2 + 1;
                }else{
                    bigger = integers[nowIndex*2+2];
                    childIndex = nowIndex * 2 + 2;
                }
                //是否只有左节点存在
            }else if((nowIndex*2+1)<=length){
                bigger = integers[nowIndex*2+1];
                childIndex = nowIndex * 2 + 1;
            }else{
                //没有子节点,则直接退出循环
                break;
            }
            //比较是否需要替换,不需要则退出循环
            if(compare(e,bigger) > 0){
                break;
            }
            integers[nowIndex] = bigger;
            nowIndex = childIndex;
        }
        integers[nowIndex] = e;
    }

    //根据堆的特性进行堆中元素的比较
    private int compare(Integer x,Integer y){
        if(!type){
            return x - y;
        }else{
            return y - x;
        }
    }

    //往堆中添加元素
    public boolean add(Integer e){
        if(e == null){
            throw new NullPointerException();
        }
        int i = size;
        //扩容数组的大小
        if(i >= integers.length){
            grow(i+1);
        }
        size = i + 1;
        //数组为空则直接赋值
        if(i == 0){
            integers[0] = e;
        }else{
            siftup(integers,i,e);
        }
        return true;
    }


    //移除堆顶元素
    public Integer removeTop(){
        if(size == 0){
            throw new NullPointerException();
        }
        Integer top = integers[0];
        integers[0] = integers[size-1];
        size--;
        //将堆顶元素进行下调,使其满足堆的性质
        siftdown(integers,0,integers[0],size-1);
        return top;
    }

    //对堆的副本进行排序,并返回排序后的结果
    public Integer[] sort(){
        Integer[] sorted = new Integer[size];
        Integer temp;
        //拷贝一份堆中元素到副本中
        for(int i=0;i<size;++i){
            sorted[i] = integers[i];
        }
        //堆排序
        for(int i=sorted.length-1;i>=0;){
            temp = sorted[0];
            sorted[0] = sorted[i];
            sorted[i] = temp;
            i--;
            siftdown(sorted,0,sorted[0],i);
        }
        return sorted;
    }

    //输出堆的内容
    public void getString(){
        for(int i=0;i<size;i++){
            System.out.print(integers[i]+" ");
        }
        System.out.println();
    }

    //对数组进行构造堆
    public void arrHeap(Integer[] arr){
        for(int i=arr.length/2-1;i>=0;--i){
            siftdown(arr,i,arr[i],arr.length-1);
        }
    }
}

运行结果:

9853 9826 9805 9765 9765 9763 9264 8776 9759 9437 9665 8971 9570 8732 7362 8183 8695 9263 8137 5695 9082 8909 9437 8289 8499 7906 9553 7852 8555 6505 7165 4255 7837 7129 8431 8976 9161 8006 6015 4488 4596 6864 7928 6152 8101 6213 7209 7689 7807 6360 7176 7402 6807 5956 9441 7184 4056 6023 5147 5299 5355 5696 6746 2104 3175 5229 7652 7048 6866 6110 6970 6065 7883 4730 3934 3579 1731 3665 3856 1401 2934 1065 136 82 5366 3001 7859 397 5458 4064 8006 1855 756 4284 4407 389 5494 4002 1759 6314 721 1243 2218 782 1551 2740 4532 1662 3265 6395 7874 1428 3384 3339 4048 2709 4095 502 221 378 2287 3457 979 1254 5648 5561 2645 13 25 2966 1496 147 4478 2497 6684 1113 2803 3967 5792 1931 1269 1620 3304 3565 5397 829 5949 2325 4081 2572 
13 25 82 136 147 221 378 389 397 502 721 756 782 829 979 1065 1113 1243 1254 1269 1401 1428 1496 1551 1620 1662 1731 1759 1855 1931 2104 2218 2287 2325 2497 2572 2645 2709 2740 2803 2934 2966 3001 3175 3265 3304 3339 3384 3457 3565 3579 3665 3856 3934 3967 4002 4048 4056 4064 4081 4095 4255 4284 4407 4478 4488 4532 4596 4730 5147 5229 5299 5355 5366 5397 5458 5494 5561 5648 5695 5696 5792 5949 5956 6015 6023 6065 6110 6152 6213 6314 6360 6395 6505 6684 6746 6807 6864 6866 6970 7048 7129 7165 7176 7184 7209 7362 7402 7652 7689 7807 7837 7852 7859 7874 7883 7906 7928 8006 8006 8101 8137 8183 8289 8431 8499 8555 8695 8732 8776 8909 8971 8976 9082 9161 9263 9264 9437 9437 9441 9553 9570 9665 9759 9763 9765 9765 9805 9826 9853 
13 25 82 136 147 221 378 389 397 502 721 756 782 829 979 1065 1113 1243 1254 1269 1401 1428 1496 1551 1620 1662 1731 1759 1855 1931 2104 2218 2287 2325 2497 2572 2645 2709 2740 2803 2934 2966 3001 3175 3265 3304 3339 3384 3457 3565 3579 3665 3856 3934 3967 4002 4048 4056 4064 4081 4095 4255 4284 4407 4478 4488 4532 4596 4730 5147 5229 5299 5355 5366 5397 
=====================
6 90 62 186 320 131 97 897 652 1666 898 1012 292 1612 129 1187 1229 989 2532 2155 1979 1240 1652 3898 1686 871 381 2459 1726 2236 3775 1858 2969 2188 2242 1319 2905 3302 2732 4563 3630 3725 3868 3372 3198 5009 3190 5031 4724 2033 3419 2888 2356 2982 3272 6843 5801 5390 3398 3292 3101 5084 4163 2282 6354 3387 3336 4206 2831 3217 2846 4079 2856 5173 5304 8986 4240 6654 8525 6866 9110 8926 6446 8472 7922 7952 3893 5850 5528 7676 5550 7466 9960 5622 4242 8916 7835 5441 4979 4381 3098 7851 5444 9055 5709 9043 3633 9561 5926 7636 6411 9213 8220 9910 9041 7852 6278 6223 4086 6268 8797 9526 9199 6273 5319 5401 4837 8579 7179 7802 7602 8581 3433 6817 4889 8735 7967 7908 6852 8860 9875 8170 9523 9254 7266 4810 7963 9385 9630 6121 
9960 9910 9875 9630 9561 9526 9523 9385 9254 9213 9199 9110 9055 9043 9041 8986 8926 8916 8860 8797 8735 8581 8579 8525 8472 8220 8170 7967 7963 7952 7922 7908 7852 7851 7835 7802 7676 7636 7602 7466 7266 7179 6866 6852 6843 6817 6654 6446 6411 6354 6278 6273 6268 6223 6121 5926 5850 5801 5709 5622 5550 5528 5444 5441 5401 5390 5319 5304 5173 5084 5031 5009 4979 4889 4837 4810 4724 4563 4381 4242 4240 4206 4163 4086 4079 3898 3893 3868 3775 3725 3633 3630 3433 3419 3398 3387 3372 3336 3302 3292 3272 3217 3198 3190 3101 3098 2982 2969 2905 2888 2856 2846 2831 2732 2532 2459 2356 2282 2242 2236 2188 2155 2033 1979 1858 1726 1686 1666 1652 1612 1319 1240 1229 1187 1012 989 898 897 871 652 381 320 292 186 131 129 97 90 62 6 
9960 9910 9875 9630 9561 9526 9523 9385 9254 9213 9199 9110 9055 9043 9041 8986 8926 8916 8860 8797 8735 8581 8579 8525 8472 8220 8170 7967 7963 7952 7922 7908 7852 7851 7835 7802 7676 7636 7602 7466 7266 7179 6866 6852 6843 6817 6654 6446 6411 6354 6278 6273 6268 6223 6121 5926 5850 5801 5709 5622 5550 5528 5444 5441 5401 5390 5319 5304 5173 5084 5031 5009 4979 4889 4837 
=====================
7637 5522 6007 4818 4264 2772 3682 2494 4279 578 
578 2494 2772 4279 4264 6007 3682 4818 7637 5522 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值