2

I'm trying to solve the problem below from CodeFights. I left my answer in Java after the question. The code works for all the problems, except the last one. Time limit exception is reported. What could I do to make it run below 3000ms (CodeFights requirement)?

Note: Write a solution with O(n) time complexity and O(1) additional space complexity, since this is what you would be asked to do during a real interview.

Given an array a that contains only numbers in the range from 1 to a.length, find the first duplicate number for which the second occurrence has the minimal index. In other words, if there are more than 1 duplicated numbers, return the number for which the second occurrence has a smaller index than the second occurrence of the other number does. If there are no such elements, return -1.

Example

For a = [2, 3, 3, 1, 5, 2], the output should be firstDuplicate(a) = 3.

There are 2 duplicates: numbers 2 and 3. The second occurrence of 3 has a smaller index than than second occurrence of 2 does, so the answer is 3.

For a = [2, 4, 3, 5, 1], the output should be firstDuplicate(a) = -1.

Input/Output

[time limit] 3000ms (java) [input] array.integer a

Guaranteed constraints: 1 ≤ a.length ≤ 105, 1 ≤ a[i] ≤ a.length.

[output] integer

The element in a that occurs in the array more than once and has the minimal index for its second occurrence. If there are no such elements, return -1.

    int storedLeastValue = -1;
    int indexDistances = Integer.MAX_VALUE;
    int indexPosition = Integer.MAX_VALUE;

    for (int i = 0; i < a.length; i++) 
    {   
            int tempValue = a[i];

            for (int j = i+1; j < a.length; j++) {
                if(tempValue == a[j])
                {
                    if(Math.abs(i-j) < indexDistances && 
                      j < indexPosition)
                    {
                        storedLeastValue = tempValue;
                        indexDistances = Math.abs(i-j);
                        indexPosition = j;
                        break;
                    }
                }
            }
        }

        return storedLeastValue;
Cœur
  • 37,241
  • 25
  • 195
  • 267
Jas
  • 385
  • 7
  • 22

8 Answers8

3

Your solution has two nested for loops which implies O(n^2) while the question explicitly asks for O(n). Since you also have a space restriction you can't use an additional Set (which can provide a simple solution as well).

This question is good for people that have strong algorithms/graph theory background. The solution is sophisticated and includes finding an entry point for a cycle in a directed graph. If you're not familiar with these terms I'd recommend that you'll leave it and move to other questions.

Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
1

Check this one, it's also O(n) , but without additional array.

    int firstDuplicate(int[] a) {
        if (a.length <= 1) return -1;
        for (int i = 0; i < a.length; i++) {
            int pos = Math.abs(a[i]) - 1;
            if (a[pos] < 0) return pos + 1;
            else a[pos] = -a[pos];
        }
        return -1;
    }
Aleksandrs
  • 1,488
  • 1
  • 14
  • 34
1

The accepted answer does not work with the task. It would work if the input array would indeed contain no bigger value than its length. But it does, eg.: [5,5].

So, we have to define which number is the biggest.

int firstDuplicate(int[] a) {

    int size = 0;
    for(int i = 0; i < a.length; i++) {
        if(a[i] > size) {
            size = a[i];
        }
    }

    int[] t = new int[size+1];

    for(int i = 0; i < a.length; i++) {
        if(t[a[i]] == 0) {
            t[a[i]]++;
        } else {
            return a[i];
        }
    }
    return -1;
}
xerx593
  • 12,237
  • 5
  • 33
  • 64
Dávid Katona
  • 47
  • 1
  • 10
0

What about this:

       public static void main(String args[]) {

            int [] a = new int[] {2, 3, 3, 1, 5, 2};
            // Each element of cntarray will hold the number of occurrences of each potential number in the input (cntarray[n] = occurrences of n)
            // Default initialization to zero's 
            int [] cntarray = new int[a.length + 1]; // need +1 in order to prevent index out of bounds errors, cntarray[0] is just an empty element

            int min = -1;
            for (int i=0;i < a.length ;i++) {
                if (cntarray[a[i]] == 0) {
                    cntarray[a[i]]++;
                } else {
                    min = a[i];
                    // no need to go further
                    break;
                }
            }
            System.out.println(min);
        }
localghost
  • 419
  • 2
  • 6
  • Hi @localghost, I liked your solution, but I'd like more details to understand the code 100%. For example, how cntarray[a[i]] == 0 can work? The code does not initialize cntarray. How can its values be equal to zero (or anything else)? Thanks a lot! – Jas Nov 20 '17 at 13:52
  • Default value of `int` is zero. Once you declare an array of `int`, all elements are initialized to zero. Have a look here: https://stackoverflow.com/questions/2154251/any-shortcut-to-initialize-all-array-elements-to-zero – localghost Nov 20 '17 at 13:59
  • 1
    @Jas: I might have missed the "O(1) additional space complexity" of your question. I'm not sure if declaring `cntarray = new int[106]` (1st invariant) fulfills this requirement. If a more appropriate answer comes up, feel free to de-accept this answer. @alfasin: Can you please comment on this? – localghost Nov 21 '17 at 08:42
  • Hi @localghost, you're right. I've found this website - https://forum.thecoders.org/t/an-interesting-coding-problem-in-codefights/163 - It has an O(1) solution. Take a look and see if you agree. – Jas Nov 22 '17 at 21:31
  • Yes, this solution is excellent. You can add a new answer and reference the source. – localghost Nov 23 '17 at 07:46
  • I added it now. Thx! – Jas Nov 25 '17 at 13:48
0

You can store array values in hashSet. Check if value is already present in hashSet if not present then add it in hashSet else that will be your answer. Below is code which passes all test cases:-

int firstDuplicate(int[] a) {
HashSet<Integer> hashSet = new HashSet<>();
        for(int i=0; i<a.length;i++){
            if (! hashSet.contains(a[i])) {
                hashSet.add(a[i]);
            } else {
                return a[i];
            }
        }
        return -1;
}
Vineet Jain
  • 1,515
  • 4
  • 21
  • 31
0

My simple solution with a HashMap

int solution(int[] a) {
HashMap<Integer, Integer> countMap = new HashMap<Integer, Integer>();
int min = -1;
for (int i=0; i < a.length; i++) {
    if (!(countMap.containsKey(a[i]))) {
        countMap.put(a[i],1);
    }
    else {
        return a[i];
    }
}  
return min;
}
st.huber
  • 1,481
  • 2
  • 24
  • 45
John
  • 1
  • 2
0

Solution is very simple:

  1. Create a hashset
  2. keep iterating over the array
  3. if element is already not in the set, add it.
  4. else element will be in the set, then it mean this is minimal index of first/second the duplicate
  int solution(int[] a) {
        HashSet<Integer> set = new HashSet<>();
        
        for(int i=0; i<a.length; i++){
            if(set.contains(a[i])){
                // as soon as minimal index duplicate found where first one was already in the set, return it
                return a[i];
            }
            
            set.add(a[i]);
        }
        
        return -1;
    }
Rohit Kumar
  • 983
  • 9
  • 11
-1

A good answer for this exercise can be found here - https://forum.thecoders.org/t/an-interesting-coding-problem-in-codefights/163 - Everything is done in-place, and it has O(1) solution.

Jas
  • 385
  • 7
  • 22