We can use different approaches:
1. Use two hashsets(as we don't need the real count): uniqueSet, duplicateSet
The first time we see the element, we put it into both sets, if we see it again, we remove it from uniqueSet.
2. We can use BitSet to reduce space - uniqueBitSet and duplicateBitSet
The first time we see the element, we put set its bit in uniqueBitSet and duplicateBitSet, if we see it again, we clear its bit in duplicateBitSet.
[CareerCup] 10.4 Find All Duplicates Elements
You have an array with all the numbers from 1 to N, where N is at most 32,000. The array may have duplicate entries and you do not know what N is. With only 4 kilobytes of memory available, how would you print all duplicate elements in the array?
It can also be solved by using BitSet. add i into bitset, if i happens again, print it out.
Bonus:
org.apache.commons.lang3.ArrayUtils.removeElements(int[], int...)
java.util.BitSet.cardinality()
1. Use two hashsets(as we don't need the real count): uniqueSet, duplicateSet
The first time we see the element, we put it into both sets, if we see it again, we remove it from uniqueSet.
2. We can use BitSet to reduce space - uniqueBitSet and duplicateBitSet
The first time we see the element, we put set its bit in uniqueBitSet and duplicateBitSet, if we see it again, we clear its bit in duplicateBitSet.
// Using Bitset public static int findUnqiueNumber1(final int[] array) { final BitSet unique = new BitSet(), duplicate = new BitSet(); for (final int number : array) { // first time see this number if (!duplicate.get(number)) { unique.set(number); duplicate.set(number); } else { // if see it again unique.clear(number); } } final int unqinueValue = unique.nextSetBit(0); if (unqinueValue == -1) { throw new NoUniqueException("No unique number"); } final int nextUnqiueValue = unique.nextSetBit(unqinueValue + 1); if (nextUnqiueValue != -1) { throw new MoreThanOneUniqueException(String.format( "More than 1 unique number found. We found at least %s, %s", unqinueValue, nextUnqiueValue)); } return unqinueValue; } // Using HashSet public static int findUnqiueNumber2(final int[] array) { final Set<Integer> uniqueSet = new HashSet<>(), duplicateSet = new HashSet<>(); for (final int number : array) { // first time see this number if (!duplicateSet.contains(number)) { uniqueSet.add(number); duplicateSet.add(number); } else { // if see it again uniqueSet.remove(number); } } if (uniqueSet.isEmpty()) { throw new NoUniqueException("No unique number"); } if (uniqueSet.size() > 1) { throw new MoreThanOneUniqueException( String.format("More than 1 unique number found. We found %s count", uniqueSet.size())); } final int unqinueValue = uniqueSet.iterator().next(); return unqinueValue; } private static class MoreThanOneUniqueException extends RuntimeException { private static final long serialVersionUID = 1L; public MoreThanOneUniqueException(final String messge) { super(messge); } } private static class NoUniqueException extends RuntimeException { private static final long serialVersionUID = 1L; public NoUniqueException(final String messge) { super(messge); } } @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void test() { Assert.assertEquals(findUnqiueNumber2(new int[] {1}), 1); Assert.assertEquals(findUnqiueNumber2(new int[] {1, 2, 2}), 1); Assert.assertEquals(findUnqiueNumber2(new int[] {1, 2, 2}), 1); } @Test public void testNoUnqiue() { thrown.expect(NoUniqueException.class); findUnqiueNumber2(new int[] {}); } @Test public void testNoUnqiue2() { thrown.expect(NoUniqueException.class); findUnqiueNumber2(new int[] {1, 1, 2, 2}); } @Test public void testMoreThanONeUnqiue() { thrown.expect(MoreThanOneUniqueException.class); findUnqiueNumber2(new int[] {1, 2}); }Related:
[CareerCup] 10.4 Find All Duplicates Elements
You have an array with all the numbers from 1 to N, where N is at most 32,000. The array may have duplicate entries and you do not know what N is. With only 4 kilobytes of memory available, how would you print all duplicate elements in the array?
It can also be solved by using BitSet. add i into bitset, if i happens again, print it out.
Bonus:
org.apache.commons.lang3.ArrayUtils.removeElements(int[], int...)
public static int[] removeElements(final int[] array, final int... values) { if (isEmpty(array) || isEmpty(values)) { return clone(array); } final HashMap<Integer, MutableInt> occurrences = new HashMap<Integer, MutableInt>(values.length); // use MutableInt to avoid Integer creation again and again. for (final int v : values) { final Integer boxed = Integer.valueOf(v); final MutableInt count = occurrences.get(boxed); if (count == null) { occurrences.put(boxed, new MutableInt(1)); } else { count.increment(); } } final BitSet toRemove = new BitSet(); for (final Map.Entry<Integer, MutableInt> e : occurrences.entrySet()) { final Integer v = e.getKey(); int found = 0; for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { found = indexOf(array, v.intValue(), found); if (found < 0) { break; } toRemove.set(found++); } } return (int[]) removeAll(array, toRemove); } static Object removeAll(final Object array, final BitSet indices) { final int srcLength = ArrayUtils.getLength(array); // No need to check maxIndex here, because method only currently called from removeElements() // which guarantee to generate on;y valid bit entries. // final int maxIndex = indices.length(); // if (maxIndex > srcLength) { // throw new IndexOutOfBoundsException("Index: " + (maxIndex-1) + ", Length: " + srcLength); // } final int removals = indices.cardinality(); // true bits are items to remove final Object result = Array.newInstance(array.getClass().getComponentType(), srcLength - removals); int srcIndex=0; int destIndex=0; int count; int set; while((set = indices.nextSetBit(srcIndex)) != -1){ count = set - srcIndex; if (count > 0) { System.arraycopy(array, srcIndex, result, destIndex, count); // System.arraycopy is native and faster. destIndex += count; } srcIndex = indices.nextClearBit(set); } count = srcLength - srcIndex; if (count > 0) { System.arraycopy(array, srcIndex, result, destIndex, count); } return result; }
java.util.BitSet.cardinality()
public int cardinality() { int sum = 0; for (int i = 0; i < wordsInUse; i++) sum += Long.bitCount(words[i]); return sum; }