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 support sublists
// TODO add lastIndexOf // TODO add lastIndexOf
// TODO remove bounds checks that are handled by java, e.g. negative indices // TODO remove bounds checks that are handled by java, e.g. negative indices
// TODO intersection for unsorted lists
private static final long serialVersionUID = 2622570032686034909L; private static final long serialVersionUID = 2622570032686034909L;
@@ -361,12 +360,19 @@ public final class IntList implements Serializable, Cloneable {
* <p> * <p>
* This method does not release any memory. Call {@link #trim()} to free unused * This method does not release any memory. Call {@link #trim()} to free unused
* memory. * 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 * @param retain
* the elements to retain * the elements to retain
* @throws NullPointerException * @throws NullPointerException
* if the specified {@link IntList} is null * if the specified {@link IntList} is null
* @see #trim() * @see #trim()
* @see #intersection(IntList, IntList)
*/ */
public void retainAll(final IntList retain) { 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}. * Returns a list with all elements that are in {@code a} and {@code b}.
* <p> * <p>
* The cardinality of each element will be equal to the minimum of the * The cardinality of each element will be equal to one (like in a set). This
* cardinality of each element in the given lists. * 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 * @param a
* a sorted {@link IntList} * a sorted {@link IntList}
@@ -758,15 +773,42 @@ public final class IntList implements Serializable, Cloneable {
* {@code b} * {@code b}
* @throws NullPointerException * @throws NullPointerException
* if {@code a} or {@code b} is null * if {@code a} or {@code b} is null
* @see #retainAll(IntList)
* @see #trim()
*/ */
public static IntList intersection(final IntList a, final IntList b) { 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()) { 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 l = 0;
int r = 0; int r = 0;
while (l < a.size() && r < b.size()) { while (l < aSize && r < bSize) {
final int lv = a.get(l); final int lv = a.get(l);
final int rv = b.get(r); final int rv = b.get(r);
@@ -777,13 +819,52 @@ public final class IntList implements Serializable, Cloneable {
r++; r++;
} else { } else {
result.add(lv); result.add(lv);
do {
l++; l++;
} while (l < aSize && lv == a.get(l));
do {
r++; r++;
} while (r < bSize && rv == b.get(r));
} }
} }
} else { return result;
throw new UnsupportedOperationException("retainAll on unsorted lists is not yet supported"); }
}
/**
* 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; return result;
} }

View File

@@ -1094,21 +1094,56 @@ public class IntListTest {
Assert.assertEquals(IntList.of(2, 4), actual); 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 * duplicate elements are removed in the result
* cardinality in either list
*/ */
{ {
final IntList a = IntList.of(3, 3, 3); final IntList a = IntList.of(3, 3, 3);
final IntList b = IntList.of(3, 3); final IntList b = IntList.of(3, 3);
final IntList actual = IntList.intersection(a, b); 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 b = IntList.of(4, 4, 4);
final IntList actual = IntList.intersection(a, b); final IntList actual = IntList.intersection(a, b);
Assert.assertEquals(IntList.of(4), actual); 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);
}
}
} }