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:
@@ -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,33 +773,99 @@ 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()) {
|
||||||
int l = 0;
|
result = intersectionSorted(a, b);
|
||||||
int r = 0;
|
|
||||||
|
|
||||||
while (l < a.size() && r < b.size()) {
|
|
||||||
|
|
||||||
final int lv = a.get(l);
|
|
||||||
final int rv = b.get(r);
|
|
||||||
|
|
||||||
if (lv < rv) {
|
|
||||||
l++;
|
|
||||||
} else if (lv > rv) {
|
|
||||||
r++;
|
|
||||||
} else {
|
|
||||||
result.add(lv);
|
|
||||||
l++;
|
|
||||||
r++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException("retainAll on unsorted lists is not yet supported");
|
result = intersectionUnsorted(a, b);
|
||||||
}
|
}
|
||||||
return result;
|
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 < aSize && r < bSize) {
|
||||||
|
|
||||||
|
final int lv = a.get(l);
|
||||||
|
final int rv = b.get(r);
|
||||||
|
|
||||||
|
if (lv < rv) {
|
||||||
|
l++;
|
||||||
|
} else if (lv > rv) {
|
||||||
|
r++;
|
||||||
|
} else {
|
||||||
|
result.add(lv);
|
||||||
|
do {
|
||||||
|
l++;
|
||||||
|
} while (l < aSize && lv == a.get(l));
|
||||||
|
do {
|
||||||
|
r++;
|
||||||
|
} while (r < bSize && rv == b.get(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user