模拟堆 优先堆 java写一个堆

文章讲述了如何使用一维数组实现堆数据结构,包括插入新元素时保持堆的性质(最小堆),以及删除、修改操作时通过up和down方法调整数组以保持堆的最小值在顶部。
摘要由CSDN通过智能技术生成

关于堆,堆其实就是一个完全二叉树。其次,如果把这个完全二叉树,从上到下,从左到右的排序的,它可以存放在一个一维数组中,而如果一个节点的index是x的话,那么它的左子树就刚好是2x,右子树是2x+1所以因为有这层关系,也就奠定了可以用一维数组来存贮的,每次找左子树的时候就把当先节点的下标*2就可以在数组中找到左子树的值了。之后来看这个最小堆的特点,之后根据它的特点以及功能用代码实现它

1,I x 能够插入数,并会在插入的时候自动把堆中的数组动态的重组,让堆中最小的值存储在堆顶部

2,PM 可以输出当前集合中的最小值,这简单,我们写的就是最小堆,所以直接输出数组第一个值就好了。

3,DM 可以删除当前集合中最小的值,并且还要删除成功后,自动把堆中的数组动态的重组,让堆中最小的值存储在堆顶部

4,D k 可以删除指定数,比如删除第k个插入的数

5,C k x 可以修改第k个插入的数,将其变为x,

第四第五也同样要符合要求,可以自动把堆中的数组动态的重组,让堆中最小的值存储在堆顶部

首先我们既然是用数组存储的就必须要有一个足够大的数组,然后还要存储一个size变量,用于存储这个堆一共有多大,

这五个点中都有一个共同的要求就是:动把堆中的数组动态的重组,让堆中最小的值存储在堆顶部,那么怎么做才能够让数组会是以堆的样子排序,让最小的值存储在最顶端呢?

我这里是写了一共up方法和一共down方法。他们的功能就是当你在一共完美的堆中,中突然插入一个数u,那么就要判断这个u是要向上移动还是向下移动,用这个u和它的左子树和右子树都做比较,如果大于子树的话,就让它和子树交换值,并递归子树,直到它不在大于左子树和右子树,或者到底最低端,此时,这个u所在的地方也就符合一定小于父节点,一定大于子节点。而up也是相同的道理,用它和他的父节点作比较,看它是否需要向上移动。

    static void down(int u){
        int t = u;
        //让他跟左子树的值最比较
        if(u*2 <= size && h[u*2] < h[t])t = u*2;
        //让他与右子树的值作比较  ,如果大于右子树,就把t存储为右子树的值
        if(u*2+1 <= size && h[u*2+1] < h[t])t = u*2+1;
        //如果u != t 也就是上面两个判断中,让t的值发生了变化,也就是说他是比左子树大或者右子树大的
        if(u != t){
            //那就将它与那个子树交换值,然后递归down(t)那个子树
            int temp = h[u];
            h[u] = h[t];
            h[t] = temp;
            down(t);
        }
    }
    static void up(int u){
        int t = u;
        if(u/2 > 0 && h[u/2] > h[t]) t = u/2;
        if (u != t){
            int temp = h[u];
            h[u] = h[t];
            h[t] = temp;
            up(t);
        }
    }

如果我们实现了这两个方法,这样在存储值到堆中,只需要直接把它放在最末尾,然后给他up(size)即可。他会根据up方法中的递归,自动给的上升到它应该在的位置。

static void insert(int x){
    h[++size] = x;
    up(size);
}

如果是删除当前数组的最小的值,那就让把最后一个数跟第一个数给交换位置,再把最后一个数给删除,把第一个数使用函数down(size)即可自动下降到它应该在的位置

static void delete(){
    h[1] = h[size];
    size--;
    down(1);
}

所以关于修改第k个数或者删除第k个数也是类似的,最后就把完整的代码附上。


import java.util.Scanner;

public class 模拟堆 {
    static int N = 100010;
    static int h[] = new int[N];
    static int size;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();
        while (m-- > 0){
            String a= sc.next();
            switch (a){
                case "I":
                    int x = sc.nextInt();
                    insert(x);
                    break;
                case "PM":
                    pr();
                    break;
                case "DM":
                    delete();
                    break;
                case "D":
                    int k = sc.nextInt();
                    deletek(k);
                    break;
                default:
                    int k1 = sc.nextInt();
                    int x1 = sc.nextInt();
                    update(k1,x1);
            }
        }
    }
    static void down(int u){
        //先把这个节点的最初的值给赋值给t,目的是看待到比较完字数后,t的值会不会发生变化。
        int t = u;
        //让他跟左子树的值最比较
        if(u*2 <= size && h[u*2] < h[t])t = u*2;
        //让他与右子树的值作比较  ,如果大于右子树,就把t存储为右子树的值
        if(u*2+1 <= size && h[u*2+1] < h[t])t = u*2+1;
        //如果u != t 也就是上面两个判断中,让t的值发生了变化,也就是说他是比左子树大或者右子树大的
        if(u != t){
            //那就将它与那个子树交换值,然后递归down(t)那个子树
            int temp = h[u];
            h[u] = h[t];
            h[t] = temp;
            down(t);
        }
    }
    static void up(int u){
        int t = u;
        if(u/2 > 0 && h[u/2] > h[t]) t = u/2;
        if (u != t){
            int temp = h[u];
            h[u] = h[t];
            h[t] = temp;
            up(t);
        }
    }
    //插入一棵树
    static void insert(int x){
        h[++size] = x;
        up(size);
    }
    //输出当前集合中的最小值
    static void pr(){
        System.out.println(h[1]);
    }
    //删除当前集合中的最小值
    static void delete(){
        h[1] = h[size];
        size--;
        down(1);
    }
    //删除第k个插入的数
    static void deletek(int k){
        h[k] = h[size];
        size--;
        down(k);up(k);
    }
    //修改第k个插入的数,将其变为x
    static void update(int k,int x){
        h[k] = x;
        down(k);up(k);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值