add intersection for unsorted lists

Changed the way the intersection is computed. An intersection does
not return duplicate values. So the result is like a set.
This commit is contained in:
2017-12-09 15:38:25 +01:00
parent b56f1e6688
commit 14181822c9
2 changed files with 143 additions and 27 deletions

View File

@@ -22,7 +22,6 @@ public final class IntList implements Serializable, Cloneable {
// TODO support sublists
// TODO add lastIndexOf
// TODO remove bounds checks that are handled by java, e.g. negative indices
// TODO intersection for unsorted lists
private static final long serialVersionUID = 2622570032686034909L;
@@ -361,12 +360,19 @@ public final class IntList implements Serializable, Cloneable {
* <p>
* This method does not release any memory. Call {@link #trim()} to free unused
* memory.
* <p>
* For a method that computes the intersection of two lists and also removes
* duplicate values, see {@link #intersection(IntList, IntList)}.
* <p>
* The algorithm has a complexity of O(n*log(m)), where n is the length of
* {@code this} and m the length of {@code retain}.
*
* @param retain
* the elements to retain
* @throws NullPointerException
* if the specified {@link IntList} is null
* @see #trim()
* @see #intersection(IntList, IntList)
*/
public void retainAll(final IntList retain) {
@@ -747,8 +753,17 @@ public final class IntList implements Serializable, Cloneable {
/**
* Returns a list with all elements that are in {@code a} and {@code b}.
* <p>
* The cardinality of each element will be equal to the minimum of the
* cardinality of each element in the given lists.
* The cardinality of each element will be equal to one (like in a set). This
* method returns a new list. The list can have unused capacity. Consider using
* {@link #trim()}.
* <p>
* See {@link #retainAll(IntList)} for a method that modifies the list and keeps
* duplicate values.
* <p>
* If both lists are sorted, then the complexity is O(n+m), where n is the
* length of the first list and m the length of the second list. If at least one
* list is not sorted, then the complexity is O(n*log(m)), where n is the length
* of the shorter list and m the length of the longer list.
*
* @param a
* a sorted {@link IntList}
@@ -758,15 +773,42 @@ public final class IntList implements Serializable, Cloneable {
* {@code b}
* @throws NullPointerException
* if {@code a} or {@code b} is null
* @see #retainAll(IntList)
* @see #trim()
*/
public static IntList intersection(final IntList a, final IntList b) {
final IntList result = new IntList(Math.min(a.size(), b.size()));
final IntList result;
if (a.isSorted() && b.isSorted()) {
result = intersectionSorted(a, b);
} else {
result = intersectionUnsorted(a, b);
}
return result;
}
/**
* Implements an intersection algorithm with O(n+m), where n is the length of
* the first list and m the length of the second list.
*
* @param a
* first list
* @param b
* second list
* @return the intersection
*/
private static IntList intersectionSorted(final IntList a, final IntList b) {
final int aSize = a.size();
final int bSize = b.size();
final IntList result = new IntList(Math.min(aSize, bSize));
assert a.isSorted() : "first list must be sorted";
assert b.isSorted() : "second list must be sorted";
int l = 0;
int r = 0;
while (l < a.size() && r < b.size()) {
while (l < aSize && r < bSize) {
final int lv = a.get(l);
final int rv = b.get(r);
@@ -777,13 +819,52 @@ public final class IntList implements Serializable, Cloneable {
r++;
} else {
result.add(lv);
do {
l++;
} while (l < aSize && lv == a.get(l));
do {
r++;
} while (r < bSize && rv == b.get(r));
}
}
} else {
throw new UnsupportedOperationException("retainAll on unsorted lists is not yet supported");
}
return result;
}
/**
* Implements an algorithm with O(n*log(m)), where n is the length of the
* shorter list and m the length of the longer list.
*
* @param a
* first list
* @param b
* second list
* @return the intersection
*/
private static IntList intersectionUnsorted(final IntList a, final IntList b) {
final int aSize = a.size();
final int bSize = b.size();
final IntList result;
if (aSize < bSize) {
result = new IntList(Math.min(aSize, bSize));
assert !a.isSorted() || !b.isSorted() : "at least one list must be unsorted";
for (int l = 0; l < aSize; l++) {
final int lv = a.get(l);
if (b.indexOf(lv) >= 0) {
result.add(lv);
}
while (l + 1 < aSize && lv == a.get(l + 1)) {
l++;
}
}
} else {
result = intersectionUnsorted(b, a);
}
return result;
}

View File

@@ -1094,21 +1094,56 @@ public class IntListTest {
Assert.assertEquals(IntList.of(2, 4), actual);
}
{
final IntList a = IntList.of(0, 2, 4, 6);
final IntList b = IntList.of(3, 5);
final IntList actual = IntList.intersection(a, b);
Assert.assertEquals(IntList.of(), actual);
}
/*
* cardinality of elements that occur multiple time is equal to the minimum
* cardinality in either list
* duplicate elements are removed in the result
*/
{
final IntList a = IntList.of(3, 3, 3);
final IntList b = IntList.of(3, 3);
final IntList actual = IntList.intersection(a, b);
Assert.assertEquals(IntList.of(3, 3), actual);
Assert.assertEquals(IntList.of(3), actual);
}
{
final IntList a = IntList.of(4);
final IntList a = IntList.of(4, 4);
final IntList b = IntList.of(4, 4, 4);
final IntList actual = IntList.intersection(a, b);
Assert.assertEquals(IntList.of(4), actual);
}
}
@Test
public void testIntersectionUnsortedLists() {
{
final IntList a = IntList.of(0, 1, 2, 3, 4);
final IntList b = IntList.of(2, 4, 5);
a.shuffle();
b.shuffle();
final IntList actual = IntList.intersection(a, b);
actual.sort();
Assert.assertEquals(IntList.of(2, 4), actual);
}
/*
* duplicate elements are removed in the result
*/
{
final IntList a = IntList.of(3, 5, 3, 3, 1);
final IntList b = IntList.of(2, 3, 3);
final IntList actual = IntList.intersection(a, b);
Assert.assertEquals(IntList.of(3), actual);
}
{
final IntList a = IntList.of(1, 4);
final IntList b = IntList.of(4, 3, 4, 4, 2);
final IntList actual = IntList.intersection(a, b);
Assert.assertEquals(IntList.of(4), actual);
}
}
}