-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathPriorityQueue.java
293 lines (265 loc) · 8.96 KB
/
PriorityQueue.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
package collection;
import java.util.Comparator;
import java.util.NoSuchElementException;
/**
* java.util.PriorityQueue の簡易版.
* 二分ヒープで実装された優先度付きキューで,以下の操作を行うことが出来る.
*
* 1. 最小要素の取得.O(1)
* 2. 最小要素の削除.O(log N)
* 3. 任意要素の追加.O(log N)
*
* verified:
* - https://atcoder.jp/contests/arc098/tasks/arc098_c
* - https://atcoder.jp/contests/aising2020/tasks/aising2020_e
* - https://atcoder.jp/contests/abc167/tasks/abc167_f
*
* @author https://atcoder.jp/users/suisen
* @param <T> 優先度付きキューに格納するデータの型
*/
@SuppressWarnings("unchecked")
public class PriorityQueue<T> {
/**
* コンストラクタで初期容量を指定しなかった場合の初期容量
*/
static final int DEFAULT_CAPACITY = 1 << 6;
/**
* 二分ヒープは配列で表現する.1-indexed なので,i の左の子は 2*i,右の子は 2*i+1.
*/
T[] que;
/**
* 比較器.{@code T} が {@code Comparable<? super T>} 型であれば {@code null} でよい.
*/
final Comparator<? super T> comparator;
/**
* 要素数
*/
int size = 0;
/**
* 比較器を用いた比較を行う場合かつ必要な容量が予想できる場合のコンストラクタ.
* 初期容量を適切に設定することで要素のコピー回数を減らすことが出来る.
* @param capacity 初期容量
* @param comparator 比較器
*/
public PriorityQueue(int capacity, Comparator<? super T> comparator) {
int k = 1;
while (k < capacity) k <<= 1;
this.que = (T[]) new Object[k];
this.comparator = comparator;
this.size = 0;
}
/**
* 比較に比較器を用いない場合かつ必要な容量が予想できる場合のコンストラクタ.
* {@code T} が {@code Comparable<? super T>} 型で無ければ実行時エラーとなるので注意.
* 初期容量を適切に設定することで要素のコピー回数を減らすことが出来る.
* @param capacity 初期容量
*/
public PriorityQueue(int capacity) {
this(capacity, null);
}
/**
* 比較器を用いた比較を行う場合のコンストラクタ.初期容量はデフォルト値を用いる.
* @param comparator 比較器
*/
public PriorityQueue(Comparator<? super T> comparator) {
this(DEFAULT_CAPACITY, comparator);
}
/**
* 比較器を用いない場合のコンストラクタ.初期容量はデフォルト値を用いる.
* {@code T} が {@code Comparable<? super T>} 型で無ければ実行時エラーとなるので注意.
*/
public PriorityQueue() {
this(DEFAULT_CAPACITY, null);
}
/**
* 優先度付きキューに要素を追加する.
* @param e 追加する要素
*/
public void add(T e) {
if (++size == que.length) grow();
if (comparator != null) {
addUsingComparator(e);
} else {
addComparable((Comparable<? super T>) e);
}
}
/**
* {@code que} 配列に要素を収めきれない場合に呼ばれ,容量を 2 倍に増やす.
*/
void grow() {
T[] newQue = (T[]) new Object[que.length << 1];
System.arraycopy(que, 0, newQue, 0, que.length);
que = newQue;
}
/**
* 比較器を用いて要素を追加する.
* @param e 追加する要素
*/
void addUsingComparator(T e) {
int i = size;
while (i > 1) {
int p = i >> 1;
if (comparator.compare(e, que[p]) >= 0) break;
que[i] = que[i = p];
}
que[i] = e;
}
/**
* 比較器を用いないで要素を追加する.
* @param e 追加する要素
*/
void addComparable(Comparable<? super T> e) {
int i = size;
while (i > 1) {
int p = i >> 1;
if (e.compareTo(que[p]) >= 0) break;
que[i] = que[i = p];
}
que[i] = (T) e;
}
/**
* 先頭要素を削除し,削除した値を返す.但し,キューが空の場合は {@code null} を返す.
* @return 削除された先頭要素.ただし,キューが空の場合は {@code null}
*/
public T poll() {
if (size == 0) return null;
if (comparator != null) {
return pollUsingComparator();
} else {
return pollComparable();
}
}
/**
* 先頭要素を削除し,削除した値を返す.但し,キューが空の場合例外を投げる.
* @return 削除された先頭要素
* @throws NoSuchElementException キューが空の場合
*/
public T removeFirst() {
if (size == 0) throw new NoSuchElementException();
if (comparator != null) {
return pollUsingComparator();
} else {
return pollComparable();
}
}
/**
* 先頭要素を削除し,比較器を用いて二分ヒープの条件を回復する.
* @return 削除した要素
*/
T pollUsingComparator() {
T ret = que[1];
T e = que[size--];
int i = 1;
int h = size >> 1;
while (i <= h) {
int l = i << 1 | 0, r = i << 1 | 1;
if (r <= size) {
if (comparator.compare(que[l], que[r]) > 0) {
if (comparator.compare(e, que[r]) <= 0) break;
que[i] = que[i = r];
} else {
if (comparator.compare(e, que[l]) <= 0) break;
que[i] = que[i = l];
}
} else {
if (comparator.compare(e, que[l]) <= 0) break;
que[i] = que[i = l];
}
}
que[i] = e;
return ret;
}
/**
* 先頭要素を削除し,比較器を用いずに二分ヒープの条件を満たすように復帰する.
* @return 削除した要素
*/
T pollComparable() {
T ret = que[1];
Comparable<? super T> e = (Comparable<? super T>) que[size--];
int i = 1;
int h = size >> 1;
while (i <= h) {
int l = i << 1 | 0, r = i << 1 | 1;
if (r <= size) {
if (((Comparable<? super T>) que[l]).compareTo(que[r]) > 0) {
if (e.compareTo(que[r]) <= 0) break;
que[i] = que[i = r];
} else {
if (e.compareTo(que[l]) <= 0) break;
que[i] = que[i = l];
}
} else {
if (e.compareTo(que[l]) <= 0) break;
que[i] = que[i = l];
}
}
que[i] = (T) e;
return ret;
}
/**
* 先頭要素を削除せずに取得する.キューが空であれば {@code null} を返す.
* @return 先頭要素.キューが空の場合は {@code null}
*/
public T peek() {
return size == 0 ? null : que[1];
}
/**
* 先頭要素を削除せずに取得する.キューが空であれば例外を投げる.
* @return 先頭要素
* @throws NoSuchElementException キューが空の場合
*/
public T getFirst() {
if (size == 0) throw new NoSuchElementException();
return que[1];
}
/**
* キューの要素数を返す
* @return キューの要素数
*/
public int size() {
return size;
}
/**
* キューが空であるかを判定する
* @return キューが空なら {@code true},そうでなければ {@code false}
*/
public boolean isEmpty() {
return size == 0;
}
/**
* キューの要素をすべて削除する
*/
public void clear() {
size = 0;
}
/***************************** DEBUG *********************************/
@Override
public String toString() {
return toString(1, 0);
}
private String toString(int k, int space) {
String s = "";
if ((k << 1 | 1) <= size) s += toString(k << 1 | 1, space + 3) + "\n";
s += " ".repeat(space) + que[k];
if ((k << 1 | 0) <= size) s += "\n" + toString(k << 1 | 0, space + 3);
return s;
}
/******* Usage *******/
public static void main(String[] args) {
PriorityQueue<Integer> pq = new PriorityQueue<>(1);
pq.add( 3); pq.add( 4); pq.add( 1); pq.add(- 1);
pq.add( 10); pq.add( 14); pq.add( 30); pq.add(- 3);
pq.add(-13); pq.add( 32); pq.add( 13); pq.add( 7);
pq.add(- 7); pq.add( 12); pq.add(-29); pq.add(- 2);
pq.add( 0); pq.add( 1); pq.add( 10);
System.out.println(pq);
while (pq.size() > 0) {
System.out.print(pq.poll());
if (pq.size() > 0) {
System.out.print(", ");
} else {
System.out.println();
}
}
}
}