59

The following is taken from a job interview:

In a given array, which contains integers, each number repeats itself once except for one, which doesn't repeat. Write a function that finds the number that doesn't repeat.

I thought about using an HashSet, but it might complicate everything...

Any ideas of a simple solution?

icza
  • 389,944
  • 63
  • 907
  • 827
Adam
  • 1,944
  • 3
  • 17
  • 19
  • 1
    Are there any constraints you have to work within? – Duncan Jones Mar 29 '15 at 19:51
  • 4
    http://stackoverflow.com/q/35185/1402846, http://stackoverflow.com/q/1089987/1402846, http://stackoverflow.com/q/2644179/1402846 – Pang Mar 30 '15 at 04:47
  • 4
    If I'm the interviewer, @Thomas answer will not satisfy me. I want clean, readable code, not short code which nobody understands. This means you'd need to write comments about what it does and clearly explain the limitations, e.g. this approach does not work if an item occurs more than 2 times. Next, provide some unit tests. Otherwise you get best answers on codegolf.SE – Thomas Weller Mar 30 '15 at 06:27
  • 3
    If I were the interviewer, it would be exactly what I asked for. It's a mean interview question. The interviewer just wanted to see if you know the same trick he does. – Dennis_E Mar 30 '15 at 09:14
  • 1
    If @Thomas' answer is what they are looking for, it's a lousy interview question. Trivia is about the worst way to determine if someone is a good programmer. I'd be wary of that organization. – Necreaux Mar 30 '15 at 14:08
  • It's unclear from the question here whether or not *all* the numbers from one up to some maximum number are used. If they are not all used, XOR is the best answer. If they are all used, addition or subtraction (or possibly other operations) can also be used. – James Mar 30 '15 at 14:52
  • Many interviewers will tweak the question as the candidate answers, adding more and more restrictions (ie. less possible solutions). The required restrictions so XOR is the only solution: 1. there is only enough memory for two registers, 2. the numbers are read from a read-only-once tape, and 3. the numbers in the list don't necessarily include all the numbers in a sequence up to some maximum. – James Mar 30 '15 at 14:54
  • Doesn't java 8 have some kind of linq equivalent? In that case you might be able to write the equivalent of `array.GroupBy(x=>x).Single(g=>g.Count()==1).Select(g=>g.Key)` – CodesInChaos Mar 30 '15 at 15:13
  • @Pang Voting to close all of those as duplicates of this one because, IMO, this one has the best accepted answer (even though I don't like the newer one that doesn't show all that much research effort being the master question all that much). – Bernhard Barker Mar 30 '15 at 15:15
  • @Dennis_E: the question may be ok for programming in embedded C and processors with very little RAM, e.g. the MSP430. But this question is tagged Java and we can assume a bit more memory and an OO approach. – Thomas Weller Mar 31 '15 at 08:30

5 Answers5

148

You can define an integer "result" initialized to 0, and then you do some bitwise operations by applying a XOR logic to all elements in your array.

At the end, "result" will be equal to the only element that appears only one time.

result = 0
for i in array:
  result ^= i
return result

http://en.wikipedia.org/wiki/Bitwise_operation#XOR

For instance, if your array contains the elements [3, 4, 5, 3, 4], the algorithm will return

3 ^ 4 ^ 5 ^ 3 ^ 4

But the XOR operator ^ is associative and commutative, so the result will be also equal to:

(3 ^ 3) ^ (4 ^ 4) ^ 5

Since i ^ i = 0 for any integer i, and i ^ 0 = i, you have

(3 ^ 3) ^ (4 ^ 4) ^ 5 = 0 ^ 0 ^ 5 = 5

dhokas
  • 1,771
  • 2
  • 13
  • 22
  • why ? can you give an example with one or more iteration ? – Adam Mar 29 '15 at 19:32
  • 2
    You have that the XOR logic operation is commutative. So for instance if you have a list [3, 4, 5, 3, 4], the algorithm will return you 3 ^ 4 ^ 5 ^ 3 ^ 4 which is equal to (3 ^ 3) ^ (4 ^ 4) ^ 5, and for every integer i, i ^ i = 0, but also i ^ 0 = i, so the result will be 0 ^ 0 ^ 5 = 5 – dhokas Mar 29 '15 at 19:35
  • @Thomas Add that explanation to your answer. Then I will upvote. – Paul Boddington Mar 29 '15 at 19:42
  • @Thomas You're a king !!! – Adam Mar 29 '15 at 19:52
  • 13
    Note that such code would need to be explained in comments if ever used in production! But this may be what the interviewers were looking for. – Duncan Jones Mar 29 '15 at 20:00
  • 1
    I totally missed the _once_ bit in _each number repeats itself once_ the first time I read the problem. Funny how that changes the set of possible solutions. – lealand Mar 29 '15 at 23:26
  • 1
    There are a lot of cool things that can be done with XOR. Another example is for permuting two integers a and b. Instead of creating a third variable, we can just do "a ^= b; b ^= a; b ^= a" – dhokas Mar 30 '15 at 03:14
  • 2
    @Thomas Shouldn't that last example for permuting be `a ^= b; b ^= a; a ^= b`? Neat trick, though. I have completely underestimated the power of XOR, it seems. – GregL Mar 30 '15 at 03:44
  • You are right, of course the last is a ^= b =) – dhokas Mar 30 '15 at 03:56
  • 4
    @GregL just don't use that XOR trick for swapping _please_. If `a == b`, you'll end up with `a == b == 0` (regardless of what `a` and `b` were. – Cole Tobin Mar 30 '15 at 13:49
  • 1) @ColeJohnson That description of the issue is misleading, since it doesn't matter if the values of `a` and `b` are, but if they reference/point to the same variable. 2) Using xor swap has been used to [backdoor RC4 encryption in The Underhanded C Contest](http://underhanded.xcott.com/?page_id=16). – CodesInChaos Mar 30 '15 at 15:06
  • 1
    You may want to add time and space complexity analysis for a more complete answer (this is often useful, interesting and/or relevant for algorithmic questions). – Bernhard Barker Mar 30 '15 at 15:16
  • 3
    @Dukeling I think it's pretty obvious this is `O(n)`. It loops through each element once – Cole Tobin Mar 31 '15 at 00:06
  • 1
    Does this technique work for negative numbers as well? – Hengameh May 03 '15 at 05:23
  • I tried for {-1, -1, -2}, but it failed and return 3!! It should have returned -2. – Hengameh May 03 '15 at 05:25
  • Good explanation. :) – Vikas Gupta Aug 08 '15 at 07:52
  • It won't work if the element appears thrice or five times... – sgupta Feb 05 '18 at 20:24
20

I've seen this question before. It's a trick. Assuming all the repeated numbers appear exactly twice you do this:

int result = 0;
for (int a : arr)
    result ^= a;
Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • @KSFT why ? can you give an example with one or more iteration ? – Adam Mar 29 '15 at 19:32
  • @Adam ...What? What do you want me to give an example of? What the useless code with a typo will do? Well, it won't do anything useful. – KSFT Mar 29 '15 at 19:33
  • you can check this post @KSFT http://stackoverflow.com/questions/1991380/what-does-the-operator-do-in-java – Yehia Awad Mar 29 '15 at 19:34
  • @яша That post doesn't help. I'm still not sure what Adam wants me to give an example of. – KSFT Mar 29 '15 at 19:35
  • @KSFT this post will help you understand ^ operator – Yehia Awad Mar 29 '15 at 19:37
  • @яша I know what `^` does and never asked. What made you think I didn't? – KSFT Mar 29 '15 at 19:42
  • `It's a trick` - you might get more up-votes if you explained "the trick". – WernerCD Mar 29 '15 at 23:43
  • 4
    @WernerCD I was in the middle of writing an explanation when Thomas put an explanation in the comments. I deleted my changes and advised Thomas to put his comment in his answer. Decision seems to have lost me about 20 upvotes... but there are more important things in life than that. – Paul Boddington Mar 29 '15 at 23:48
  • 2
    More important than unicorn votes? BLASPHEMY! – WernerCD Mar 29 '15 at 23:49
  • 1
    @pbabcdefp In which case I'd consider throwing away six more and deleting this answer, meaning the alternative options bubble closer to the top for inspection by the masses. – Duncan Jones Mar 30 '15 at 13:44
  • Does this technique work for negative numbers as well? I tried for {-1, -1, -2}, but it failed and return 3!! It should have returned -2. – Hengameh May 03 '15 at 05:27
  • 1
    @Hengameh It does work with negative numbers, yes. What was the exact code you tried? – Paul Boddington May 03 '15 at 10:27
14

Yet another "ordinary" solution (in Java):

public static int findSingle(int[] array) {

    Set<Integer> set = new HashSet<Integer>();

    for (int item : array) {
        if (!set.remove(item)) {
            set.add(item);
        }
    }       

    assert set.size() == 1;

    return set.iterator().next();
}

In my opinion the solution with XOR is kind of beautiful.

This one is not as fast as XOR but usage of HashSet makes it close to O(n). And it is certainly more readable.

user3078523
  • 1,520
  • 18
  • 27
5

The best answer is already given (XOR-ing the elements), this is to provide an alternative, more general way.

If the input array would be sorted (we can make it sorted), we could simply iterate over the elements in pairs (stepping by 2) and if the elements of the "pair" are different, we're done:

public static int findSingle(int[] arr) {
    Arrays.sort(arr);
    for (int i = 0, max = arr.length - 1; i < max; i += 2)
        if (arr[i] != arr[i + 1])
            return arr[i];
    return arr[arr.length - 1]; // Single element is the last
}

Note: This solution sorts the input array; if this is unwanted or not allowed, it can be cloned first:

arr = arr.clone();

If input array is sorted, the Arrays.sort(arr) call can be left out of course.

Generalization

The advantage of this solution is that it can be applied to all types which are comparable and therefore can be sorted (types which implement Comparable), for example String or Date. The XOR solution is limited to numbers only.

Here is a slightly modified version which takes an input array of any element type which is comparable:

public static <E extends Comparable<E>> E findSingle(E[] arr) {
    Arrays.sort(arr);
    for (int i = 0, max = arr.length - 1; i < max; i += 2)
        if (arr[i].compareTo(arr[i + 1]) != 0)
            return arr[i];
    return arr[arr.length - 1]; // Single element is the last
}

Note: In most cases you could also use arr[i].equals(arr[i + 1]) to compare elements instead of using Comparable.compareTo(). For details read the linked javadoc. Quoting the relevant part:

It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y)). Generally speaking, any class that implements the Comparable interface and violates this condition should clearly indicate this fact. The recommended language is "Note: this class has a natural ordering that is inconsistent with equals."

Now you can call this with a String[] for example:

System.out.println(findSingle(new String[] { "1", "2", "3", "1", "3" }));

Output:

2

Final notes:

Starting from the problem statement it is not checked whether there are more than 2 occurrences of the elements, and neither is whether the array length is odd. Also the second example doesn't check for null values, these are to be added if necessary.

icza
  • 389,944
  • 63
  • 907
  • 827
2

Here's a slightly less obfuscated way to do it:

List list = Arrays.asList(a);
int result;
for(int i:a)
{
    if(list.indexOf(i)==list.lastIndexOf(i))
    {
        result = i;
        break;
    }
}

result will contain the non-repeated value.

KSFT
  • 1,774
  • 11
  • 17
  • Can you Java your code ?... – Adam Mar 29 '15 at 19:35
  • 3
    @Adam I'm not sure what "Java" means as a verb. – KSFT Mar 29 '15 at 19:36
  • Sorry, Iv'e meant, if you can write it in Java...? – Adam Mar 29 '15 at 19:37
  • 4
    @Adam I did write it in Java. – KSFT Mar 29 '15 at 19:37
  • What for(int i:a) means ? – Adam Mar 29 '15 at 19:39
  • @Adam That's a [foreach](http://docs.oracle.com/javase/6/docs/technotes/guides/language/foreach.html) loop. – KSFT Mar 29 '15 at 19:43
  • I didn't knew there's a foreach ver. for Java... Thanks :) – Adam Mar 29 '15 at 19:46
  • I can return the favour. `list` hasn't been used. Also `i` will not contain the non-repeated value as it is local to the loop. – Paul Boddington Mar 29 '15 at 19:51
  • 2
    This is the code you are most likely to write once you have the job.. Although `asList` isn't valid method of an array – Duncan Jones Mar 29 '15 at 19:53
  • 6
    If the list is huge, I'm guessing that invoking list.lastIndexOf(i) every single iteration will lead to O(n^2) if it does what I think it does, which will tank your program performance-wise depending on how large the array is. – Water Mar 30 '15 at 00:51
  • This method also requires enough memory to store all the numbers. The XOR algorithm requires only the for loop counter and one accumulator. If you have 2 trillion numbers, that makes a big difference. The XOR algorithm will finish, this one will take *much* longer due to the memory usage. – James Mar 30 '15 at 02:07
  • 1
    @James the numbers are already in an array! This method required no more memory that the others. It's just **very** slow... – Boris the Spider Mar 30 '15 at 09:30
  • 1
    @Duncan not sure, this method is `O(n^2)`, which is rather slow for large input. In production and in the general case, I tend to go for the "sort then collapse" option - it's `O(n lg n)` and can short-circuit as soon as you find what you're looking for. – Boris the Spider Mar 30 '15 at 09:33
  • 1
    @BoristheSpider Yeah, sort then collapse was my first thought. Please take my comment as a more general "IRL you'd probably do it the 'simple' way first and only optimise to something dastardly cunning if performance is critical". – Duncan Jones Mar 30 '15 at 09:44
  • 1
    I also don't like `List list = Arrays.asList(a);`. Firstly, `List` should be a generic type. Secondly, this code only does what you expect if `a` is an `Integer[]` not an `int[]`. That should be made clear in the answer. – Duncan Jones Mar 30 '15 at 13:40
  • @BoristheSpider: Yes, according to this question, you're correct. I was given a slightly different (and explicitly more difficult) question--that the numbers were stored on a read-once tape and there was not enough RAM to hold all the numbers. – James Mar 30 '15 at 13:56