34

I just had a codility problem give me a hard time and I'm still trying to figure out how the space and time complexity constraints could have been met.

The problem is as follows: A dominant member in the array is one that occupies over half the positions in the array, for example:

{3, 67, 23, 67, 67}

67 is a dominant member because it appears in the array in 3/5 (>50%) positions.

Now, you are expected to provide a method that takes in an array and returns an index of the dominant member if one exists and -1 if there is none.

Easy, right? Well, I could have solved the problem handily if it were not for the following constraints:

  • Expected time complexity is O(n)
  • Expected space complexity is O(1)

I can see how you could solve this for O(n) time with O(n) space complexities as well as O(n^2) time with O(1) space complexities, but not one that meets both O(n) time and O(1) space.

I would really appreciate seeing a solution to this problem. Don't worry, the deadline has passed a few hours ago (I only had 30 minutes), so I'm not trying to cheat. Thanks.

Martijn Pieters
  • 889,049
  • 245
  • 3,507
  • 2,997
Matthias
  • 953
  • 2
  • 9
  • 15
  • what language were you using? – Kyle Sep 14 '12 at 02:38
  • 14
    I am really curious about what questions like really tests ? I've been working for 5 years as a Java Developer - mostly developing enterprise systems - not once I actually needed to solve problems like this one ! – Adelin Mar 20 '16 at 14:57
  • 1
    To pass such interview questions I think you have to be some kind of programming competitor and spend like more than 30% of your time solving such problems, what do you think ? – Adelin Mar 20 '16 at 15:01
  • 3
    You're right, solving these kind of problems is certainly a practiced skill. I'm sure that with time, the industry will wake up to the fact that these tests are not really that useful for determining programming aptitude. – Matthias Mar 22 '16 at 11:39
  • Codility tests you on your algorithmic thinking with heavy time constraints. To bypass the system, think out-of-the box. Take the test offline: make a screenshot of the questions, code in your own IDE and then send your solution to the company directly instead of submitting it to the Codility system. There is no advantage in submitting your solution in the Codility system, because you get zero feedback anyway, you cannot ask anyone for information when the question is not clear, and they record your every action which slows down your typing and compilation speed. – CCC Dec 22 '18 at 16:03

24 Answers24

47

Googled "computing dominant member of array", it was the first result. See the algorithm described on page 3.

element x;
int count ← 0;
For(i = 0 to n − 1) {
  if(count == 0) { x ← A[i]; count++; }
  else if (A[i] == x) count++;
  else count−−;
}
Check if x is dominant element by scanning array A

Basically observe that if you find two different elements in the array, you can remove them both without changing the dominant element on the remainder. This code just keeps tossing out pairs of different elements, keeping track of the number of times it has seen the single remaining unpaired element.

Keith Randall
  • 22,422
  • 1
  • 32
  • 53
  • 1
    how would you figure this out without knowing the solution first? is it some sort of algorithm that's used often? – Rob Sep 03 '13 at 23:37
  • 3
    @Rob: I didn't figure it out, I just searched for it. Someone smart figured it out for me. Kind of like most of human progress... – Keith Randall Sep 22 '13 at 16:53
  • but this code just checks if x is that dominant number. it does not actually find it. also i've tried the algorithm on your link and it does not work in this case because as dominant elements are dispersed, the count-- keeps going back to 0 and the other elements get selected. – Sonic Soul May 04 '15 at 12:22
  • 1
    @SonicSoul: no, this algorithm finds x. If there is a dominant element, x is guaranteed to be set to it at the end of the loop. Since x occurs more than n/2 times, you increment the count more than decrementing it, so it can't end up at 0. – Keith Randall May 04 '15 at 14:07
  • hmm, perhaps i am doing something wrong, but here is a case that didn't work for me (3, 1, 2, 3, 3, 4, 4, 1, 3, 3) as the loop iterates through it, it only gets up 2 count for a 3 because the intermittent elements keep bringing the count down and changing x to another variable. unless it's supposed to go through the loop twice. once to find x and 2nd time to count x ? – Sonic Soul May 04 '15 at 15:07
  • 1
    @SonicSoul: Yes, your final sentence is correct. The final `count` is not the # of appearances of `x`. Ignore the final count. You need to iterate through the array again and count occurrences of `x`. p.s. 3 is not a dominant element in your example. It needs to appear >n/2 times, not ==n/2 times. – Keith Randall May 04 '15 at 16:29
  • ahhha! well now it makes sense :) thanks very much for replying 2 years later – Sonic Soul May 04 '15 at 16:56
20

Find the median with BFPRT, aka median of medians (O(N) time, O(1) space). Then scan through the array -- if one number dominates, the median will be equal to that number. Walk through the array and count the number of instances of that number. If it's over half the array, it's the dominator. Otherwise, there is no dominator.

Community
  • 1
  • 1
Jerry Coffin
  • 437,173
  • 71
  • 570
  • 1,035
  • 4
    Thanks, I understand it now. Still, I can't imagine that many programmers would manage this without knowing that algorithm. This problem was rated 'easy' by codility. – Matthias Sep 14 '12 at 03:46
  • 2
    @Matthias: It depends on what you use. In Java is probably is pretty hard (and even if you know the right algorithm, coding it up in half an hour probably non-trivial). In C++, it's almost ridiculously trivial because the standard library already has the right algorithms (`std::nth_element` and `std::count`) so most of the job is done in two lines of code (caveat: `nth_element` *could* also use QuickSelect, which is linear on average, but quadratic worst case). – Jerry Coffin Sep 14 '12 at 03:52
  • although this is an interesting answer to the question, i believe the other more popular answer gets the vote for me. The main reason being that it is more simpler to understand. – JarJarrr Sep 09 '15 at 21:49
4

Adding a Java 100/100 O(N) time with O(1) space:

https://codility.com/demo/results/demoPNG8BT-KEH/

class Solution {
  public int solution(int[] A) {
        int indexOfCandidate = -1;
        int stackCounter = 0, candidate=-1, value=-1, i =0;

        for(int element: A ) {
            if (stackCounter == 0) {
                value = element;
                ++stackCounter;
                indexOfCandidate = i;
            } else {
                if (value == element) {
                    ++stackCounter;
                } else {
                    --stackCounter;
                }
            }
            ++i;
        }

        if (stackCounter > 0 ) {
            candidate = value;
        } else {
            return -1;
        }

        int countRepetitions = 0;
        for (int element: A) {
            if( element == candidate) {
                ++countRepetitions;

            }
            if(countRepetitions > (A.length / 2)) {
                return indexOfCandidate;
            }
        }
        return -1;
    }
}

If you want to see the Java source code it's here, I added some test cases as comments as the beginning of the file.

moxi
  • 1,275
  • 18
  • 20
3

Java solution with score 100%

public  int solution(int[] array) {

    int candidate=0;
    int counter = 0;

    // Find candidate for leader
    for(int i=0; i<array.length; i++){

        if(counter == 0) candidate = i;

        if(array[i] == array[candidate]){
            counter++;
        }else {
            counter--;
        }
    }

    // Count candidate occurrences in array
    counter = 0;
    for(int i=0; i<array.length; i++){
        if(array[i] == array[candidate]) counter++;
    }

    // Check that candidate occurs more than array.lenght/2
    return counter>array.length/2 ? candidate : -1;
}
nickc
  • 31
  • 2
1

Here's my C solution which scores 100%

int solution(int A[], int N) {

    int candidate;
    int count = 0;
    int i;

    // 1. Find most likely candidate for the leader
    for(i = 0; i < N; i++){

        // change candidate when count reaches 0
        if(count == 0) candidate = i;

        // count occurrences of candidate
        if(A[i] == A[candidate]) count++;
        else count--;          
    }

    // 2. Verify that candidate occurs more than N/2 times
    count = 0;
    for(i = 0; i < N; i++) if(A[i] == A[candidate]) count++;

    if (count <= N/2) return -1;
    return candidate; // return index of leader
}
df611
  • 143
  • 11
1

100%

import java.util.HashMap;
import java.util.Map;

class Solution {
    public static int solution(int[] A) {
        final int N = A.length;
        Map<Integer, Integer> mapOfOccur = new HashMap((N/2)+1);

        for(int i=0; i<N; i++){
            Integer count = mapOfOccur.get(A[i]);
            if(count == null){
                count = 1;
                mapOfOccur.put(A[i],count);
            }else{
                mapOfOccur.replace(A[i], count, ++count);
            }
            if(count > N/2)
                return i;

        }
        return -1;
    }
}
KamilJ
  • 239
  • 2
  • 5
  • 13
0

Does it have to be a particularly good algorithm? ;-)

static int dominant(final int... set) {
  final int[] freqs = new int[Integer.MAX_VALUE];
  for (int n : set) {
    ++freqs[n];
  }
  int dom_freq = Integer.MIN_VALUE;
  int dom_idx = -1;
  int dom_n = -1;
  for (int i = set.length - 1; i >= 0; --i) {
    final int n = set[i];
    if (dom_n != n) {
      final int freq = freqs[n];
      if (freq > dom_freq) {
        dom_freq = freq;
        dom_n = n;
        dom_idx = i;
      } else if (freq == dom_freq) {
        dom_idx = -1;
      }
    }
  }
  return dom_idx;
}

(this was primarily meant to poke fun at the requirements)

obataku
  • 27,809
  • 3
  • 38
  • 50
0

In python, we are lucky some smart people have bothered to implement efficient helpers using C and shipped it in the standard library. The collections.Counter is useful here.

>>> data = [3, 67, 23, 67, 67]
>>> from collections import Counter
>>> counter = Counter(data)  # counter accepts any sequence/iterable
>>> counter  # dict like object, where values are the occurrence 
Counter({67: 3, 3: 1, 23: 1})
>>> common = counter.most_common()[0]
>>> common
(67, 3)
>>> common[0] if common[1] > len(data) / 2.0 + 1 else -1
67
>>>

If you prefer a function here is one ...

>>> def dominator(seq):
        counter = Counter(seq)
        common = counter.most_common()[0]
        return common[0] if common[1] > len(seq) / 2.0 + 1 else -1
...
>>> dominator([1, 3, 6, 7, 6, 8, 6])
-1
>>> dominator([1, 3, 6, 7, 6, 8, 6, 6])
6
Meitham
  • 7,479
  • 4
  • 30
  • 42
  • Counter uses O(n) space irrespective of whether it's implemented by the standard library of the language or not. – Chandranshu Oct 19 '13 at 07:58
0

This question looks hard if a small trick does not come to the mind :). I found this trick in this document of codility : https://codility.com/media/train/6-Leader.pdf.

The linear solution is explained at the bottom of this document.

I implemented the following java program which gave me a score of 100 on the same lines.

public int solution(int[] A) {

    Stack<Integer> stack = new Stack<Integer>();

    for (int i =0; i < A.length; i++)
    {
        if (stack.empty())
            stack.push(new Integer(A[i]));
        else
        {
            int topElem = stack.peek().intValue();
            if (topElem == A[i])
            {
                stack.push(new Integer(A[i]));
            }
            else
            {
                stack.pop();
            }
        }            
    }

    if (stack.empty())
        return -1;

    int elem = stack.peek().intValue();
    int count = 0;
    int index = 0;
    for (int i = 0; i < A.length; i++)
    {
        if (elem == A[i])
        {
            count++;
            index = i;
        }
    }

    if (count > ((double)A.length/2.0))
        return index;
    else
        return -1;
}
NoOne
  • 303
  • 1
  • 4
  • 11
0

Consider this 100/100 solution in Ruby:

# Algorithm, as described in https://codility.com/media/train/6-Leader.pdf:
#
# * Iterate once to find a candidate for dominator.
# * Count number of candidate occurences for the final conclusion.
def solution(ar)
  n_occu = 0
  candidate = index = nil

  ar.each_with_index do |elem, i|
    if n_occu < 1
      # Here comes a new dominator candidate.
      candidate = elem
      index = i
      n_occu += 1
    else
      if candidate == elem
        n_occu += 1
      else
        n_occu -= 1
      end
    end # if n_occu < 1
  end

  # Method result. -1 if no dominator.
  # Count number of occurences to check if candidate is really a dominator.
  if n_occu > 0 and ar.count {|_| _ == candidate} > ar.size/2
    index
  else
    -1
  end
end

#--------------------------------------- Tests

def test
  sets = []
  sets << ["4666688", [1, 2, 3, 4], [4, 6, 6, 6, 6, 8, 8]]
  sets << ["333311", [0, 1, 2, 3], [3, 3, 3, 3, 1, 1]]
  sets << ["313131", [-1], [3, 1, 3, 1, 3, 1]]
  sets << ["113333", [2, 3, 4, 5], [1, 1, 3, 3, 3, 3]]

  sets.each do |name, one_of_expected, ar|
    out = solution(ar)
    raise "FAILURE at test #{name.inspect}: #{out.inspect} not in #{expected.inspect}" if not one_of_expected.include? out
  end

  puts "SUCCESS: All tests passed"
end
Alex Fortuna
  • 1,163
  • 11
  • 15
0

Here is an easy to read, 100% score version in Objective-c

  if (A.count > 100000)
    return -1;
NSInteger occur = 0;
NSNumber *candidate = nil;
for (NSNumber *element in A){
    if (!candidate){
        candidate = element;
        occur = 1;
        continue;
    }

    if ([candidate isEqualToNumber:element]){
        occur++;
    }else{
        if (occur == 1){
            candidate = element;
            continue;
        }else{
            occur--;
        }
    }
}
if (candidate){
    occur = 0;
    for (NSNumber *element in A){
        if ([candidate isEqualToNumber:element])
            occur++;
    }
    if (occur > A.count / 2)
        return [A indexOfObject:candidate];
}
return -1;
Javier Quevedo
  • 2,046
  • 1
  • 15
  • 25
0

100% score JavaScript solution. Technically it's O(nlogn) but still passed.

function solution(A) {
  if (A.length == 0)
    return -1;

  var S = A.slice(0).sort(function(a, b) {
    return a - b;
  });

  var domThresh = A.length/2;
  var c = S[Math.floor(domThresh)];
  var domCount = 0;

  for (var i = 0; i < A.length; i++) {
    if (A[i] == c)
      domCount++;

    if (domCount > domThresh)
      return i;
  }

  return -1;
}
Jung Oh
  • 29
  • 1
0

This is the solution in VB.NET with 100% performance.

Dim result As Integer = 0
        Dim i, ladderVal, LadderCount, size, valCount As Integer
        ladderVal = 0
        LadderCount = 0
        size = A.Length
        If size > 0 Then


            For i = 1 To size - 1
                If LadderCount = 0 Then
                    LadderCount += 1
                    ladderVal = A(i)
                Else
                    If A(i) = ladderVal Then
                        LadderCount += 1
                    Else
                        LadderCount -= 1
                    End If
                End If
            Next
            valCount = 0
            For i = 0 To size - 1
                If A(i) = ladderVal Then
                    valCount += 1
                End If
            Next
            If valCount <= size / 2 Then
                result = 0
            Else
                LadderCount = 0
                For i = 0 To size - 1
                    If A(i) = ladderVal Then
                        valCount -= 1
                        LadderCount += 1
                    End If
                    If LadderCount > (LadderCount + 1) / 2 And (valCount > (size - (i + 1)) / 2) Then
                        result += 1
                    End If
                Next
            End If
        End If
        Return result

See the correctness and performance of the code

V Malhi
  • 29
  • 6
0

Below solution resolves in complexity O(N).

public int solution(int A[]){
    int dominatorValue=-1;
    if(A != null && A.length>0){
        Hashtable<Integer, Integer> count=new Hashtable<>();
        dominatorValue=A[0];
        int big=0;
        for (int i = 0; i < A.length; i++) {
            int value=0;
            try{
                value=count.get(A[i]);
                value++;
            }catch(Exception e){
            }
            count.put(A[i], value);
            if(value>big){
                big=value;
                dominatorValue=A[i];
            }
        }
    }
    return dominatorValue;
}
Anandh
  • 11
  • 3
0

100% in PHP https://codility.com/demo/results/trainingVRQGQ9-NJP/

function solution($A){

    if (empty($A)) return -1;

    $copy = array_count_values($A);  // 3 => 7, value => number of repetition

    $max_repetition = max($copy); // at least 1 because the array is not empty

    $dominator = array_search($max_repetition, $copy);

    if ($max_repetition > count($A) / 2) return array_search($dominator, $A); else return -1;

}
0

i test my code its work fine in arrays lengths between 2 to 9

public static int sol (int []a)
{
    int count = 0 ;
    int candidateIndex = -1;
    for (int i = 0; i <a.length ; i++)
    {
        int nextIndex = 0;
        int nextOfNextIndex = 0;

        if(i<a.length-2)
        {
            nextIndex = i+1;
            nextOfNextIndex = i+2;
        }
        if(count==0)
        {
            candidateIndex = i;
        }
        if(a[candidateIndex]== a[nextIndex])
        {
            count++;

        }
        if (a[candidateIndex]==a[nextOfNextIndex])
        {
            count++;

        }


    }
    count -- ;
    return count>a.length/2?candidateIndex:-1;
}
0

Adding a Java 100/100 O(N) time with O(1) space:

// you can also use imports, for example:
import java.util.Stack;

// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");

class Solution {
    public int solution(int[] A) {
        // write your code in Java SE 8
        int count = 0;
        Stack<Integer> integerStack = new Stack<Integer>();
        for (int i = 0; i < A.length; i++) {
            if (integerStack.isEmpty()) {
                integerStack.push(A[i]);
            } else if (integerStack.size() > 0) {
                if (integerStack.peek() == A[i])
                    integerStack.push(A[i]);
                else
                    integerStack.pop();
            }
        }
        if (!integerStack.isEmpty()) {
            for (int i = 0; i < integerStack.size(); i++) {
                for (int j = 0; j < A.length; j++) {
                    if (integerStack.get(i) == A[j])
                        count++;
                    if (count > A.length / 2)
                        return j;
                }
                count = 0;
            }
        }
        return -1;
    }
}

Here is test result from codility.

DwlRathod
  • 700
  • 6
  • 17
-1

I think this question has already been resolved somewhere. The "official" solution should be :

  public int dominator(int[] A) {
    int N = A.length;

    for(int i = 0; i< N/2+1; i++)
    {
        int count=1;
        for(int j = i+1; j < N; j++)
        {
            if (A[i]==A[j]) {count++; if (count > (N/2)) return i;}
        }
    }

    return -1;
  }
m c
  • 1,064
  • 1
  • 12
  • 20
-1

How about sorting the array first? You then compare middle and first and last elements of the sorted array to find the dominant element.

public Integer findDominator(int[] arr) {
    int[] arrCopy = arr.clone();

    Arrays.sort(arrCopy);

    int length = arrCopy.length;
    int middleIndx = (length - 1) /2;

    int middleIdxRight;
    int middleIdxLeft = middleIndx;

    if (length % 2 == 0) {
        middleIdxRight = middleIndx+1;
    } else {
        middleIdxRight = middleIndx;
    }

    if (arrCopy[0] == arrCopy[middleIdxRight]) {
        return arrCopy[0];
    }

    if (arrCopy[middleIdxLeft] == arrCopy[length -1]) {
        return arrCopy[middleIdxLeft];
    }

    return null;
}
Jacek Obarymski
  • 360
  • 2
  • 9
  • Arrays.sort(arrCopy); is `O(n)` in the best case, and `O(n * log n)' in general case. Moreover returned indexes doesn't match with the original indexes. – Anton Boritskiy Oct 06 '12 at 22:18
-1

C#

int dominant = 0;
        int repeat = 0;
        int? repeatedNr = null;
        int maxLenght = A.Length;
        int halfLenght = A.Length / 2;
        int[] repeations = new int[A.Length];

        for (int i = 0; i < A.Length; i++)
        {
            repeatedNr = A[i];
            for (int j = 0; j < A.Length; j++)
            {
                if (repeatedNr == A[j])
                {
                    repeations[i]++;
                }
            }
        }
        repeatedNr = null;
        for (int i = 0; i < repeations.Length; i++)
        {
            if (repeations[i] > repeat)
            {
                repeat = repeations[i];
                repeatedNr = A[i];
            }
        }
        if (repeat > halfLenght)
            dominant = int.Parse(repeatedNr.ToString());
torch
  • 15
  • 1
-1
class Program
{
    static void Main(string[] args)
    {
        int []A= new int[] {3,6,2,6};
        int[] B = new int[A.Length];
        Program obj = new Program();
        obj.ABC(A,B);

    }

    public int ABC(int []A, int []B)
    { 
        int i,j;

        int n= A.Length;
        for (j=0; j<n ;j++)
        {
            int count = 1;
            for (i = 0; i < n; i++)
            {
                if ((A[j]== A[i] && i!=j))
                {
                    count++;

                }

             }
           int finalCount = count;
            B[j] = finalCount;// to store the no of times a number is repeated 

        }
       // int finalCount = count / 2;
        int finalCount1 = B.Max();// see which number occurred max times
        if (finalCount1 > (n / 2))
        { Console.WriteLine(finalCount1); Console.ReadLine(); }

        else
        { Console.WriteLine("no number found"); Console.ReadLine(); }
        return -1;
    }
}
annya
  • 7
  • 1
-1

In Ruby you can do something like

def dominant(a)
  hash = {}
  0.upto(a.length) do |index|
    element = a[index]
    hash[element] = (hash[element] ? hash[element] + 1 : 1)
  end

  res = hash.find{|k,v| v > a.length / 2}.first rescue nil
  res ||= -1
  return res
end
CaDs
  • 96
  • 1
  • 4
  • What about this requirement: "expected worst-case space complexity is O(1)"? Proposed solution's space complexity is at least O(N), not to mention it's an associative hash. – Alex Fortuna Jan 24 '14 at 21:46
-1

@Keith Randall solution is not working for {1,1,2,2,3,2,2}

his solution was:

element x;
int count ← 0;
For(i = 0 to n − 1) {
  if(count == 0) { x ← A[i]; count++; }
  else if (A[i] == x) count++;
  else count−−;
}
Check if x is dominant element by scanning array A

I converted it into java as below:

int x = 0;
int count = 0;

for(int i = 0; i < (arr.length - 1); i++) {

    if(count == 0) {
        x = arr[i];
        count++;
    }
    else if (arr[i] == x)
        count++;

    else count--;
}

return x;

Out put : 3 Expected: 2

  • `arr.length -1` should be `arr.length`. The -1 should not be there. Either that, or change the `i < (arr.length -1)` to `i <= (arr.length -1)`. – xavier Apr 29 '21 at 21:24
-3

This is my answer in Java: I store a count in seperate array which counts duplicates of each of the entries of the input array and then keeps a pointer to the array position that has the most duplicates. This is the dominator.

private static void dom(int[] a) {
        int position = 0;
        int max = 0;
        int score = 0;
        int counter = 0;
        int[]result = new int[a.length];

        for(int i = 0; i < a.length; i++){
            score = 0;
            for(int c = 0; c < a.length;c++){

                if(a[i] == a[c] && c != i ){
                    score = score + 1;
                    result[i] = score; 
                    if(result[i] > position){
                        position = i;
                    }
            }

            }
        }


                 //This is just to facilitate the print function and MAX = the number of times that dominator number was found in the list.

        for(int x = 0 ; x < result.length-1; x++){
            if(result[x] > max){
                max = result[x] + 1;
            }

        }




  System.out.println(" The following number is the dominator " + a[position] +  " it appears a total of " + max);





}
drlobo
  • 2,029
  • 5
  • 27
  • 42
  • Here is something with little description, https://github.com/dinkar1708/coding_interview/blob/master/codility/leader_dominator.py – DPM Dec 13 '19 at 10:39