Parallel sort two arrays - c

Suppose I have two arrays:
char **words = (char**) malloc(MAX * sizeof(char*));
int *count = malloc(MAX * sizeof(int));
The first one stores the words in a given list:
words[0] = "myword0" / words[1] = "myword1" / words[2] = "myword3" ...
and the second one stores the number of occurences of each word in the list:
count[0] = 4 //means that "myword0" appears 4 times in the list
I want to print the most frequent word and its number of occurrences. If there are words that appear the same number of times, print the first one in alphabetical order.
To do that, I thought about alphabetically sorting words:
int sortWordsAlph(const void* a, const void* b)
{
char **x = (char**)a;
char **y = (char**)b;
return (strcmp(*x,*y));
}
and in int main():
qsort(words, MAX, sizeof(char*), sortWordsAlph);
Now, the problem is with count. It should still point to the occurrences of each word, thus meaning I have to sort it too. How can I do this?
Maybe I should use a swapping algorithm instead of qsort?

If you want to use qsort(), you should use an array of structs that contain both the word and the count. You can then pass your array to qsort() with a custom comparison function:
struct wc_s {
char *word;
int count;
};
int cmp_words (const void *p1, const void *p2)
{
const struct wc_s *s1 = p1;
const struct wc_s *s2 = p2;
return strcmp (s1->word, s2->word);
}
int cmp_counts (const void *p1, const void *p2)
{
const struct wc_s *s1 = p1;
const struct wc_s *s2 = p2;
return s2->count - s1->count;
}
...
struct wc_s *wc_list = malloc (MAX * sizeof *wc_list);
...
qsort (wc_list, MAX, sizeof *wc_list, cmp_counts);
If qsort() was guaranteed to perform a stable sort (i.e. two elements that compare the same retain their original order), you could solve your problem by first sorting by word, and then sorting by count. Unfortunately, the resulting order of two equal elements is unspecified.
What you could do is sort the array by count, and then go through the array to find sub-arrays with the same counts, and sort those individually:
int start = 0;
int length;
while (start < MAX) {
for (length = 1; start+length < MAX; length++) {
if (wc_list[start].count != wc_list[start+length].count)
break;
}
qsort (wc_list+start, length, sizeof *wc_list, cmp_words);
start += length;
}

Related

Sorting multiple arrays in the struct based on the ascending order of one array in C

I am trying to sort the multiple arrays based on the ascending order of one array.
Here is the example:
int a[10] = {55140, 32294, 33321, 64321, 55312}
float b[10] = {11.11, 202.22, 3213.21, 144.32, 1.32}
const char* c[10] = {+, -, -, -, +}
unsigned char* d[10] = {22DS3K, 1FGJ29, 21FD43, 98DS03, 56DK23}
Now, after arranging the array 'a' in ascending order I want to sort the other arrays. The output should look like as follows:
a[10] = {32294, 33321, 55140, 55312, 64321}
b[10] = {202.22, 3213.21, 11.11, 1.32, 144.32}
c[10] = {-, -, +, +, -}
d[10] = {1FGJ29, 21FD43, 22DS3K, 56DK23, 98DS03}
Arranging the ascending order works fine. But, I am unable to sort the other arrays. I woould like to create a function to use it in my main function. Pleased to hear some suggestions.
I have seen the below post, but did not help me.
Sorting an array based on another in C
Here is the code that I have tried:
struct Data{
int a[10];
float b[10];
const char* c[10];
unsigned char* d[10];
} data;
int data_a[10];
float data_b[10];
const char* data_c[10];
unsigned char* d[10];
void ascending(int *t; int N){
int i,j,tmp;
for(i=0;j<N;j++){
for(j=i+1;j<N;j++){
if(t[i] > t[j]){
tmp=t[i];
t[i]=t[j];
t[j]=tmp;
}}}}
int main(){
int i;
for(i=0;i<5;i++){
data.a[i] = data_a[i];
data.b[i] = data_b[i];
data.c[i] = data_c[i];
data.d[i] = data_d[i];
}
ascending(data.a, 5);
for(i=0;i<5;i++){
printf("Data is %d,%.2f,%s,%hhn\n", data.a[i],data.b[i],data.c[i],data.d[i]};
}}
May I know if I am missing something or doing something completely wrong?
Keeping such a set of arrays in synch is a nightmare.
What you seem to want is an array of structs instead of 5 separate arrays.
If I get you right, this is what would fit your needs better:
(Also your initializers don't really match the data type of your arrays c and d.)
typedef struct data_s {
int a;
float b;
const char *c;
const char *d;
} data_t;
data_t data[10] =
{
[0] = {.a=55140, .b=11.11, .c="+", .d="22DS3K"},
[1] = {.a=32294, .b=202.22, .c="-", .d="1FGJ29"},
[2] = {.a=33321, .b=3213.21, .c="-", .d="21FD43"},
[3] = {.a=64321, .b=144.32, .c="-", .d="98DS03"},
[4] = {.a=55312, .b=1.32, .c="+", .d="56DK23"}
};
Then you can sort (just use qsort) for member a of your struct and as you will swap whole structs at once, the corresponding other members will be sorted accordingly:
int compare_a(const void *data1, const void*data2)
{
return ((data_t*)data2)->a - ((data_t*)data1)->a;
}
int compare_d(const void *data1, const void*data2)
{
return strcmp(((data_t*)data1)->d, ((data_t*)data2)->d);
}
int main(void)
{
...
// optional:
// Populate the missing fields with some default data
size_t num_elem = sizeof(data)/sizeof(data[0]);
for (size_t i = 5; i < num_elem; i++)
{
data[i].a = (int)i * 111;
data[i].b = i * 1.11;
data[i].c = "none";
data[i].d = "";
}
// Sort for field a
qsort(data, num_elem, sizeof (data[0]), compare_a);
// Variant: Size unknown, use dynamic memory allocation
size_t num_elem2 = 123;
data_t *data2 = malloc(num_elem2 * sizeof (*data2));
for (size_t i = 0; i < num_elem; i++)
{
data2[i].a = (int)i * 111;
data2[i].b = i*1.11;
data2[i].c = "none";
data2[i].d = "";
}
// Sort for field d
qsort(data, num_elem, sizeof (data[0]), compare_d);
}

Using qsort in C on an array of strings

I can't figure out how to use qsort. I want to sort an array of strings. Like so:
John Adam
Adam -> John
Stacy Stacy
However, nothing I do seems to work. I've tried copying exactly what others have used (about 5 different qsort functions from various sources) and nothing has worked. I have one for int's that works (backwards but at least it works).
Here's the necessary code I have:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char name[80];
int age;
} RECORD;
RECORD record[25];
int main (int argc, char *argv[80]){ // Using command line to get my strings
int i = 2, j;
for(j = 0; j < (argc / 2); j++) //Converting and storing my ages
{
record[j].age = atoi(argv[i]);
i = i + 2;
}
int p, q = 1;
for(p = 0; p < (argc / 2); p++)
{
strcpy(record[p].name, argv[q]);
q = q + 2;
}
}
int compareByName(const void* a, const void* b) //The qsort that doesn't work at all
{
const char *ia = (const char *)a;
const char *ib = (const char *)b;
return strncmp(ia, ib, 25);
}
int compareByAge (const void * a, const void * b) //My other qsort that works backwards
{
RECORD *RECORDA = (RECORD *)a;
RECORD *RECORDB = (RECORD *)b;
return ( RECORDB->age - RECORDA->age );
}
void printRecords(RECORD r[], int num){
//printing stuff here
double size = sizeof r[0];
double count = sizeof(r)/size; //My qsort with size stuff, doesn't work
qsort(r, count, size, compareByName); // if I do it the same as the other
qsort (r, 25, sizeof(RECORD), compareByAge); //My other qsort that works backwards
//more printing stuff here
}
You don't have an array of strings, you have an array of RECORDs, and it sounds like you want to sort that array based on the strings in the name array of the records. So you want something like:
int compareByName(const void *a_, const void *b_) {
RECORD *a = a_, *b = b_;
return strcmp(a->name, b->name);
}
and then you sort with
qsort (r, 25, sizeof(RECORD), compareByName);
Okay, I see several issues here.
It's worth noting that sizeof is evaluated at compile time, so you can't use sizeof(r) to determine the size of a dynamically-sized array you were passed. I'm going to guess that's why num is passed in to printRecords.
As #Chris points out, you're sorting RECORD structures rather than character pointers, so the comparison function and the qsort call both need to take that into account.
You have the subtraction reversed in the age comparison - it needs to return a negative number if the left side is less than the right side, so use RECORDA->age - RECORDB->age.
First off (this is why your strings aren't sorting), double count = sizeof(r)/size; is wrong. sizeof(r) isn't doing what you expect it to. You'll need to pass the size of the array to printRecords() as described in this question:
How to get the length of array in C? is "sizeof" is one of the solution?
Second off,
int compareByAge (const void * a, const void * b) //My other qsort that works backwards is backwards because you're doing it backwards. Comparator functions always return A - B, not B - A.

Swaping two rows of 2 dimensional character array in C

I have a 2D character array, I want to swap two rows of this array.
I can think of this function.
char str[5][5];
swap(str[i],str[j]);
void swap(char * p, char *q) {
char *temp;
temp = p;
p = q;
q = temp;
}
This function does not work.
I also came up with this,
char ** temp1;
char ** temp2;
temp1 = &str[i];
temp2 = &str[j];
*temp1 = str[j];
*temp2 = str[i];
This does not work either, please suggest me a proper way to do this task.
Try the following approach
#include <stdio.h>
#include <string.h>
void swap( char *s1, char *s2, size_t n )
{
char t[n];
strcpy( t, s1 );
strcpy( s1, s2 );
strcpy( s2, t );
}
#define N 5
int main(void)
{
char s[N][N] =
{
"Cat",
"Dog",
"Cow",
"Bird",
"Goat"
};
for ( size_t i = 0; i < N; i++ ) puts( s[i] );
puts( "" );
swap( s[0], s[3], N );
for ( size_t i = 0; i < N; i++ ) puts( s[i] );
return 0;
}
The program output is
Cat
Dog
Cow
Bird
Goat
Bird
Dog
Cow
Cat
Goat
Take into account that you can use also standard function strncpy instead of strcpy.
The other approach is to use standard function memcpy that to copy exactly N characters.
void swap( char * ary, int idx1, int idx2, int rowlen )
{
for ( int x=0; x<rowlen; x++ )
{
temp = ary[idx1][x];
ary[idx1][x] = ary[idx2][x];
ary[idx2][x] = temp;
}
}
#define ROWLEN 5
void main( void )
{
char str[5][ROWLEN];
swap( str, 2, 4, ROWLEN ); // swap rows 2 and 4
}
I didn't compile this so there might be syntax errors, but this should convey the idea.
Use a loop to swap every elements of the two rows :
void swapRows(char* row1, char* row2, size_t rowlen)
{
for (size_t i=0; i<rowlen; ++i)
{
char tmp = row1[i];
row1[i] = row2[i];
row2[i] = tmp;
}
}
Here we have a function that takes a pointer to two rows of your array, the length of a row, and that runs a very simple loop to swap every elements of those rows. Here is how you would use it :
int main()
{
char str[2][2] = {{'1','2'},{'3','4'}};
swapRows(&str[0][0], &str[1][0], 2);
// str now contains {'3','4'},{'2','1'}
}
What you seem to have tried is to swap the pointers to the rows instead of physically moving the content of the rows. That can't work. Pointers only allow you to find your data, but moving them around doesn't actually modify that data. Now, if you had a char** array this idea would work, but in the case of a contiguous 2D char[][] array you have to move things physically.
Since this is a contiguous 2D array, not an array of pointers, one should use memcpy to copy five-element subarrays, like this:
void swap(char *p, char *q) {
char temp[5];
memcpy(temp, p, sizeof(temp));
memcpy(p, q, sizeof(temp));
memcpy(q, temp, sizeof(temp));
}
The idea is to make an array buffer sufficient to hold one "row", copy the first row into it, and then do the swap the usual way (i.e. with an assignment of a temporary variable).
Demo.

string_comparator in C

Okay so I need to several quite long strings in C. So I say to myself "why, you'd better use that handy dandy qsort function! Better write yourself a string_comparator for it!"
So of course I do and here she is:
int string_comparator(const void* el1, const void* el2) {
char* x = (char*) el1;
char* y = (char*) el2;
int str_len = strlen(x);
int i = 0;
for (; i < str_len; i++) {
//when there are non-equal chars
if (x[i] != y[i]) {
break;
}
}
return x[i] - y[i];
}
So of course I pass my handy dandy string_comparator function to the C qsort function as such:
qsort(list.words, list.num_words, sizeof(char*), string_comparator);
list is a struct that holds a char** (words) and ints which refer to the number of words held by it (such as num_words)
Now I have the problem where my list is not getting sorted alphabetically like I had hoped! I put a bunch of printf statements in my comparator and it printed out garbage values for the strings every time so I'm fairly sure that is the problem. But why is that the problem?? I've used qsort before (never to sort words..just sorting characters) and from what I understand this should work...What's going wrong here?
I appreciate any suggestions!
This is a common mistake when using qsort(). Here are the corrections:
char *x = *(char **) el1;
char *y = *(char **) el2;
Because list.words has type char **, not type char *, right?
Another example of qsort()
Here's how you sort an array of int with qsort():
int int_comparator(const void *el1, const void *el2)
{
int x = *(int *) el1;
int y = *(int *) el2;
return x - y;
}
void sort_ints(int *a, size_t n)
{
// these two lines are both "correct"
// the second line is more "obviously correct"
// qsort(a, n, sizeof(int), int_comparator);
qsort(a, n, sizeof(*a), int_comparator);
}
Now, if you go through and replace int with char *, you have to replace int * with char **.

How to do reverse memcmp?

How can I do reverse memory comparison? As in, I give the ends of two sequences and I want the pointer to be decremented towards the beginning, not incremented towards the end.
There's no built-in function in the C standard library to do it. Here's a simple way to roll your own:
int memrcmp(const void *s1, const void *s2, size_t n)
{
if(n == 0)
return 0;
// Grab pointers to the end and walk backwards
const unsigned char *p1 = (const unsigned char*)s1 + n - 1;
const unsigned char *p2 = (const unsigned char*)s2 + n - 1;
while(n > 0)
{
// If the current characters differ, return an appropriately signed
// value; otherwise, keep searching backwards
if(*p1 != *p2)
return *p1 - *p2;
p1--;
p2--;
n--;
}
return 0;
}
If you something high-performance, you should compare 4-byte words at a time instead of individual bytes, since memory latency will be the bottleneck; however, that solution is significantly more complex and not really worth it.
Much like in a post (C memcpy in reverse) originally linked by Vlad Lazarenko, here is a solution based on that, that I haven't yet tested but should get you started.
int reverse_memcmp(const void *s1, const void *s2, size_t n)
{
unsigned char *a, *b;
a = s1;
b = s2;
size_t i = 0;
// subtracting i from last position and comparing
for (i = 0; i < n; i++) {
if (a[n-1-i] != b[n-1-i]) {
// return differences between different byte, strcmp()-style
return (a[n-1-i] - b[n-1-i]);
}
}
return 0;
}
All you need to do is specify your two ends and the size that you'd like to compare, as well as a step size. Please note especially that the step size may be the most important part for getting the expected results. It will greatly ease the implementation if you restrict the sizes. For the size of a char you could do something like:
int compare (void *one, void *two, size_t size)
{
char *one_char = (char *)one;
char *two_char = (char *)two;
size_t i;
for (i = 0; i < size; i++)
{
if (*(one_char - i) != *(two_char - i))
return(NOT_EQUAL);
}
return(EQUAL);
}
shorter code (C code doesn't need force pointer type cast):
int reverse_memcmp(const void *end1, const void *end2, size_t n) {
const unsigned char *a = end1, *b = end2;
for (; n; --n)
if (*--a != *--b) return *a - *b;
return 0;
}

Resources