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;
}
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;
}
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.
I wrote a function creating a dynamic array of random values and another function creating a new array consisting of unique values of the previous array. The algorithm used counts unique values correctly. However, I faced a problem in printing all values. In the example below the program printed 7 2 12714320 4 5 instead of 7 2 4 5 6 .
This is the program which can be tested:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int *delduplicate(int *v, int size_old, int *size_new);
main()
{
int n;
int *norepeat;
float *results;
int dim, size_norepeat, i;
int a[7] = {7,2,2,4,5,6,7};
norepeat = delduplicate(a, 7, &size_norepeat);
for (int i = 0; i < size_norepeat; i++)
printf("%d ", norepeat[i]);
}
// delduplicate function
int *delduplicate(int *v, int size_old, int *size_new)
{
int i, j, k = 1, uniques = 1, repeats, *new_v, temp;
// count the number of unique elements
for (i = 1; i < size_old; i++)
{
int is_unique = 1;
for (j = 0; is_unique && j < i; j++)
{
if (v[i] == v[j])
is_unique = 0;
}
if (is_unique)
uniques++;
}
*size_new = uniques;
// create new array of unique elements
new_v = (int*) malloc(*size_new * sizeof(int));
// fill new array with unique elements
new_v[0] = v[0];
for (i = 1; i < size_old; i++)
{
int is_unique = 1;
for (j = 0; j < i; j++)
{
if (v[i] == v[j])
is_unique = 0;
}
if (is_unique)
new_v[k] = v[i];
k++;
}
return new_v;
}
The problem should be happening here:
// fill new array with unique elements
new_v[0] = v[0];
for (i = 1; i < size_old; i++)
{
int is_unique = 1;
for (j = 0; j < i; j++)
{
if (v[i] == v[j])
is_unique = 0;
}
if (is_unique)
new_v[k] = v[i];
k++;
}
Your problem is probably occurring in the following section -
if (is_unique)
new_v[k] = v[i];
k++;
Here you are incrementing k at each iteration. However, you only want to increment it whenever you have found a unique element. if() without brackets only considers the first statement. So change it to this -
if (is_unique){
new_v[k] = v[i];
k++;
}
This change should make your program run fine.
Side Note : If you do not want to use brackets for an if() , for() , etc, you can separate the statements by commas and use without having the brackets. Like this -
if (is_unique)
new_v[k] = v[i],
k++;
I'm working on this program for my University exam. I need to sort passenger array with a sorting algorithm (I choose bubblesort due to it's simplicity).
I need to create a generic function and pass as formal parameters:
-a list of objects i want to sort;
-a sort criterion.
So I think that I'll have to create only 1 Increasing sorting function and 1 decreasing sorting function and pass them the parameters to sort by.
I already tried to pass char *file_name to function, but I think that I'm wrong.
int passengersIncreasingBubbleSort_Birthyear(passengers test[], int x) {
int i = 0, j = 0, min_idx, flag = 0;
passengers temp;
for (i = 0; i < x; i++) {
min_idx = i;
for (j = i + 1; j < x; j++) {
if (test[j].birth_date.year < test[min_idx].birth_date.year) {
min_idx = j;
}
}
temp = test[min_idx];
test[min_idx] = test[i];
test[i] = temp;
flag = 1;
}
return flag;
}
I tried this:
int passengersIncreasingBubbleSort_Birthyear(passengers test[], int x, char *value1, char *value2) {
int i = 0, j = 0, min_idx, flag = 0;
passengers temp;
for (i = 0; i < x; i++) {
min_idx = i;
for (j = i + 1; j < x; j++) {
if (value1 < value2) {
min_idx = j;
}
}
temp = test[min_idx];
test[min_idx] = test[i];
test[i] = temp;
flag = 1;
}
return flag;
}
But it doesn't work as expected.
Ok, I achieved it.
int cmpfunction_Increasing_Birthdate (const void * a, const void * b)
{
passengers *passengerA = (passengers *)a;
passengers *passengerB = (passengers *)b;
return ( passengerA->signup_date.year - passengerB->signup_date.year );
}
and this is my call:
qsort(array, x, sizeof(passengers), cmpfunction_Increasing_Birthdate);
Now the question is, how can I also compare both year, month and day? Could I do it in the same compare function?
I was trying to use the function Initialize to update the value of array distance, here is my code:
#include <stdio.h>
#define NOT_CONNECTED -1
int nodesCount;
void Initialize(int (*distance)[nodesCount], int nodesCount) {
int i, j;
for(i = 0; i <= nodesCount; ++i) {
for(j = 0; j <= nodesCount; ++j) {
distance[i][j] = NOT_CONNECTED;
}
distance[i][i] = 0;
}
for(int i = 0; i <= nodesCount; i++)
printf("distance[%d] = %d\n", i, distance[i][i]);
}
int main() {
scanf("%d", &nodesCount);
int distance[nodesCount + 1][nodesCount + 1];
Initialize(distance, nodesCount);
for(int i = 0; i <= nodesCount; i++)
printf("distance[%d] = %d\n", i, distance[i][i]);
}
But the test result printed out shows the updated array value is wired, could anybody explain what might have happened to this code?
distance[0] = 0
distance[1] = 0
distance[2] = 0
distance[3] = 0
distance[4] = 0
distance[5] = 0
distance[0] = 0
distance[1] = -1
distance[2] = -1
distance[3] = -1
distance[4] = -1
distance[5] = 32677
In
void Initialize(int (*distance)[nodesCount], int nodesCount)
compiler has no idea what is nodesCount in int (*distance)[nodesCount] because it has not seen it yet declared. This should be
void Initialize(int nodesCount, int (*distance)[nodesCount])
Then, you should pass nodesCount+1 to Initialize function
Initialize(nodesCount+1, distance);
Note that in function Initializeyou have to change every occurrence of <=nodesCount in for loops to <nodesCount.
for (i=0; i<nodesCount; ++i){ /* ... */ }
I have sets S1 = {s11,s12,s13), S2 = {s21,s22,s23) and so on till SN.I need to generate all the permutations consisting elements of S1,S2..SN.. such that there is only 1 element from each of the sets.
For eg:
S1 = {a,b,c}
S2 = {d,e,f}
S3 = {g,h,i}
My permuations would be:
{a,d,g}, {a,d,h}, {a,d,i}, {a,e,g}, {a,e,h}....
How would I go about doing it? (I could randomly go about picking up 1 from each and merging them, but that is even in my knowledge a bad idea).
For the sake of generality assume that there are 'n' elements in each set. I am looking at implementing it in C. Please note that 'N' and 'n' is not fixed.
It's just a matter of recursion. Let's assume these definitions.
const int MAXE = 1000, MAXN = 1000;
int N; // number of sets.
int num[MAXN]; // number of elements of each set.
int set[MAXN][MAXE]; // elements of each set. i-th set has elements from
// set[i][0] until set[i][num[i]-1].
int result[MAXN]; // temporary array to hold each permutation.
The function is
void permute(int i)
{
if (i == N)
{
for (int j = 0; j < N; j++)
printf("%d%c", result[j], j==N-1 ? '\n' : ' ');
}
else
{
for (int j = 0; j < num[i]; j++)
{
result[i] = set[i][j];
permute(i+1);
}
}
}
To generate the permutations, simply call permute(0);
If you know exactly how many sets there are and it's a small number one might normally do this with nested loops. If the number of sets is greater than 2 or 3, or it is variable, then a recursive algorithm starts to make sense.
And if this is homework, it's likely that implementing a recursive algorithm is the object of the entire assignment. Think about it, for each set, you can call the enumeration function recursively and have it start enumerating the next set...
If they are in a container, just iterate through each:
#include <stdio.h>
int main(void)
{
int set1[] = {1, 2, 3};
int set2[] = {4, 5, 6};
int set3[] = {7, 8, 9};
for (unsigned i = 0; i < 3; ++i)
{
for (unsigned j = 0; j < 3; ++j)
{
for (unsigned k = 0; k < 3; ++k)
{
printf("(%d, %d, %d)", set1[i], set2[j], set3[k]);
}
}
}
return 0;
}
Generic solution:
typedef struct sett
{
int* nums;
int size;
} t_set;
inline void swap(t_set *set, int a, int b)
{
int tmp = set->nums[a];
set->nums[a] = set->nums[b];
set->nums[b] = tmp;
}
void permute_set(t_set *set, int from, void func(t_set *))
{
int i;
if (from == set->size - 1) {
func(set);
return;
}
for (i = from; i < set->size; i++) {
swap(set, from, i);
permute_set(set, from + 1, func);
swap(set, i, from);
}
}
t_set* create_set(int size)
{
t_set *set = (t_set*) calloc(1, sizeof(t_set));
int i;
set->size = size;
set->nums = (int*) calloc(set->size, sizeof(int));
for(i = 0; i < set->size; i++)
set->nums[i] = i + 1;
return set;
}
void print_set(t_set *set) {
int i;
if (set) {
for (i = 0; i < set->size; i++)
printf("%d ", set->nums[i]);
printf("\n");
}
}
int main(int argc, char **argv)
{
t_set *set = create_set(4);
permute_set(set, 0, print_set);
}
This is a fairly simple iterative implementation which you should be able to adapt as necessary:
#define SETSIZE 3
#define NSETS 4
void permute(void)
{
char setofsets[NSETS][SETSIZE] = {
{ 'a', 'b', 'c'},
{ 'd', 'e', 'f'},
{ 'g', 'h', 'i'},
{ 'j', 'k', 'l'}};
char result[NSETS + 1];
int i[NSETS]; /* loop indexes, one for each set */
int j;
/* intialise loop indexes */
for (j = 0; j < NSETS; j++)
i[j] = 0;
do {
/* Construct permutation as string */
for (j = 0; j < NSETS; j++)
result[j] = setofsets[j][i[j]];
result[NSETS] = '\0';
printf("%s\n", result);
/* Increment indexes, starting from last set */
j = NSETS;
do {
j--;
i[j] = (i[j] + 1) % SETSIZE;
} while (i[j] == 0 && j > 0);
} while (j > 0 || i[j] != 0);
}
You may think about the elements of a set as values of a cycle counter. 3 sets means 3 for cycles (as in GMan answare), N sets means N (emulated) cycles:
#include <stdlib.h>
#include <stdio.h>
int set[3][2] = { {1,2}, {3,4}, {5,6} };
void print_set( int *ndx, int num_rows ){
for( int i=0; i<num_rows; i++ ) printf("%i ", set[i][ndx[i]] );
puts("");
}
int main(){
int num_cols = sizeof(set[0])/sizeof(set[0][0]);
int num_rows = sizeof(set)/sizeof(set[0]);
int *ndx = malloc( num_rows * sizeof(*ndx) );
int i=0; ndx[i] = -1;
do{
ndx[i]++; while( ++i<num_rows ) ndx[i]=0;
print_set( ndx, num_rows );
while( --i>=0 && ndx[i]>=num_cols-1 );
}while( i>=0 );
}
The most efficient method I could come up with (in C#):
string[] sets = new string[] { "abc", "def", "gh" };
int count = 1;
foreach (string set in sets)
{
count *= set.Length;
}
for (int i = 0; i < count; ++i)
{
var prev = count;
foreach (string set in sets)
{
prev = prev / set.Length;
Console.Write(set[(i / prev) % set.Length]);
Console.Write(" ");
}
Console.WriteLine();
}