Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
array a1 = [1, 2, 3, 4, 5, 8];
array a2 = [1, 4, 5];
array a3 = a1 - a2; /* [2, 3, 8] */
array a4 = a2 - a1; /* print -None */
Here array would be the type my program uses to represent a struct which is used as a container. The rest of it is pseudo code, of course I'm not creating the arrays like that nor subtracting.
The simplest solution I can think of involves nested loops. any idea to solve an efficient way to solve this problem?
You want set difference. If both arrays are sorted, and contain no duplicates, you can iterate through both simultaneously:in linear time.
Whenever you encounter an element b in list B greater than the current element a of A, you can be sure that B does not contain a (or it would not be sorted). Then a is in A and not B, so it is in your output. Proceed to the next element of A, and compare it to the first element of B greater than the previous a, which is your current b. If b < a, it is also less than any remaining element of a, so you can advance to the next element of b. If you do encounter a == b, it is not in the set difference, so compare the next elements of both lists. If you reach the end of B first, add all remaining elements of A. When you reach the end of A, stop.
You can sort a list and remove duplicates in O(n log n) time, or faster if you can radix sort.
Sample Code
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
void print_set( const size_t asize, const int a[asize] )
{
const ptrdiff_t n = (ptrdiff_t)asize;
assert(n >= 0);
putchar('{');
if (n > 0) {
printf( "%d", a[0] );
for ( ptrdiff_t i = 1; i < n; ++i )
printf( ", %d", a[i] );
}
putchar('}');
fflush(stdout);
return;
}
size_t setdiff( const size_t asize, const int a[asize],
const size_t bsize, const int b[bsize],
const size_t csize, int c[csize] )
/* Calculates c = a - b, where a, b and c are sets. Returns the number of
* elements in c. The destination array c must be large enough to hold the
* result (asize elements are always enough). All sets must be sorted and
* contain no duplicates (checked at runtime).
*/
{
ptrdiff_t i = 0, j = 0, k = 0;
const ptrdiff_t m = (ptrdiff_t)asize;
const ptrdiff_t n = (ptrdiff_t)bsize;
const ptrdiff_t p = (ptrdiff_t)csize;
assert(m >= 0);
assert(n >= 0);
assert(p >= 0);
while ( i < m ) {
if ( j == n || a[i] < b[j] ) {
assert( k < p );
c[k++] = a[i++];
assert( i == m || a[i] > a[i-1]);
} else if ( a[i] > b[j] ) {
++j;
assert( j == n || b[j] > b[j-1]);
} else {
assert( a[i] == b[j] );
++i;
assert( i == m || a[i] > a[i-1]);
++j;
assert( j == n || b[j] > b[j-1]);
}
}
return (size_t)k;
}
int main(void)
{
static const int a[] = {1, 2, 3, 4, 5, 6};
static const size_t m = sizeof(a)/sizeof(a[0]);
static const int b[] = {1, 4, 5};
static const size_t n = sizeof(b)/sizeof(b[0]);
int c[6] = {0};
static const size_t p = sizeof(c)/sizeof(c[0]);
print_set( m, a );
printf(" - ");
print_set( n, b );
printf(" = ");
const size_t q = setdiff( m, a, n, b, p, c );
print_set( q, c );
printf(".\n");
print_set( n, b );
printf(" - ");
print_set( m, a );
printf(" = ");
const size_t r = setdiff( n, b, m, a, p, c );
print_set( r, c );
printf(".\n");
return EXIT_SUCCESS;
}
Assuming the two arrays are sorted, as in your examples, you should be able to locate common elements optimally using bsearch. I've provided an example, but in the future please make an effort to write your own C code in the future, then I/we might be able to help you better!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare(void const *x, void const *y) {
return memcmp(x, y, sizeof (int)); // XXX: This might not be what you expect for negative values!
}
size_t difference(void *dest, size_t dest_size, void const *x, size_t x_size, void const *y, size_t y_size, size_t size, int (*compare)(void const *, void const *)) {
typedef unsigned char item[size];
item const *a = x, *b = y;
item *d = dest;
size_t cursor = 0, d_size = 0;
for (size_t x = 0; x < x_size; x++) {
item *ptr = bsearch(a + x, b + cursor, y_size - cursor, size, compare);
if (ptr) {
cursor = ptr - b;
}
else {
memcpy(d[d_size++], a[x], sizeof (item));
}
}
return d_size;
}
void print(char *name, int array[], size_t array_size) {
printf("%s: %s", name, array_size ? "" : "NONE\n");
for (size_t x = 0; x < array_size; x++, putchar(x < array_size ? ',' : '\n')) {
printf("%d", array[x]);
}
}
int main(void) {
int a[] = { 1, 2, 3, 4, 5, 8 },
b[] = { 1, 4, 5 },
c[sizeof a / sizeof *a],
d[sizeof a / sizeof *a];
size_t c_size = difference(c, sizeof c / sizeof *c,
a, sizeof a / sizeof *a,
b, sizeof b / sizeof *b,
sizeof (int), compare);
size_t d_size = difference(d, sizeof d / sizeof *d,
b, sizeof b / sizeof *b,
a, sizeof a / sizeof *a,
sizeof (int), compare);
print("c", c, c_size);
print("d", d, d_size);
}
First, you will obviously need a find function:
int find(int val, const int* a, int na)
{
int i;
for (i = 0; i < na; ++i)
{
if (val == a[i])
return i;
}
return -1;
}
The diff function become quite trivial, assuming you have an allocated array for the result. r should be able to hold at least na elements.
int diff(int* r, const int* a, int na, const int* b, int nb)
{
// returns the number of elenment in resulting set.
// on output r contains the elements from a that are not found in b
// expects r to provide enough room for na elements
int i, nr;
nr = 0;
for (i = 0; i < na; ++i)
{
if (find(a[i], b, nb) < 0)
{
r[nr++] = a[i];
}
}
return nr;
}
I know that in a sense, this is still a nested loop. But it is much more readable. Plus, you've gained a generic find() function that you can use elsewhere in your code.
I thought the difference of two sets was not as you describe, but defined as A with intersection of A and B removed. i.e. If a value is found several times in A, but only once in B, only one element of A is removed from the result set. For this case, you'd need a remove function.
int remove(int at, int* a, int na)
{
// removes first occurence of val in a.
// returns the number of elements in a after remove op.
if (0 <= at && at < na)
{
memmove(&a[at], &a[at + 1], sizeof(int) * (na - (at + 1)));
return na - 1;
}
return na;
}
The diff when elements are not unique is not much more complex, we'll loop on b.
int difference(int* r, const int* a, int na, const int* b, int nb)
{
// returns elements from a, minus set b.
// handles non-unique cases, so this is the true difference or a-b
// returns the number of elements in resulting set
int nr, found;
memcpy(r, a, na * sizeof(int));
nr = na;
for (i = 0; i < nb; ++i)
{
found = find(b[i], r, nr);
if (found >= 0)
{
nr = remove(found, r, nr);
}
}
return nr;
}
I did not try to compile these, so there might be some typos, but it should be fairly close..
Edit: These algorithms have a complexity of O(N^2), so by sorting a (or b) and using a binary search, you could get them down to 1) O(2NlogN) 2) O(3NlogN), which is not bad, since you'd only need to change the find() function to implement optimization.
Related
I am studying the pointers and this question became interesting. I want it to be like this: we have two arrays of integers. Determine the value and number of the largest element of the first array that is not part of the second but I don't know how to make the second part of the code that will check if the largest number is not included in the second array
#include <stdio.h>
int main()
{
long array[100], * maximum, size, c, location = 1;
printf("Enter the number of elements in array\n");
scanf_s("%ld", &size);
printf("Enter %ld integers\n", size);
for (c = 0; c < size; c++)
scanf_s("%ld", &array[c]);
maximum = array;
*maximum = *array;
for (c = 1; c < size; c++)
{
if (*(array + c) > *maximum)
{
*maximum = *(array + c);
location = c + 1;
}
}
printf("Maximum element is present at location number %ld and it's value is %ld.\n", location, *maximum);
return 0;
}
For starters your incomplete code already is wrong. It changes the source array
maximum = array;
//...
*maximum = *(array + c);
More precisely it changes the first element of the array. It shall not do that.
As for your question then the code will look more clear and readable if to implement the algorithm as separate functions.
Here is a demonstration program.
#include <stdio.h>
int find( const int a[], size_t n, int value )
{
const int *p = a;
while (p != a + n && *p != value) ++p;
return p != a + n;
}
int * max_exclusive_element( const int a1[], size_t n1, const int a2[], size_t n2 )
{
const int *max = a1;
while (max < a1 + n1 && find( a2, n2, *max ) ) ++max;
if (max < a1 + n1)
{
for ( const int *p = max; ++p < a1 + n1; )
{
if ( *max < *p && !find( a2, n2, *p ) )
{
max = p;
}
}
}
return ( int * )( max == a1 + n1 ? NULL : max );
}
int main( void )
{
int a1[] = { 1, 3, 5, 6, 7, 8, 9 };
const size_t N1 = sizeof( a1 ) / sizeof( *a1 );
int a2[] = { 1, 3, 5, 7, 9 };
const size_t N2 = sizeof( a2 ) / sizeof( *a2 );
int *max = max_exclusive_element( a1, N1, a2, N2 );
if (max != NULL)
{
printf( "Maximum element is present at location number %td and it's value is %d.\n",
max - a1, *max );
}
}
The program output is
Maximum element is present at location number 5 and it's value is 8.
The function find determines whether a given value is present in an array. And the second function max_exclusive_element finds the maximum element according to the requirement.
As you can see all loops use pointers.
If you need to use arrays with elements of the type long int then it will be not hard to change the presented code.
I think you are doing this in the wrong order. If you find the maximum first and that is in the second array, you need to find the maximum again. You should first check each number in the first array against the second. If it is in the second, change the value to LONG_MIN. Then the maximum will be the right answer. Basically something like this:
#include <limits.h>
int i = 0;
for (; i < n; ++i) {
int j = 0;
for (; j < n; ++j) {
/* in both arrays? */
if (arr2[i] == arr1[j]) {
arr1[j] = LONG_MIN;
break;
}
}
}
At this point any numbers in arr1 that are in arr2 will be set to LONG_MIN. Now just calculate max like you already did.
Edit: changed INT_MIN to LONG_MIN. I didn't notice you were doing long int arrays.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I try to implement the basic Merge Sort, however something goes wrong and it incorrectly duplicates some elements in my input array and even changes some elements, so the output array becomes corrupted. I use tmp[] as a global declared array pointer (long *tmp; -> in global declarations) What am I missing or making wrong?
Also, how can I improve the time complexity of this algorithm?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void merge(long *arr, int l, int m, int r);
void mergeSort(long *arr, int l, int r);
//Global Declarations
long *tmp;
//Merge Sort
void Merge_Sort(long *Array, int Size) {
tmp = malloc(sizeof(long) * Size);
mergeSort(Array, 0, Size - 1);
}
//Merge Sort helper function
void mergeSort(long *arr, int l, int r) {
if (l >= r)
return;
// divide the array into two arrays
// call mergeSort with each array
// merge the two arrays into one
int m = l + ((r - l) / 2; //integer overflow
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
//merge function
static void merge(long *arr, int l, int m, int r) {
//tmp[] is a global array with the same size as arr[]
memcpy(&tmp[l], &arr[l], m - l + 1); //copy left subarray to tmp
memcpy(&tmp[m + 1], &arr[m + 1], r - m); //copy right subarray to tmp
int i = l;
int j = m + 1;
for (int k = l; k <= r; k++) {
if (i > m)
arr[k] = tmp[j++]; //if the left sub-array is exhausted
else
if (j > r)
arr[k] = tmp[i++]; //if the right sub-array is exhausted
else
if (tmp[j] < tmp[i])
arr[k] = tmp[j++]; //compare the current values
else
arr[k] = tmp[i++];
}
}
int main() {
long array[10] = {
-3153274050600690459,
6569843820458972605,
-6837880721686463424,
1876340121514080353,
-1767506107468465601,
-1913444019437311076,
-426543213433372251,
6724963487502039099,
-1272217999899710623,
3399373277871640777,
};
Merge_Sort(array, 10);
for (int i = 0; i < 10; i++) {
printf("%ld\n". array[i]);
}
return 0;
}
Output (incorrect):
-1913444019437311076
-426543213433372251
140464981228095
140388532523709
94285492859968
94285492861503
-1767506107468465601
6724963487502039099
-1272217999899710623
3399373277871640777
Expected output:
-6837880721686463424
-3153274050600690459
-1913444019437311076
-1767506107468465601
-1272217999899710623
-426543213433372251
1876340121514080353
3399373277871640777
6569843820458972605
6724963487502039099
The merge function does not copy the correct number of bytes:
memcpy(&tmp[l], &arr[l], m - l + 1); //copy left subarray to tmp
memcpy(&tmp[m + 1], &arr[m + 1], r - m); //copy right subarray to tmp
You must compute the correct number of bytes by multiplying the number of elements by the size of the element. Note also that the left and right subarrays are contiguous, so it suffices to write:
memcpy(&tmp[l], &arr[l], sizeof(*tmp) * (r - l + 1));
There are other problems:
avoid using a global variable tmp, just pass it to mergeSort as an extra argument
you must free the temporary array after mergeSort() finishes.
Here is a modified version:
#include <stdlib.h>
#include <string.h>
//merge function
static void merge(long *arr, int l, int m, int r, long *tmp) {
//tmp[] is a global array with the same size as arr[]
memcpy(&tmp[l], &arr[l], sizeof(*tmp) * (r - l + 1));
for (int k = l, i = l, j = m + 1; k <= r; k++) {
if (i <= m && (j > r || tmp[i] <= tmp[j]))
arr[k] = tmp[i++];
else
arr[k] = tmp[j++];
}
}
//Merge Sort helper function
static void mergeSort(long *arr, int l, int r, long *tmp) {
if (l < r) {
// divide the array into two arrays
// call mergeSort with each array
// merge the two arrays into one
int m = l + (r - l) / 2; //avoid integer overflow
mergeSort(arr, l, m, tmp);
mergeSort(arr, m + 1, r, tmp);
merge(arr, l, m, r);
}
}
//Merge Sort
void Merge_Sort(long *array, int size) {
long *tmp = malloc(sizeof(*tmp) * size);
mergeSort(array, 0, Size - 1, tmp);
free(tmp);
}
Regarding your other question: how can I improve the time complexity of this algorithm?
The merge sort algorithm has a time complexity of O(N * log(N)) regardless of the set distribution. This is considered optimal for generic data. If your data happens to have known specific characteristics, other algorithms may have a lower complexity.
if all values are n a small range, counting sort is a good alternative
if there are many duplicates and a small number K of distinct unique values, the complexity can be reduced to O(N + K.log(K)).
integer values can be sorted with radix sort that can be more efficient for large arrays.
if the array is almost sorted, insertion sort or a modified merge sort (testing if the left and right subarrays are already in order with a single initial test) can be faster too.
Using Timsort can result in faster execution for many non random distributions.
Here is an implementation of radix_sort() for arrays of long:
#include <stdlib.h>
#include <string.h>
void radix_sort(long *a, size_t size) {
size_t counts[sizeof(*a)][256] = {{ 0 }}, *cp;
size_t i, sum;
unsigned int n;
unsigned long *tmp, *src, *dst, *aa;
dst = tmp = malloc(size * sizeof(*a));
src = (unsigned long *)a;
for (i = 0; i < size; i++) {
unsigned long v = src[i] + (unsigned long)VAL_MIN;
for (n = 0; n < sizeof(*a) * 8; n += 8)
counts[n >> 3][(v >> n) & 255]++;
}
for (n = 0; n < sizeof(*a) * 8; n += 8) {
cp = &counts[n >> 3][0];
for (i = 0, sum = 0; i < 256; i++)
cp[i] = (sum += cp[i]) - cp[i];
for (i = 0; i < size; i++)
dst[cp[((src[i] + (unsigned long)VAL_MIN) >> n) & 255]++] = src[i];
aa = src;
src = dst;
dst = aa;
}
if (src == tmp)
memcpy(a, src, size * sizeof(*a));
free(tmp);
}
/*I recently learnt qsort function. This c code is giving incorrect output. Need help with this.
PROBLEM- Sorting an array of integers in alternate fashion. (the elements at even indices and those at odd indices are sorted separately)
OUTPUT- 0 4 1 2 5 8 7 5 9 3
10 5
*/
#include <stdio.h>
#include <stdlib.h>
// This function is used in qsort to decide the relative order
// of elements at addresses p and q.
int comparator(const void *p, const void *q)
{
// Get the values at given addresses
int l = *(const int *)p;
int r = *(const int *)q;
return (l-r);
}
// A utility function to print an array
void printArr(int arr[], int n)
{
int i;
for (i = 0; i < n; i = i+1)
printf("%d ", arr[i]);
}
// Driver program to test above function
int main()
{
int arr[] = {1,4,7,2,9,3,0,8,6,5};
int size0 = sizeof(arr) / sizeof(arr[0]);
int size1 = (int) ((float)sizeof(arr) / sizeof(arr[0]) / 2 + 0.5);
int size2 = size0 - size1;
qsort((void *)arr+1, size2, 2*sizeof(arr[0]), comparator);
//sort odd positions
qsort((void *)arr, size1, 2*sizeof(arr[0]), comparator);
//sort even positions
printf("Output array is\n");
printArr(arr, size0);
printf("\n%d %d", size0, size1);
return 0;
}
It is possible to use qsort() for sorting of even/odd elements separately.
However, the setup must be changed slightly to accomplish this.
As Peter mentioned correctly (and I didn't consider before as I must admit) sorting for even elements will "destroy" the result for odd elements as swapping considers the element size which is denoted as pair of even and odd element.
This in mind, a solution can be done for all that if the result of first sorting is saved before second sorting is done.
In my sample, I copied the relevant elements after first sorting and merged them in after second sorting.
This is my sample testQSortEvenOdd.c:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
int compEven(const int *p1, const int *p2)
{
return (p1[0] > p2[0]) - (p1[0] < p2[0]);
}
int compOdd(const int *p1, const int *p2)
{
return (p1[1] > p2[1]) - (p1[1] < p2[1]);
}
void printArray(size_t n, int *arr, int step)
{
for (; n--; arr += step) printf(" %d", *arr);
putchar('\n');
}
int main()
{
int arr[] = { 1, 4, 7, 2, 9, 3, 0, 8, 6, 5 };
enum { size = sizeof arr / sizeof *arr };
assert(!(size & 1));
/* sort odd positions */
qsort(arr, (size + 1) / 2, 2 * sizeof *arr,
(int(*)(const void*, const void*))&compOdd);
/* output of sorted array for odd positions */
puts("Odd elements sorted:");
printArray(size / 2, arr + 1, 2);
int arrRes[(size + 1) / 2];
for (size_t i = 1; i < size; i += 2) arrRes[i / 2] = arr[i];
/* sort even positions */
qsort(arr, (size + 1) / 2, 2 * sizeof *arr,
(int(*)(const void*, const void*))&compEven);
/* output of sorted array for even positions */
puts("Even elements sorted:");
printArray((size + 1) / 2, arr, 2);
/* merge array with copy */
for (size_t i = 1; i < size; i += 2) arr[i] = arrRes[i / 2];
puts("Merged elements:");
printArray(size, arr, 1);
/* done */
return 0;
}
Tested in Cygwin on Windows 10 (64 bit):
$ gcc --version
gcc (GCC) 6.4.0
$ gcc -std=c11 -o testQSortEvenOdd testQSortEvenOdd.c
$ ./testQSortEvenOdd
Odd elements sorted:
2 3 4 5 8
Even elements sorted:
0 1 6 7 9
Merged elements:
0 2 1 3 6 4 7 5 9 8
$
Some additional notes:
The way I (and the questioner) used qsort(), it handles two consecutive int values at once. Hence, it must be granted that the array has an appropriate number of elements. (Otherwise, qsort() either does out-of-bound access or cannot consider the last element.) To consider this fact, I inserted the
assert(!(size & 1));
which can be read as "Assure that the array has an even number of elements."
I decided to make separate functions compEven() and compOdd() as IMHO it simplified things. I changed the signature of both to my needs and got complains (warnings) from gcc about wrong function signature. Therefore, I casted the function pointers to the expected type (to make gcc silent).
Jonathon gave a nice hint to make the comparison functions robust against underflow issues. return p1[0] - p2[0]; can cause wrong results when the difference becomes greater than INT_MAX or smaller than INT_MIN. Instead he recommends to use:
return (p1[0] > p2[0]) - (p1[0] < p2[0]);
which never can have any overflow/underflow issues.
How it works:
In case a < b: (a > b) - (a < b) ⇒ 0 - 1 ⇒ -1
In case a == b: (a > b) - (a < b) ⇒ 0 - 0 ⇒ 0
In case a > b: (a > b) - (a < b) ⇒ 1 - 0 ⇒ 1
Very clever Jonathan Leffler – I'm impressed.
qsort:
void qsort( void *ptr, size_t count, size_t size,
int (*comp)(const void *, const void *) );
Sorts the given array pointed to by ptr in ascending order. The array contains count elements of size bytes. Function pointed to by comp is used for object comparison.
ptr - pointer to the array to sort
count - number of element in the array
size - size of each element in the array in bytes
comp - comparison function which returns a negative integer value if
the first argument is less than the second,
In your program, you are passing size of each element as 2*sizeof(arr[0]) which results in 8 bytes which is incorrect input to qsort(). Hence you are getting incorrect output.
qsort needs a contiguous block of memory to function properly.
If you need to sort odd and even indexed elements separately, you could start by separating the elements, sort them indipendently and then merge the two parts.
You can do that even without allocating any extra memory:
#include <stdio.h>
#include <stdlib.h>
int less_int(const void *lhs, const void *rhs)
{
return *(const int *)lhs < *(const int *)rhs ? -1
: *(const int *)lhs > *(const int *)rhs ? 1 : 0;
}
int greater_int(const void *lhs, const void *rhs)
{
return *(const int *)lhs > *(const int *)rhs ? -1
: *(const int *)lhs < *(const int *)rhs ? 1 : 0;
}
void sort_ascending(int* arr, size_t n)
{
qsort(arr, n, sizeof *arr, less_int);
}
void sort_descending(int* arr, size_t n)
{
qsort(arr, n, sizeof *arr, greater_int);
}
inline void swap_int(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
size_t partition_odd_even(int* arr, size_t n )
{
size_t n_odds = n - n / 2;
for (size_t i = 1, j = n_odds + n_odds % 2; i < n_odds; i += 2, j += 2)
{
swap_int(arr + i, arr + j);
}
return n_odds;
}
void interleave_odd_even(int* arr, size_t n )
{
size_t n_odds = n - n / 2;
for (size_t i = 1; i < n_odds; ++i )
{
for (size_t j = n_odds - i; j < n_odds + i; j += 2)
{
swap_int(arr + j, arr + j + 1);
}
}
}
void print_arr(int* arr, size_t n);
int main(void)
{
int arr[] = {1, 4, 7, 2, 9, 3, 0, 8, 6, 5};
size_t arr_size = sizeof arr / sizeof *arr;
print_arr(arr, arr_size);
size_t n_odds = partition_odd_even(arr, arr_size);
size_t n_evens = arr_size - n_odds;
// print_arr(arr, arr_size);
sort_ascending(arr, n_odds);
// print_arr(arr, n_odds);
sort_descending(arr + n_odds, n_evens);
// print_arr(arr + n_odds, n_evens);
interleave_odd_even(arr, arr_size);
print_arr(arr, arr_size);
return 0;
}
void print_arr(int* arr, size_t n)
{
for(size_t i = 0; i < n; ++i)
{
printf(" %d", arr[i]);
}
puts("");
}
Which gives:
1 4 7 2 9 3 0 8 6 5
0 8 1 5 6 4 7 3 9 2
EDIT
As noted in the comments below by greybeard the code above is not really time efficient as the merge part is O(N²). Using a temporary array which contains only the elements to be sorted in a particular way, the following program only need O(N) extra time and O(N/K) space, where K is the number of different sorting order needed (2 in OP's question).
Jonathan Leffler also pointed out that it could be made more general allowing the algorithm "to handle 3, 4, … N evenly interlaced sub-arrays, possibly with different sort orders for each". I implemented it in the following snippet by passing to the sort function an array of pointer to compare functions.
#include <stdio.h>
#include <stdlib.h>
// compare functions
typedef int (*PCMPFN)(const void*, const void*);
int ascending_cmp_int(const void *lhs, const void *rhs)
{
return *(const int *)lhs < *(const int *)rhs ? -1
: *(const int *)lhs > *(const int *)rhs ? 1 : 0;
}
int descending_cmp_int(const void *lhs, const void *rhs)
{
return *(const int *)lhs > *(const int *)rhs ? -1
: *(const int *)lhs < *(const int *)rhs ? 1 : 0;
}
// This function is never called. Whithout knowing the actual implementation
// of 'qsort' we can't make any assumption
int untouched_cmp_int(const void *lhs, const void *rhs)
{
(void)lhs; // Those parameters are unused here, this is to avoid a warning
(void)rhs;
return 0;
}
// Copy the elements of the source array starting from index 'start' with stride 'step'
size_t strided_split(int* dest, const int *src, size_t n, size_t start, size_t step)
{
size_t j = 0;
for (size_t i = start; i < n; i += step, ++j)
{
dest[j] = src[i];
}
return j;
}
// Inverse of the previous
void strided_merge(int* dest, const int *src, size_t n, size_t start, size_t step)
{
for (size_t i = start, j = 0; j < n; i += step, ++j)
{
dest[i] = src[j];
}
}
// Apply different sort orders to different elements
void alternate_sort(int* arr, const size_t n, PCMPFN comps[], const size_t k)
{
int tmp[n/k + 1]; // Please note that VLA are optional in C11
for ( size_t i = 0; i < k; ++i )
{
if ( comps[i] == untouched_cmp_int )
continue;
// First select the elements
size_t n_copied = strided_split(tmp, arr, n, i, k);
// then sort only them as needed
qsort(tmp, n_copied, sizeof tmp[0], comps[i]);
// Once sorted, copy back the elements in the source array
strided_merge(arr, tmp, n_copied, i, k);
}
}
void print_arr(const int* arr, const size_t n);
int main(void)
{
int arr[] = {1, 4, 7, 2, 9, 3, 0, 8, 6, 5};
const size_t N = sizeof arr / sizeof *arr;
print_arr(arr, N);
PCMPFN compares[] = {
descending_cmp_int, untouched_cmp_int, ascending_cmp_int
};
const size_t K = sizeof compares / sizeof *compares;
alternate_sort(arr, N, compares, K);
print_arr(arr, N);
return 0;
}
void print_arr(const int* arr, const size_t n)
{
for(size_t i = 0; i < n; ++i)
{
printf(" %d", arr[i]);
}
puts("");
}
I am creating a function which can compare two arrays. It returns 1 when they are the same and return 0 when they are not.
It required the program run as linear time, so i cannot use a for-for loop to compare it. Any suggestions for me?
Examples of arrays for which scrambled should return 1:
a = {10,15,20}, b = {10,15,20}
a = {1,2,3,4,5}, b = {5,3,4,2,1}
a = {}, b = {} (i.e. len = 0)
a = {2,1,3,4,5}, b = {1,2,4,3,5}
Examples of arrays for which scrambled should return 0:
a = {1,1}, b = {1,2}
a = {10,15,20}, b = {10,15,21}
a = {1,2,3,4,5}, b = {5,3,4,2,2}
If you can specify a maximum value for the array elements, you can compare them in linear time pretty easily, by just looping through each one and counting the values which are present, like so:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_ARRAY_VALUE 100
bool compare_arrays(int * arr1, size_t arr1_size,
int * arr2, size_t arr2_size)
{
/* Set up array to count elements */
int * table = calloc(MAX_ARRAY_VALUE + 1, sizeof * table);
if ( !table ) {
perror("couldn't allocate memory");
exit(EXIT_FAILURE);
}
/* Increment index if element is present in first array... */
for ( size_t i = 0; i < arr1_size; ++i ) {
table[arr1[i]]++;
}
/* ...and decrement index if element is present in second array. */
for ( size_t i = 0; i < arr2_size; ++i ) {
table[arr2[i]]--;
}
/* If any elements in array are not zero, then arrays are not equal */
for ( size_t i = 0; i < MAX_ARRAY_VALUE + 1; ++i ) {
if ( table[i] ) {
free(table);
return false;
}
}
free(table);
return true;
}
int main(void) {
int a1[] = {10, 20, 30, 10};
int a2[] = {20, 10, 10, 30};
int a3[] = {1, 4, 5};
int a4[] = {1, 3, 5};
if ( compare_arrays(a1, 4, a2, 4) ) {
puts("a1 and a2 are equal"); /* Should print */
}
else {
puts("a1 and a2 are not equal"); /* Should not print */
}
if ( compare_arrays(a3, 3, a4, 3) ) {
puts("a3 and a4 are equal"); /* Should not print */
}
else {
puts("a3 and a4 are not equal"); /* Should print */
}
if ( compare_arrays(a1, 4, a4, 3) ) {
puts("a1 and a4 are equal"); /* Should not print */
}
else {
puts("a1 and a4 are not equal"); /* Should print */
}
return 0;
}
which outputs:
paul#horus:~/src/sandbox$ ./lincmp
a1 and a2 are equal
a3 and a4 are not equal
a1 and a4 are not equal
paul#horus:~/src/sandbox$
Without specifying the maximum value, you can loop through each array and find the maximum. It'll still be linear time, but without an upper bound you might end up with a huge index table.
Because the comparison of the two arrays is independent of the order of the elements, both must be sorted before they can be compared. Because of this, you can't do this in linear time. The best you can do is O(n log n), as that is the best order of most sorting algorithms.
My C is pretty rusty so the may be some memory problem with the script below. However, the basic task is to sort the 2 arrays, compare them element by element. If they all match, scramble should return 1, other it's 0.
The script below works with GCC, haven't tested it with any other compiler. It compares arrays of equal length. The case of unequal length is left for the OP as an minor exercise.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare_ints(const void * a , const void * b)
{
const int * ia = (const int *)a;
const int * ib = (const int *)b;
return *ia > *ib;
}
int scramble(const int * a, const int * b, unsigned int len)
{
int * sorted_a = malloc(len * sizeof(int));
int * sorted_b = malloc(len * sizeof(int));
memcpy(sorted_a, a, len * sizeof(int));
memcpy(sorted_b, b, len * sizeof(int));
qsort(sorted_a, len, sizeof(int), compare_ints);
qsort(sorted_b, len, sizeof(int), compare_ints);
for (int i = 0; i < len; i++)
{
if (sorted_a[i] != sorted_b[i])
{
free(sorted_a);
free(sorted_b);
return 0;
}
}
free(sorted_a);
free(sorted_b);
return 1;
}
int main (int argc, char const *argv[])
{
int a[3] = {20, 10, 15};
int b[3] = {10, 15, 20};
int is_equal = scramble(a, b, 3);
printf("is_equal = %i\n", is_equal);
return 0;
}
FYI: you can't do it in linear time. qsort has O(n log n).
At a certain extent, you can obtain the sum of all the hashed values of the first array, and compare it to the sum of the hashed values of the second array. It'll work, but I don't know precise it is.
Here is my attempt, all my tests have given positive results so far :
#include <stdio.h>
unsigned int hash(unsigned int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x);
return x;
}
int compareArrays(int *arr1, int *arr2, int size) {
if (size == 0) return 1;
unsigned long sum1 = 0;
unsigned long sum2 = 0;
for (int i = 0; i < size; ++i) {
sum1 += hash(arr1[i]);
}
for (int i = 0; i < size; ++i)
sum2 += hash(arr2[i]) ;
return sum1 == sum2;
}
int main(void) {
int a[] = {1,2,3,4,5,6,7,8,255};
int b[] = {1,2,3,4,5,6,7,8,9};
int size = 9;
printf("Are they the same? %s.\n", compareArrays(a, b, size) ? "yes" : "no");
return 0;
}
You can use memcmp function with this format:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
For example;
/* memcmp example */
#include <stdio.h>
#include <string.h>
int main ()
{
char buffer1[] = "abcde";
char buffer2[] = "abcde";
int n;
n=memcmp ( buffer1, buffer2, sizeof(buffer1) );
if (n>0) printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
else if (n<0) printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
else printf ("'%s' is the same as '%s'.\n",buffer1,buffer2);
return 0;
}
output is:
'abcde' is same as 'abcde'.
This question already has answers here:
How do I determine the size of my array in C?
(24 answers)
Closed 7 years ago.
I have some C code to practice the quick sort. I want to use macro the get the length of the array. The macro works fine in the main() function. But when I use the macro inside the sort function, it does not return the length of array.
Please see the comments inside the code I left.
Also, I want to use struct to create the member function pointer called "sort" and "quick_sort". Any people who are good at c programming gives me some advise if there are some points that I can improve, not matter the syntax, the code format. I feel kind of weird about the sort and quick_sort functions format inside the struct. My purpose is use Array struct to call the functions.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NELEMS(a) (sizeof(a) / sizeof(a[0]))
typedef struct _Array Array;
struct _Array
{
void (*sort)(int* arr);
void (*quick_sort)(int* arr, int l, int r);
};
void sort(int* arr);
void sort(int* arr)
{
// Issues here.
// The len is 2 not 5.
// the macro returns the sizeof arr here is 8, not 20.
int len = NELEMS(arr);
if(len == 0){
return;
}
void quick_sort(int* arr, int l, int r);
quick_sort(arr, 0, len-1);
}
void quick_sort(int* arr, int l, int r)
{
int j;
if(l < r)
{
j = partition(arr, l, r);
quick_sort(arr, l, j - 1);
quick_sort(arr, j+1, r);
}
}
int partition( int* a, int l, int r) {
int pivot, i, j, t;
pivot = a[l];
i = l; j = r+1;
while( 1)
{
do ++i; while( a[i] <= pivot && i <= r );
do --j; while( a[j] > pivot );
if( i >= j ) break;
t = a[i]; a[i] = a[j]; a[j] = t;
}
t = a[l]; a[l] = a[j]; a[j] = t;
return j;
}
void print_array(int* array, int len){
int i;
for(i = 0; i < len; i++)
printf("%d, \n", array[i]);
}
int main(int argc, char const *argv[])
{
int nums[5] = {5, 1, 3, 2, 4};
// len is 20 / 4 = 5. It works fine.
int len = NELEMS(nums);
Array *array = malloc(sizeof(Array));
array->sort = sort;
array->quick_sort = quick_sort;
sort(nums);
print_array(nums, NELEMS(nums));
return 0;
}
The macro works in main because nums is an array, sizeof(nums) gets the size of the array.
However, when it's passed as function argument, it's automatically converted to a pointer. In sort(), sizeof(nums) only gets the size of the pointer.
You could fix it by passing the size of the array explicitly.