Related
I'm doing quick sorting with different methods of selecting pivots, I don't see any problems in my code, the functions work while testing separated, but when I put them together, they don't work most of the time.
I've tried moving the files to another path, and changing the way I access the array.
void quick_sort(uint32_t arr[], int first, int last, int pivot_opt)
{
int i, j;
uint32_t pivot = pivot_select(arr, last, pivot_opt);
i = first;
j = last;
do
{
comparations_count++;
while (arr[i] < pivot) i++; // Counting elements smaller than pivot
comparations_count++;
while (arr[j] > pivot) j--; // Counting elements greater than pivot
comparations_count++;
if (i <= j)
{
exchanges_count++;
swap(&arr[i++], &arr[j--]); // Placing smaller elements in the left, and greater elements in the right without touching the pivot
}
comparations_count++;
} while (i <= j);
comparations_count++;
if (first < j)
quick_sort(arr, first, j, pivot_opt); // Sorting smaller elements of array
comparations_count++;
if (i < last)
quick_sort(arr, i, last, pivot_opt); // Sorting greater elements of array
}
uint32_t pivot_select(uint32_t arr[], int last, int pivot_opt)
{
uint32_t pivot = 0;
int random_index = 0;
switch (pivot_opt)
{
case 0:
pivot = arr[last]; // Choosing the pivot as the last element in the array
break;
case 1:
random_index = rand()%(last); // Choosing the pivot as a random element of array
pivot = arr[random_index];
break;
case 2:
pivot = median(arr, last); // Choosing the pivot as avg of three random indexes of the array
break;
default:
break;
}
return pivot;
}
uint32_t median(uint32_t arr[], int n)
{
if (n <= 3)
{
return arr[0]; // If the array have 3 or less elements, choose as pivot first element
}
else
{
int index[3] = {0}; // Index of 3 elements of original array
int last_index = 0; // Last chosen index, to verify if index was selected
int i = 0;
while(i < 3) // Selecting 3 random index
{
int current_index = (rand()%(n));
if (current_index == last_index)
i--;
else
{
index[i++] = current_index;
last_index = current_index;
}
}
uint32_t array[3] = {arr[index[0]], arr[index[1]], arr[index[2]]}; // Creating array with the elements on random indexes
insertion_sort(array, 3); // Sorting the array
return array[1]; // Returning the pivot as the middle element of array
}
}
I'm getting this error on median function
Program received signal SIGSEGV, Segmentation fault.
0x0000555555555546 in median (arr=<error reading variable: Cannot access memory at address 0x7fffff7fefd8>, n=<error reading variable: Cannot access memory at address 0x7fffff7fefd4>) at /media/storage/Codes/Data Structure/Recursive_Sorting/main.c:107
107 {
I put all the libraries I'm using so I don't miss one.
Code for testing:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
#define size 1000
uint32_t comparations_count;
uint32_t exchanges_count;
int main(int argc, char** argv)
{
uint32_t array[size];
fill(array, size);
permute_array(array, size);
comparations_count = 0;
exchanges_count = 0;
quick_sort(array, 0, size-1, 2);
return 0;
}
void fill(uint32_t arr[], uint32_t n)
{
for (size_t i = 0; i < n; i++) // Filling the array in ascending order
arr[i] = i;
}
void swap(uint32_t *a, uint32_t *b)
{
// Swapping two elements
uint32_t t = *a;
*a = *b;
*b = t;
}
void permute_array(int a[], size_t n)
{
// Adapted from:
// https://www.geeksforgeeks.org/shuffle-a-given-array-using-fisher-yates-shuffle-algorithm/
srand(time(NULL)); // Init random seed
for (size_t i = n - 1; i > 0; i--) // Permute array
{
size_t j = rand() % (i+1); // Pick a random index from 0 to i
swap(&a[i], &a[j]); // Swap arr[i] with the element at random index
}
}
void insertion_sort(uint32_t arr[], size_t n)
{
uint32_t current_index = 0;
uint32_t current_value = 0;
for (size_t i = 1; i < n; i++) {
current_index = i;
current_value = arr[i];
while (current_index > 0) {
if (current_value < arr[current_index - 1]) {
swap(&arr[current_index], &arr[current_index-1]);
current_index--;
}else { break; }
}
}
}
Let's start with this one:
while (arr[i] < pivot) i++;
What if all the elements are less than pivot, your i will be out of bounds, change the condition to while(arr[i] < pivot && i <= j) i++;
Consider this one:
while (arr[j] > pivot) j--;
What if all the elements are greater than pivot, your j will be out of bounds (a negative number), change the condition here too.
According to my opinion, the above-mentioned areas are causing problems.
Happy debugging!
I wish to sort a second array as per the first array. e.g.
first = {1,8,7,2,4}
second = {9,7,2,10,3}
I want first to be unchanged and second to be sorted in the same relative order as the first. i.e. the lowest value is at index 0, the second lowest value is at index 3, third lowest value is at index 4 etc etc
second = {2,10,9,3,7}
I have already tried some code for the following
#include <stdio.h>
typedef struct
{
int num;
int pos;
}ArrType;
ArrType arrA[5] = {{1,0},{8,1},{7,2},{2,3},{4,4}};
ArrType arrB[5] = {{9,0},{7,1},{2,2},{10,3},{3,4}};;
int cmparr(const void *a, const void *b)
{
ArrType *tmpa, *tmpb;
tmpa = (ArrType*) a;
tmpb = (ArrType*) b;
return(arrA[tmpa->pos].num - arrA[tmpb->pos].num);
}
int main(void)
{
int i;
qsort(arrB,5, sizeof(ArrType), cmparr);
for (i=0; i<5; i++)
{
printf ("%d ",arrB[i].num);
}
return (0);
}
The actual output is
9 10 3 2 7
I am open to a different data structure, but arrB should only be sorted one time.
I have seen some solutions for this in C++, Javascipt and other languages. But there is not a solution in C.
Edit - These arrays would be quite large in the final program. I am looking for a single sorting operation. i.e. single call to qsort
You need to create the meta-data that matches the desired ordering (i.e an array of indexes). Then apply that meta-data to the second array.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int first[] = {1,8,7,2,4};
int second[] = {9,7,2,10,3};
int compare(const void * a, const void * b);
int binary_search(int array[], int min, int max, int target);
void print_array(int * array, int c);
int main()
{
int idx;
int c = sizeof(first)/sizeof(int);
int * sorted = NULL;
int * indexes = NULL;
int * result = NULL;
if (NULL == (sorted = malloc(sizeof(first)))) {
return -1;
}
memcpy(sorted, first, sizeof(first));
if (NULL == (indexes = malloc(sizeof(first)))) {
free(sorted);
return -1;
}
memset(indexes, -1, sizeof(first));
if (NULL == (result = malloc(sizeof(second)))) {
free(sorted);
free(indexes);
return -1;
}
memset(result, -1, sizeof(second));
// 1st: Sort the reference array
qsort (sorted, c, sizeof(int), compare);
// 2nd: Record the position of each sorted element in the original array (this is your meta-data)
for (idx=0; idx<c; idx++) {
indexes[idx] = binary_search(sorted, 0, c, first[idx]);
}
// 3rd sort the target array
memcpy(sorted, second, sizeof(second));
qsort (sorted, c, sizeof(int), compare);
// 4th apply the stored positions to the sorted target array
for (idx = 0; idx < c; idx++) {
result[idx] = sorted[indexes[idx]];
}
print_array(result, c);
free(result);
free(indexes);
free(sorted);
return 0;
}
int compare(const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
int binary_search(int array[], int min, int max, int target)
{
int mid;
while (min <= max)
{
mid = min + (max - min)/2;
if (target > array[mid])
min = mid + 1;
else if (target < array[mid])
max = mid - 1;
else
return mid;
}
return -1;
}
void print_array(int * array, int c)
{
for(int i = 0; i < c; i++) {
printf("%d ", array[i]);
}
printf("\n");
}
Demo
Here is my approach, it uses qsort twice and arrC contains the result.
#include <stdio.h>
typedef struct
{
int num;
int pos;
}ArrType;
ArrType arrA[5] = {{1,0},{8,1},{7,2},{2,3},{4,4}};
int arrB[5] = {9,7,2,10,3};;
int arrC[5];
int cmpInt(const void *a, const void *b)
{
return(*a - *b);
}
int cmp(const void *a, const void *b)
{
ArrType *tmpa, *tmpb;
tmpa = (ArrType*) a;
tmpb = (ArrType*) b;
return(tmpa->num - tmpb->num);
}
int main(void)
{
int i;
qsort(arrA,5, sizeof(ArrType), cmp);
qsort(arrB,5, sizeof(ArrType), cmpInt);
for (i=0; i<5; i++)
{
arrC[arrA[i].pos] = arrB[i];
}
return (0);
}
Since C doesn't have a lambda compare (which could be used to sort an array of indexes according to first[]), the code below sorts an array of pointers ap[] to the elements of first[] using qsort(). Using pointers eliminates the need to pass an array name as a parameter for the compare function, which in turn allows the compare function to work with qsort(). The expression (ap[i]-first) converts a pointer into an index. Next second[] is sorted, also using qsort(). Then ap[] is used as a set of ranks to reorder second[] in place and in O(n) time.
To explain reorder by rank versus reorder by index:
dst[rank[i]] = src[i]; /* reorder by rank */
dst[i] = src[index[i]]; /* reorder by index */
Example code:
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
/* compare for ptr to integer */
int cmppi(const void *p0, const void *p1){
return (*(int *)p0 - *(int *)p1);
}
/* compare for ptr to ptr to integer */
int cmpppi(const void *p0, const void *p1){
return (**(int **)p0 - **(int **)p1);
}
int main()
{
int first[] = {1, 8, 7, 2, 4};
int second[] = {9, 7, 2,10, 3};
int **ap; /* array of pointers */
int *tmpp;
int tmpi;
size_t i, j;
/* allocate and generate array of pointers to first[] */
ap = (int **)malloc(sizeof(first)/sizeof(first[0])*sizeof(int *));
for(i = 0; i < sizeof(first)/sizeof(first[0]); i++)
ap[i] = &first[i];
/* sort ap */
qsort(ap, sizeof(first)/sizeof(first[0]), sizeof(int *), cmpppi);
/* sort second */
qsort(second, sizeof(second)/sizeof(second[0]), sizeof(int), cmppi);
/* reorder ap and second in place using ap as rank (O(n) time) */
for (i = 0; i < sizeof(second) / sizeof(second[0]); i++){
while(i != (j = ap[i] - first)){
tmpp = ap[i]; /* swap(ap[i], ap[j]) */
ap[i] = ap[j];
ap[j] = tmpp;
tmpi = second[i]; /* swap(second[i], second[j] */
second[i] = second[j];
second[j] = tmpi;
}
}
/* display second[] */
for (i = 0; i < sizeof(second) / sizeof(second[0]); i++)
printf("%3d", second[i]);
printf("\n");
free(ap);
return 0;
}
I have two arrays side by side, one lists the different teams and the other lists the scores. I am able to sort the order of scores in descending order. Can this order then be used to move the corresponding team to the correct position of the leader board? eg. move the two teams with 100 points (USA and Germany) to the top of the board
#include <stdio.h>
int main()
{
char teams[18][20]={"England","Ireland","Wales","Scotland","France","Italy","Germany","Uraguay","Belgium","USA","Mexico","Australia","Belize","Denmark","Sweden","Japan","South Africa","Algeria"};
int points[18]={43,5,77,23,89,0,100,46,94,100,45,55,32,65,11,37,26,78};
int i;
int j;
int a;
for (i = 0; i < 18; ++i)
{
printf("%i ",i+1);
printf("%s",teams[i]);
printf("\t%d\n", points[i]);
}
printf("\n");
for (i = 0; i < 18; ++i)
{
for (j = i + 1; j < 18; ++j)
{
if (points[i] < points[j])
{
a = points[i];
points[i] = points[j];
points[j] = a;
}
}
}
for (i = 0; i < 18; ++i)
{
printf("%i ",i+1);
printf("%s",teams[i]);
printf("\t%d\n", points[i]);
}
return 0;
}
As mentioned in a comment, the typical solution is to model your data as an array of structures, rather than separate arrays. This makes sense, since the data is associated with each other.
You'd have something like:
struct score {
const char *name;
int points;
} scores[] = {
{ "England", 43 },
{ "Ireland", 5 },
/* and so on */
};
Then you can use qsort() (or your own sorting code, if that's of interest) to sort entire structure instances, and the all the data will remain together since entire structures are being moved around.
Also arrange your teams array when sorting;
a = points[i];
b = teams[i];
points[i] = points[j];
teams[i] = teams[j];
points[j] = a;
teams[j] = b;
The obvious way (as pointed out by others) is embedding your arrays into a struct, but if you are forced to use parallel arrays you can build your own function and sort both arrays at once:
#include <stdio.h>
static int comp(const void *a, const void *b)
{
return *(int *)a - *(int *)b;
}
static void swap(int v1[], char *v2[], int a, int b)
{
int temp1;
char *temp2;
temp1 = v1[a];
v1[a] = v1[b];
v1[b] = temp1;
temp2 = v2[a];
v2[a] = v2[b];
v2[b] = temp2;
}
static void sort(int v1[], char *v2[], int left, int right, int (*comp)(const void *, const void *))
{
int i, last;
if (left >= right) return;
swap(v1, v2, left, (left + right) / 2);
last = left;
for (i = left + 1; i <= right; i++) {
if (comp(&v1[i], &v1[left]) < 0)
swap(v1, v2, ++last, i);
}
swap(v1, v2, left, last);
sort(v1, v2, left, last - 1, comp);
sort(v1, v2, last + 1, right, comp);
}
int main(void)
{
char *teams[] = {"England","Ireland","Wales","Scotland","France","Italy","Germany","Uraguay","Belgium","USA","Mexico","Australia","Belize","Denmark","Sweden","Japan","South Africa","Algeria"};
int points[] = {43,5,77,23,89,0,100,46,94,100,45,55,32,65,11,37,26,78};
size_t i, n = sizeof(points) / sizeof(*points);
sort(points, teams, 0, n - 1, comp);
for (i = 0; i < n; i++) {
printf("%s->%d\n", teams[i], points[i]);
}
return 0;
}
Output:
Italy->0
Ireland->5
Sweden->11
Scotland->23
South Africa->26
Belize->32
Japan->37
England->43
Mexico->45
Uraguay->46
Australia->55
Denmark->65
Wales->77
Algeria->78
France->89
Belgium->94
Germany->100
USA->100
Code:
#include <stdio.h>
void testSort(int values[], int n);
int main(void)
{
int hs[] = {5,3,2,1,4};
printf("Unsorted: %i %i %i %i %i\n", hs[0], hs[1], hs[2], hs[3], hs[4]);
testSort(hs, 5);
printf("Sorted: %i %i %i %i %i\n", hs[0], hs[1], hs[2], hs[3], hs[4]);
}
void testSort(int values[], int n)
{
for (int i = 0; i < n-1; i++)
{
int hold;
int current = values[i];
int next = values[i + 1];
if (current > next)
{
hold = current;
current = next;
next = hold;
}
}
return;
}
I'm trying to do bubble sort and right now it goes through the array once, but my question is: Why isn't my hs[] updating after calling function? The second printf shows that it remained the same.
EDIT:
As mentioned, turns out I was changing data but of the copies. For some reason I when I created the variables current/next I felt as if they were representing values[i]/values[i+1] but in reality I was just creating new variable and passing the value of values[0] which is 5 and assigning it to current. Obviously leaving values[] unchanged. Thanks everyone
The problem is that you're only modifying the function's local variables, not the array's elements.
It's the same principle as why this program will print 1 and not 2:
int main()
{
int array[] = {1};
int x = array[0];
x = 2;
printf("array[0] = %d\n", array[0]);
return 0;
}
You need to assign values to the array's elements:
void testSort(int values[], int n)
{
for (int i = 0; i < n-1; i++)
{
if (values[i] > values[i+1])
{
int hold = values[i];
values[i] = values[i+1];
values[i+1] = hold;
}
}
}
Once you've fixed this, you will notice that this function only works for some inputs.
Solving that bug is left as an exercise.
Please try below code:-
void bubble_sort(int list[], int n){
int c, d, t;
for (c = 0 ; c < ( n - 1 ); c++)
{
for (d = 0 ; d < n - c - 1; d++)
{
if (list[d] > list[d+1])
{
t = list[d];
list[d] = list[d+1];
list[d+1] = t;
}
}
}
}
I am trying to make a qsort type of function that has the same paramenters. I also wrote 3 functions to compare int, float and characters. For some reason it does not work in any case.
I don't know whether this is a problem regarded my qsortx function or not, but I checked it several times and it should work perfectly fine. I am not sure what the problem is, or what I am doing wrong. I am currently learning the function pointers and I might not have got everything right related to it. Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void qsortx(void*, int, int, int (*)(const void*, const void*));
int intcmp();
int floatcmp();
int charcmp();
int main()
{
int i,n;
char items[]={'c', 'a', 'b'};
n = 3;
for (i=0;i<n;++i) {
printf("%c ", items[i]);
}
printf("\n");
qsortx(items, n, sizeof(char), charcmp);
for (i=0;i<n;++i) {
printf("%c ", items[i]);
}
printf("\n");
return 0;
}
void qsortx (void *tp, int length, int pace, int(*fp)(const void* a, const void* b)) {
int switched,i,j;
void *p;
p=(void*)malloc(pace);
switched = 1;
while (1) {
if (switched == 0) {
return;
}
switched = 0;
for (i=0; i<length-1;++i) {
for (j=0;j<length-1;++j) {
printf("%c %c", tp+i, tp+j);
if (fp(tp+i, tp+j) > 0) {
memcpy(p, tp+i, pace);
memcpy(tp+i, tp+j, pace);
memcpy(tp+j, p, pace);
switched++;
}
}
}
}
}
int intcmp(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int floatcmp(const void* a, const void* b) {
return *(float*)a - *(float*)b;
}
int charcmp(const void* a, const void* b) {
return *(char*)a - *(char*)b;
}
You have multiple problems related to pointer arithmetic and element sizes. You also have a logic error in your sort (which I guess you know is a unidirectional shaker sort). Here's a version of the qsortx() function that fixes these deficiencies:
void qsortx (void *tp, int length, int pace, int(*fp)(const void* a, const void* b)) {
if (length > 1) {
char *bound = ((char *) tp) + (length * pace);
char *p = malloc(pace);
char *item1p;
for (item1p = tp; item1p < (bound - pace); item1p += pace) {
char *item2p;
for (item2p = item1p + pace; item2p < bound; item2p += pace) {
if (fp(item1p, item2p) > 0) {
memcpy(p, item1p, pace);
memcpy(item1p, item2p, pace);
memcpy(item2p, p, pace);
}
}
}
free(p);
}
}
Note that:
All pointer arithmetic is performed on values of type char *.
The element size (pace) must be taken into account as you step through the input array, else you just scramble your data.
The innermost loop should start at the element after the one being considered in the next-outer loop.
switched = 1 is a better choice than switched ++ because it cannot overflow, and all you care about is zero vs. non-zero. (Update: but switched is no longer relevant.)
(Update) It is incorrect to exit early in the event that a pass through the item1p loop results in zero swaps. Just because one element is already in its correct place does not mean that all the subsequent elements are also in their correct places. I updated my code above to remove that behavior.
(Update) As chux observed, the temporary space reserved for swapping elements was not freed. I added an appropriate free(p).
(Update) I also made sorting conditional on the array length being greater than 1, which avoids undefined behavior associated with bound - pace in the event that the length is zero.
here is the pseudo code and implementation of the quicksort (qsort) algorithm, with some accessory code, as defined in the http://www.codingbot.net/2013/01/quick-sort-algorithm-and-c-code.html web page:
Note that this algorithm is slightly different from qsort()
in that there is a different parameter list and certain other details.
However, the basic algorithm is the same.
function quicksort('array')
if length('array') β€ 1
return 'array' // an array of zero or one elements is already sorted
select and remove a pivot value 'pivot' from 'array'
create empty lists 'less' and 'greater'
for each 'x' in 'array'
if 'x' β€ 'pivot'
then append 'x' to 'less'
else
append 'x' to 'greater'
endif
end for
return concatenate(quicksort('less'), 'pivot', quicksort('greater') );
notice that qsort is a partition sort, using recursion.
#include<stdio.h>
#include<conio.h>
void quick_sort(int arr[20],int,int);
int main()
{
int arr[20],n,i;
clrscr();
printf("Enter the number of elements in the Array: ");
if( 1 != scanf(" %d",&n) )
{
perror( "scanf for count of elements" );
exit(1);
}
printf("\nEnter %d elements:\n\n",n);
for(i=0 ; i<n ; i++)
{
printf(" Array[%d] = ",i);
if( 1 != scanf(" %d",&arr[i]) )
{
perror( "scanf for element values" );
exit(2);
}
}
quick_sort(arr,0,n-1);
printf("\nThe Sorted Array is:\n\n");
for(i=0 ; i<n ; i++)
{
printf(" %4d",arr[i]);
}
getch();
}
void quick_sort(int arr[20],int low,int high)
{
int pivot; // used in partitioning the array
int j; // loop index
int temp; // for swapping
int i; // loop index
if(low<high)
{
pivot = low;
i = low;
j = high;
while(i<j)
{
// find next item not in proper sequence
while((arr[i] <= arr[pivot]) && (i<high))
{
i++;
}
// find next item not in proper sequence
while(arr[j] > arr[pivot])
{
j--;
}
// following is where a callback function would be invoked
if(i<j)
{
temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
temp=arr[pivot];
arr[pivot] = arr[j];
arr[j]=temp;
// following is where recursion is used to perform sort on sub partitions
quick_sort(arr,low,j-1);
quick_sort(arr,j+1,high);
}
}
this is a much better algorithm for your purposes.
however, it only handles integers, so you would need to
add the comparison function as a 4th parameter to quicksort()
and modify the code to use your comparison function
#include <stdio.h>
#include <stdlib.h>
void swap(int *x,int *y);
int choose_pivot(int i,int j );
void quicksort(int list[],int m,int n);
void display(int list[],const int n);
int main()
{
const int SIZE = 10;
int list[SIZE];
int i = 0;
/* generates random numbers and fill the list */
for(i = 0; i < SIZE; i++ )
{
list[i] = rand();
}
printf("The list before sorting is:\n");
display(list,SIZE);
/* sort the list using quicksort algorithm */
quicksort(list,0,SIZE-1);
printf("The list after sorting:\n");
display(list,SIZE);
}
void swap(int *x,int *y)
{
// for integer swaps, 3 exclusive OR operations would be much faster
// and not require a temp variable
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int choose_pivot(int i,int j )
{
return((i+j) /2);
}
void quicksort(int list[],int m,int n)
{
int key,i,j,k;
if( m < n)
{
k = choose_pivot(m,n);
swap(&list[m],&list[k]);
key = list[m];
i = m+1;
j = n;
while(i <= j)
{
while((i <= n) && (list[i] <= key))
{
i++;
}
while((j >= m) && (list[j] > key))
{
j--;
}
if( i < j)
{
swap(&list[i],&list[j]);
}
}
/* swap two elements */
swap(&list[m],&list[j]);
/* recursively sort the lesser list */
quicksort(list,m,j-1);
quicksort(list,j+1,n);
}
}
void display(int list[],const int n)
{
int i;
for(i=0; i<n; i++)
{
printf("%d\t",list[i]);
}
}