题目
维护一个集合,初始时集合为空,支持如下几种操作:
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/
本文仅用作学习记录和交流