-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDeque.java
289 lines (259 loc) · 8.47 KB
/
Deque.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
package collection;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
/**
* java.util.ArrayDeque<T> の簡易版.次の操作を行うことが出来る
*
* - 先頭/末尾の追加: amortized O(1)
* - 先頭/末尾の取得: O(1)
* - 先頭/末尾の削除: O(1)
* - ランダムアクセス: O(1) (ArrayDeque では O(N))
*
* 実装は Ring Buffer による.
*
* verified:
* - http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP2_1_B
* - https://atcoder.jp/contests/arc005/tasks/arc005_3
*
* @author https://atcoder.jp/users/suisen
* @param <T> Deque に格納するデータの型
*/
@SuppressWarnings("unchecked")
public class Deque<T> implements Iterable<T>, RandomAccess {
/**
* コンストラクタで初期容量を指定しなかった場合の初期容量
*/
static final int DEFAULT_CAPACITY = 1 << 6;
/**
* Ring Buffer.剰余算を高速化するためにサイズは 2 冪になるようにする.
*/
T[] buf;
/**
* buf のサイズ.
*/
int len = 1;
/**
* 剰余算の代わりに行う論理積演算に用いる mask.
*/
int mask;
/**
* Deque の先頭要素の index.
* 0 <= head < len は保証されていないので,Deque には mask を通してアクセスする.
*/
int head = 0;
/**
* Deque の末尾要素の index + 1.つまり,[head, tail) の半開区間に要素が入っている.
* 0 <= tail-1 < len は保証されていないので,Deque には mask を通してアクセスする.
*/
int tail = 0;
/**
* 初期容量を与えて初期化する.
* 予め必要な容量が分かっている場合はその値を用いて初期化するとメモリ使用量が減る.
* また,最大容量を与えた場合は追加操作が償却ではなく真に定数時間で行える.
* @param capacity 初期容量
*/
public Deque(int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException(
String.format("Capacity %d is negative.", capacity)
);
}
while (this.len < capacity) {
this.len <<= 1;
}
this.mask = this.len - 1;
this.buf = (T[]) new Object[len];
}
/**
* 初期容量をデフォルト値 {@code DEFAULT_CAPACITY = 64} で初期化する.
* 必要容量の見積もりがつかない場合はこれを使う.
*/
public Deque() {
this(DEFAULT_CAPACITY);
}
/**
* Deque の末尾要素を取得する.O(1)
* @return 末尾要素
* @throws NoSuchElementException 要素数が 0 の場合
*/
public T getLast() {
if (size() == 0) throw new NoSuchElementException();
return buf[(tail - 1) & mask];
}
/**
* Deque の先頭要素を取得する.O(1)
* @return 先頭要素
* @throws NoSuchElementException 要素数が 0 の場合
*/
public T getFirst() {
if (size() == 0) throw new NoSuchElementException();
return buf[head];
}
/**
* Deque へのランダムアクセス.O(1)
* @param index 先頭から何番目の要素を取得するか (0-indexed)
* @return 先頭から {@code index} 番目の要素 (0-indexed)
* @throws IndexOutOfBoundsException {@code index} が負であるか,または要素数以上である場合
*/
public T get(int index) {
if (index < 0 || index >= size()) {
throw new IndexOutOfBoundsException(
String.format("Index %d out of bounds for length %d.", index, size())
);
}
return buf[(head + index) & mask];
}
/**
* Deque の末尾に要素を追加する.amortized O(1)
* @param v 追加する要素.{@code null} を許容する.
*/
public void addLast(T v) {
if (size() == len) grow();
buf[tail++ & mask] = v;
}
/**
* Deque の先頭に要素を追加する.amortized O(1)
* @param v 追加する要素.{@code null} を許容する.
*/
public void addFirst(T v) {
if (size() == len) grow();
buf[--head & mask] = v;
}
/**
* Deque の末尾要素を削除する.O(1)
* @return 削除された要素
* @throws NoSuchElementException 要素数が 0 の場合
*/
public T removeLast() {
if (size() == 0) throw new NoSuchElementException();
return buf[--tail & mask];
}
/**
* Deque の先頭要素を削除する.O(1)
* @return 削除された要素
* @throws NoSuchElementException 要素数が 0 の場合
*/
public T removeFirst() {
if (size() == 0) throw new NoSuchElementException();
return buf[head++ & mask];
}
/**
* Deque の末尾要素を削除する.O(1)
* @return 末尾要素が存在した場合は削除された要素を返し,存在しない場合は {@code null} を返す.
*/
public T pollLast() {
if (size() == 0) return null;
return removeLast();
}
/**
* Deque の先頭要素を削除する.O(1)
* @return 先頭要素が存在した場合は削除された要素を返し,存在しない場合は {@code null} を返す.
*/
public T pollFirst() {
if (size() == 0) return null;
return removeFirst();
}
/**
* Deque の要素数を返す.O(1)
* @return 要素数
*/
public int size() {
return tail - head;
}
/**
* Deque が空であるかを判定する.O(1)
* @return 空であれば {@code true},そうでなければ {@code false}
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* Deque の要素を全て削除する.
*/
public void clear() {
head = tail = 0;
}
/**
* Deque の要素を先頭から順に格納した配列を生成する.
* @param clazz Deque に格納しているデータの {@code class}
* @return Deque の要素を先頭から順に格納した配列
*/
public T[] toArray(Class<T> clazz) {
T[] ret = (T[]) Array.newInstance(clazz, size());
Iterator<T> it = iterator();
Arrays.setAll(ret, i -> it.next());
return ret;
}
/**
* Ring Buffer の容量を 2 倍にする.
*/
private void grow() {
T[] newBuf = (T[]) new Object[len << 1];
head &= mask;
tail &= mask;
int len1 = len - head;
int len2 = head;
System.arraycopy(buf, head, newBuf, 0, len1);
System.arraycopy(buf, 0, newBuf, len1, len2);
this.head = 0;
this.tail = this.len;
this.len <<= 1;
this.mask = this.len - 1;
this.buf = newBuf;
}
/**
* @return 先頭要素から末尾要素までの順方向イテレータ
*/
public Iterator<T> iterator() {
return new Iterator<T>(){
int it = head;
public boolean hasNext() {return it < tail;}
public T next() {return buf[it++ & mask];}
};
}
/**
* @return 末尾要素から先頭要素までの逆方向イテレータ
*/
public Iterator<T> descendingIterator() {
return new Iterator<T>(){
int it = tail;
public boolean hasNext() {return it > head;}
public T next() {return buf[--it & mask];}
};
}
/***************************** DEBUG *********************************/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
Iterator<T> it = iterator();
while (it.hasNext()) {
sb.append(it.next().toString());
if (it.hasNext()) sb.append(',');
}
sb.append(']');
return sb.toString();
}
/******* Usage *******/
public static void main(String[] args) {
Deque<Integer> dq = new Deque<>();
dq.addLast(2);
dq.addLast(3);
dq.addLast(4);
System.out.println(dq);
dq.removeFirst();
dq.removeLast();
System.out.println(dq);
dq.addFirst(1);
dq.addFirst(0);
dq.addFirst(-1);
System.out.println(dq);
dq.clear();
System.out.println(dq);
System.out.println(dq.pollFirst()); // => null
// System.out.println(dq.removeFirst()); => NoSuchElementException
}
}