java---堆的五种操作---数组模拟实现(每日一道算法2022.8.15)

题目
维护一个集合,初始时集合为空,支持如下几种操作:

I x,插入一个数 x;
PM,输出当前集合中的最小值;
DM,删除当前集合中的最小值(数据保证此时的最小值唯一);
D k,删除第 k 个插入的数;
C k x,修改第 k 个插入的数,将其变为 x;

输入:
操作次数
8
具体操作
I -10
PM
I -10
D 1
C 2 8
I 6
PM
DM
输出:
-10
6
public class 堆_模拟堆 {
    //初始化,heap存储堆的数据,hp存储的是第k个插入的点的堆下标,ph存储的是堆里面的某一个点是第几个插入的点
    //size代表当前堆的元素数量,m代表第k个插入的数的下标
    public static int N = 100010, size = 0, m = 0;
    public static int[] heap = new int[N], hp = new int[N], ph = new int[N];

    public static void main(String[] args) {
        //初始化
        Scanner in = new Scanner(System.in);
        int times = Integer.parseInt(in.nextLine());

        //五种功能
        while (times-- > 0) {
            String[] data_arr = in.nextLine().split(" ");
            String model = data_arr[0];

            if (model.equals("PM")) {
                //直接拿到堆顶,就是最小的
                System.out.println(heap[1]);
            }

            else if (model.equals("DM")) {
                //删除最小值就是删除堆顶,将堆顶和最后一个值交换,然后size--,相当于逻辑删除,然后从堆顶down一遍
                swap_heap(1, size--);
                down(1);
            }

            else if (model.equals("C")) {
                int k = Integer.parseInt(data_arr[1]), x = Integer.parseInt(data_arr[2]);
                //用ph[k]拿到值的在堆中的下标,将堆中下标为k的替换为x,然后down或者up
                k = ph[k];
                heap[k] = x;
                up(k);down(k);
            }

            else {
                int x = Integer.parseInt(data_arr[1]);
                if (model.equals("I")) {
                    //ph和hp是相反的,heap插入就很简单了,插在末尾然后up
                    size++;
                    m++;
                    ph[m] = size; hp[size] = m;
                    heap[size] = x;
                    up(size);
                }
                else if (model.equals("D")) {
                    //用ph[k]拿到值的在堆中的下标, 和最后一个值交换,然后size--,相当于逻辑删除,down或者up
                    x = ph[x];
                    swap_heap(x, size--);
                    up(x);down(x);
                }
            }
        }
    }

    //注意和堆排序那道题不一样,这里的swap使用的是堆的特别swap
    public static void down(int u) {
        int t = u;
        if (u*2<=size && heap[u*2]<heap[t]) {t = u*2;}
        if (u*2+1<=size && heap[u*2+1]<heap[t]) {t = u*2+1;}
        if (u!=t) {
            swap_heap(u, t);
            down(t);
        }
    }

    //判断如果当前值大于其父值,交换, 切记是当父值比当前值大的时候交换
    public static void up(int u) {
        while (u/2 > 0 && heap[u]<heap[u/2]) {
            swap_heap(u, u/2);
            u = u / 2;
        }
    }

    //堆的特有swap方式,swap值的同时,将hp和ph值进行交换,达到彻底交换
    public static void swap_heap(int x, int y) {
        swap(ph, hp[x], hp[y]);         //交换ph的值,切记先交换ph值,因为我们需要用hp的值来找到ph中的值
        swap(hp, x, y);                 //交换hp值
        swap(heap, x, y);           //交换heap,也就是堆中的值
    }

    //每次都单独写swap操作太麻烦而且可读性差,就单独写个方法吧,再次吐槽java没有swap方法啊啊啊啊!!!!
    public static void swap(int[] arr, int x, int y) {
        int temp = arr[x];
        arr[x] = arr[y];
        arr[y] = temp;
    }
}

大佬13333409541的解释非常棒: (借用一下哎嘿~)

1、理解hp与ph数组,以及为什么需要它们
* 堆h[i]只能存放数据,不能存放是第几个数字,所以需要ph[k] = i来指明,第k个数字在h[]中对应的i是多少
* 在执行交换操作的时候,可以直接交换数字,swap(h[a],h[b])
但是对于ph[k_1] = a和ph[k_2] = b来说,a和b是它们存放的值,不 能通过值来找下标,也就是找不k_1,k_2是多少
* 于是引入hp[a] = k_2,hp[b] = k_2,则可以实现反向的操作

2、形象理解heap_swap中的次序是任意的
h[]:房间号无直接实际意义,里边住着犯人
ph[]:花名册,狱警所有,写明了几号犯人住在哪个房间号里,用于抓某些人
(但是狱警无权过问每个号里住的是谁)
hp[]:住户册,监狱所有,写明了哪个房间号里住的是几号,用于管理监狱
(但是监狱没必要知道哪个犯人住在哪里)
heap_swap:已知两个犯人住的地方,交换它们住的地方,并且让狱警和管理 处都知道这件事情
swap(h[a], h[b]):两个人换地方住
swap(hp[a], hp[b]):监狱管理处翻房间号,把里边存放的犯人号交换
swap(ph[hp[a]], ph[hp[b]]):狱警:先申请查住户册,看这两个地方住的谁,再在花名册下写下来,这两个人位置换了
h[a] = 10, h[b] = 20 swap: h[a] = 20,h [b] = 10
hp[a] = 1 ,hp[b] = 2 swap:hp[a] = 2 ,hp[b] = 1
ph[1] = a ,ph[2] = b swap:ph[1] = b ,ph[2] = a
//这种不变形也很像线代中:代表交换的初等矩阵,进行逆运算之后,仍然是该初等矩阵

声明:算法思路来源为y总,详细请见https://www.acwing.com/
本文仅用作学习记录和交流

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值