-1

The problem is to create an array of player ranks based on 2 other arrays: leaderboard and player scores. More explanations of the problem here: https://www.hackerrank.com/challenges/climbing-the-leaderboard/problem.

The code below is a spaghetti but it's working fine. But, for large size of ranked array(200000 elements for example), it times out. I'm not asking for code to copy/paste. I just wanna know if there is a way to optimize this code.


int* climbingLeaderboard(int ranked_count, int* ranked, int player_count, int* player, int* result_count) {
    
    *result_count=player_count;
    // remove duplicates
    int removed=0;
    for(int i=0, j=1; i<ranked_count-removed; i++, j++){   
      if(ranked[i]==ranked[j]){
        for(int k=j; k<ranked_count-removed; k++) 
           ranked[k]=ranked[k+1]; 
        removed++;
      }
    }
    int newsize=ranked_count-removed;
    // create an array to store ranks then fill it
    int* positions=malloc(newsize*sizeof(int));
    positions[0]=1;
    for(int i=0, j=1; j<newsize; i++, j++){
        positions[j]=(ranked[j]<ranked[i])? (positions[i]+1) : positions[i];
    }
    // create and fill the results array using binary search
    int* res = malloc(player_count*sizeof(int));
    int start=0, end=newsize-1, middle=(start+end)/2;
    int j, k=newsize-1;
    for(int i=0; i<player_count; i++){
        if(i>0&&player[i]==player[i-1]){
            *(res+i)=(*(res+(i-1))); 
            continue;
        }
        if(player[i]>=ranked[middle]){
            *(res+i)=positions[middle]; 
            j=middle-1;
            while(j>=0){
                if(player[i]>=ranked[j]) 
                    *(res+i)=positions[j];
                else if(j==k) 
                    *(res+i)=positions[j]+1; 
                else break; 
                --j;
            }
            start=0; end=middle-1;
        }
        else{
            *(res+i)=positions[newsize-1]+1; 
            j=newsize-1;
            while(j>=middle){
                if(player[i]>=ranked[j]) 
                    *(res+i)=positions[j];
                else if(j==k) 
                    *(res+i)=positions[j]+1; 
                else break; 
                --j;
            }
            start=middle+1; end=newsize-1;
        }
        middle=(start+end)/2;
    }
    free(positions);
    return res;
}
redone_lsf
  • 11
  • 2
  • The "efficient algorithm" is just as much a *part* of these problems as the actual coding. Maybe more so, because you need to be able to code anyway. – Weather Vane Jan 31 '23 at 21:59
  • 7
    Get out of the habit of using `*(res+i)`. This is equivalent to `res[i]` and the latter is generally considered easier to read and understand. – Barmar Jan 31 '23 at 21:59
  • 4
    The enter key is your friend. It will be easier for you to follow the flow of your own code (and find problems with it) if you don’t put as much as possible on single lines. If you put braces on their own lines and a newline after every semicolon your code will be significantly more readable. – 44stonelions Jan 31 '23 at 22:00
  • 1
    I'm not going to create an account on HackerRank to read the description of the problem you're trying to sole and the constraints on the input. That belongs in the question here. – Retired Ninja Jan 31 '23 at 22:03
  • @RetiredNinja you don't need an account to read the problem. Just close the popup. – Weather Vane Jan 31 '23 at 22:05
  • 1
    @WeatherVane Perhaps that works, but all of the information needed to answer the question should be part of the question. That page could move or be removed in the future. – Retired Ninja Jan 31 '23 at 22:07
  • Can you reformat the code for readability? As posted, it is artificially difficult to optimize. – chqrlie Jan 31 '23 at 22:09
  • @44stonelions I see that writing the maximum of instructions in one line is better than scrolling. Anyways, I always look for code readability after solving the problem :) – redone_lsf Jan 31 '23 at 22:09
  • 2
    @redone_lsf But if no one can read your code, no one can help you. – Steve Summit Jan 31 '23 at 22:11
  • 3
    @redone_lsf: I would advise you to change this approach: readable code helps solve the problem. Cramming statements on the same line causes hard to find bugs. The space bar is your friend – chqrlie Jan 31 '23 at 22:12
  • 1
    @redone_lsf: more horizontal space needed. Get rid of the `*(res+i)` syntax and avoid multiple statements on the same line... – chqrlie Jan 31 '23 at 22:17
  • @chqrlie I think it is readable now – redone_lsf Jan 31 '23 at 22:18
  • @redone_lsf: I'm afraid I don't, sorry I cannot help you more. – chqrlie Jan 31 '23 at 22:19
  • 1
    *fill the results array using binary search*: there seems to be a lot of linear searches in this loop. – chqrlie Jan 31 '23 at 22:24
  • @chqrlie I was trying to solve the problem the fastest possible way – redone_lsf Jan 31 '23 at 22:29
  • modifying the argument arrays should not be allowed... – chqrlie Jan 31 '23 at 22:37
  • @WeatherVane when the final `else` is not executed. `--j;` will be executed – redone_lsf Jan 31 '23 at 22:37
  • @chqrlie I modified the array because it won't be used elsewhere in the program. – redone_lsf Jan 31 '23 at 22:42
  • It was the one-line code that caused confusion. It's more readable now. – Weather Vane Jan 31 '23 at 22:45
  • @redone_lsf: indeed given how `main` is written, it won't, but it is an unnecessary side effect of your function `climbingLeaderboard`. Such side effects are nasty in production code. – chqrlie Jan 31 '23 at 22:46
  • This is a [multimap](https://en.cppreference.com/w/cpp/container/multimap), which you can implement in C as a tree. – Neil Jan 31 '23 at 22:54
  • @redone_lsf *I always look for code readability after solving the problem* That won't work so well when you're trying to solve **real** problems that take days or weeks to work on instead of some toy. When you come back to code you wrote two weeks ago, **you** won't be able to read it. – Andrew Henle Jan 31 '23 at 23:20

2 Answers2

1

The initial loop to remove duplicates has a potential quadratic time complexity. You can achieve linear complexity using the 2 finger approach:

    int removed = 0;
    for (int i = 1, j = 1; j < ranked_count; j++) {
        if (ranked[i - 1] != ranked[j])
            ranked[i++] = ranked[j];
        else
            removed++;
    }

More generally, the argument arrays should not be changed in spite of the sloppy prototype given:

int *climbingLeaderboard(int ranked_count, int *ranked,
                         int player_count, int *player,
                         int *result_count);

Here are simple steps I would recommend to solve this problem:

  • allocate and initialize a ranking array with the ranking for each of the scores in the ranked array. Be careful to allocate ranked_count + 1 elements.
  • allocate a result array res of length player_count, set the result_count to player_count.
  • starting with pos = ranked_count, for each entry i in player:
    • locate the position pos where the entry would be inserted in the ranking array using binary search between position 0 and the current pos inclusive. Make sure you find the smallest entry in case of duplicate scores.
    • set res[i] to ranking[pos]
  • free the ranking array
  • return the res array.

Here is a simple implementation:

int *climbingLeaderboard(int ranked_count, int *ranked,
int player_count, int *player,
int *result_count)
{
if (player_count <= 0) {
*result_count = 0;
return NULL;
}
int *ranking = malloc(sizeof(*ranking) * (ranked_count + 1));
int rank = 1;
ranking[0] = rank;
for (int i = 1; i < ranked_count; i++) {
if (ranked[i] != ranked[i - 1])
rank++;
ranking[i] = rank;
}
ranking[ranked_count] = rank + 1;

int *res = malloc(sizeof(*res) * player_count);
*result_count = player_count;

int pos = ranked_count;
for (int i = 0; i < player_count; i++) {
int start = 0;
while (start < pos) {
int middle = start + (pos - start) / 2;
if (ranked[middle] > player[i])
start = middle + 1;
else
pos = middle;
}
res[i] = ranking[pos];
}
free(ranking);
return res;
}

chqrlie
  • 131,814
  • 10
  • 121
  • 189
0

Look for ways to use "branchless" to improve execution speed:

    positions[0]=1;
    for(int i=0, j=1; j<newsize; i++, j++){
        positions[j]=(ranked[j]<ranked[i])? (positions[i]+1) : positions[i];
    }

becomes

    positions[0] = 1;
    for( int i = 0, j = 1; j < newsize; i++, j++ )
        positions[j] = positions[i] + (ranked[j] < ranked[i]);

Other than this, I don't even want to try to sort out what this code is attempting.

Fe2O3
  • 6,077
  • 2
  • 4
  • 20