範囲に関係する問題は、多くの場合、ツリーの使用に役立ちます。を使用してそれを行う方法は次のTreeSet
とおりです。
public class DisjointChecker {
private final NavigableSet<Integer> integers = new TreeSet<Integer>();
public boolean check(int value) {
return integers.add(value);
}
public boolean check(int from, int to) {
NavigableSet<Integer> range = integers.subSet(from, true, to, true);
if (range.isEmpty()) {
addRange(from, to);
return true;
}
else {
return false;
}
}
private void addRange(int from, int to) {
for (int i = from; i <= to; ++i) {
integers.add(i);
}
}
}
ここでは、エラー ハンドラーを呼び出すのではなく、check
メソッドは引数が以前のすべての引数と素であったかどうかを示すブール値を返します。範囲バージョンのセマンティクスは元のコードとは異なります。範囲がばらばらでない場合、要素は追加されませんが、元の場合、最初のばらばらでない要素より下の要素が追加されます。
いくつかの点について詳しく説明する必要があります。
Set::add
追加によってセットが変更されたかどうかを示すブール値を返します。これをメソッドからの戻り値として使用できます。
NavigableSet
あいまいですが標準的なサブインターフェースでSortedSet
あり、悲しいことに無視されています。ただし、実際にはSortedSet
ここでわずかな変更を加えるだけでプレーンを使用できます。
NavigableSet::subSet
メソッド ( など) は、指定された範囲に制限された基になるセットのSortedSet::subSet
軽量ビューを返します。これにより、1 回の操作で範囲全体とのオーバーラップについてツリーをクエリする非常に効率的な方法が提供されます。
- ここでの
addRange
方法は非常に単純で、以前に n 個のアイテムを確認したチェッカーに m 個のアイテムを追加すると、O(m log n) で実行されます。O(m) で実行されるバージョンを作成するにはSortedSet
、整数の範囲を記述した の実装を記述してから を使用しますSet::addAll
。これの の実装には、線形時間でTreeSet
他の を追加する特別なケースが含まれているためです。SortedSet
その特別なセットの実装のコードは非常に単純ですが、多くのボイラープレートが含まれているため、読者の演習として残します。