Binary search implemention ( mostly code structure problems, aka newbie problems) - c

I have just learned my very first steps in C (I used to code in python) and as a such I'm facing great difficulty with the syntax, and as well as with binary search implementation. (I'm new to programming.)
So here's my code:
#include <stdlib.h>
#include <stdio.h>
#define size 1000
int binary_search(int array[size], int givenNumber) {
do {
int start = 0;
int end = size;
int middle = start + end / 2;
int left = size - 1;
int right = size + 1;
if (array[middle] == givenNumber) {
printf("location: %d, number: %i", middle, givenNumber);
return middle;
} else if (array[left] < givenNumber) {
int start = 0;
int end = array[left];
int middle = start + end / 2;
return middle;
} else if (array[right] > middle) {
int start = array[right];
int end = size;
int middle = start + end / 2;
return middle;
} else {
return -1;
break;
}
}
} while (int middle != givenNumber)
int main() {
int sorted_array[size];
for (int i = 0; i < size; i++) {
sorted_array[i] = i;
}
return binary_search(sorted_array, 349);
}
My problems are:
1 - when compiling, the error is something along the lines of "in while block "middle" isn't defined"
I have no clue on why the value isn't passing from the do block to the while block. Note that I added "return middle" to each if / else-if block, as I I thought it may help.
2- I'm not even quite sure if my own implementation of this binary search is correct. I have looked up how to implement it but I found it next to impossible to read the syntax, this is just to give a heads up.
UPDATE:
I have reconstructed the whole code according to the notes users have given in the answers below, and well, my algorithm is working and now it could find any number in any given array, however I'm unable to figure out a way to tell if the array doesn't have that givenNumber as it would get eventually stuck.
Here's an input/output:
int array[size] = {1,2,3,4,5,6,8,9,10,11,14,24,53,100};
function: binary_search(array, 24);
output: Location: 11, Number: 24
Which is pretty good, however if I enter a number which doesn't exist in the array, the loop continues to search endlessly (gets stuck in recursion)
here's the updated code:
#include <stdio.h>
#include <stdlib.h>
#define size 14
int start = 0;
int end = size -1;
int middle;
int left;
int right;
int binary_search(int array[size], int givenValue)
{
middle = (start + end) / 2;
printf("\nstart: %d \nend: %d \nmiddle: %d \n\n",start, end, middle);
do
{
if (start > end)
{
printf("item isn't found");
break;
return -1;
}
middle = (start+end)/2;
left = middle -1;
right = middle +1;
if (array[middle] == givenValue)
{
printf("Location: %d, Number: %d", middle, givenValue);
return middle;
break;
}
if(array[middle] > givenValue)
{
end = right;
return binary_search(array, givenValue);
}
if(array[middle] < givenValue)
{
start = left;
return binary_search(array, givenValue);
}
}
while (start <= end);
}
int main(void)
{
int array[size] = {1,2,3,4,5,6,8,9,10,11,14,24,53,100};
return binary_search(array, 24);
}
My condition is that if start is greater than end then the item doesn't exist, and it doesn't work at all because left/right keeps getting stuck at the same values (Run the code to know what I mean)
How do I fix this issue?

There are a number of issues:
The loop does not iterate. All parts of if/else have a return
massive amount of "shadowing" of variables
size is invariant so left/right are always set to the same thing
left and right should be started at 0 and size - 1 respectively
variables are used inconsistently (e.g. start is both an array index and an array value)
The function is more complex than it needs to be and has some extraneous variables
Your binary search algorithm is suspect
First and foremost, I'd recommend removing all "sub-scoped" variable declarations that shadow outer scoped ones (i.e. put all variables at the outermost scope).
Do this until you're more comfortable with these declarations. Learn more about the difference between:
variable declaration: int x;
variable declaration with initializer: int x = 5;
variable assignment: x = 5;
I've annotated your original function, created a test/diagnostic function and created a refactored function that passes the diagnostic test:
#include <stdio.h>
#define size 1000
int
binary_fixed(int *array, int givenNumber)
{
int left;
int right;
int middle;
int curval;
int retindex = -1;
left = 0;
right = size - 1;
while (left <= right) {
middle = (left + right) / 2;
curval = array[middle];
if (curval == givenNumber) {
retindex = middle;
break;
}
if (curval > givenNumber)
right = middle - 1;
else
left = middle + 1;
}
return retindex;
}
int
binary_search(int *array, int givenNumber)
{
int middle;
// NOTE/BUG: this does _not_ iterate
// NOTE/BUG: _massive_ amount of "shadowing" of variables
// NOTE/BUG: size is _invariant_ so left/right are _always_ set to the
// same thing
// NOTE/BUG: left and right should start at 0 and size - 1 respectively
// NOTE/BUG: variables are used _inconsistently_ (e.g. start is both
// an array index and an array _value_)
do {
int start = 0;
int end = size;
int middle = start + end / 2;
int left = size - 1;
int right = size + 1;
if (array[middle] == givenNumber) {
printf("location: %d, number: %i\n", middle, givenNumber);
return middle;
}
else if (array[left] < givenNumber) {
int start = 0;
int end = array[left];
int middle = start + end / 2;
return middle;
}
else if (array[right] > middle) {
int start = array[right];
int end = size;
int middle = start + end / 2;
return middle;
}
else {
return -1;
}
} while (middle != givenNumber);
printf("BADRETURN givenNumber=%d\n", givenNumber);
}
int sorted_array[size];
void
test(const char *who,int (*fnc)(int *,int))
{
int i;
int r;
for (i = 0; i < size; i++) {
r = fnc(sorted_array, i);
if (r != i) {
printf("ERROR -- EXPECTED: %d ACTUAL: %d (from %s)\n", i, r, who);
// break;
}
}
}
int
main()
{
for (int i = 0; i < size; i++) {
sorted_array[i] = i;
}
//test("ORIGINAL",binary_search);
test("FIXED",binary_fixed);
return 0;
}
UPDATE:
Since you're coming from python, here are a few points that may help with your understanding.
Arrays are passed to functions as pointers, so in the function argument list, int *array is equivalent. Doing int array[size] is an advanced technique. Avoid it for now. In C, you can't do array.count as you can in python. So, for now, pass the count as an additional argument.
Pointers are something that python doesn't have, so you'll have to learn about how to use them. Once you get the hang of them, they are quite powerful and can make code run quite fast.
In C [and most languages except python [and php]], the default scope is global. It's the reverse of python. In python, unless you specify global x, then x is private to the function. In other languages, to have x be local to the function, you have to declare it at function scope.
In C, all variables must be declared somewhere: global scope, function scope [or a block scope within a function], or as an argument. And, each declaration must specify an explicit type. There is no equivalent of javascript's declaration of var x. After that, x could be either a string or a value depending upon what you set it to: (e.g. x = 23 or x = "abc")
In C, x must be given a type such as: int x; or double x; or int *x; or char *x.
Here is your revised code with annotations:
#include <stdio.h>
#include <stdlib.h>
// NOTE: this is hardwired
#define size 14
// NOTE: this is _global_ scope -- while not absolutely wrong, using function
// scope below is faster/better and provides better isolation -- this doesn't
// scale as well
int start = 0;
int end = size - 1;
int middle;
int left;
int right;
// NOTE: in C, using "int *array" is equivalent [and in many cases preferred]
// NOTE: the array count should be passed in as a separate argument
int
binary_search(int array[size], int givenValue)
{
// NOTE: this is _function_ scope
// NOTE: this calc of middle is extraneous because it is recalculated
// below
middle = (start + end) / 2;
printf("\nstart: %d \nend: %d \nmiddle: %d \n\n", start, end, middle);
// NOTE/BUG: this function combines _both_ a loop implementation and a
// recursive implementation -- we have to pick one or the other as trying
// to do both messes things us -- the recursion is broken [see below]
// NOTE: this loop checks start vs end _twice_ in the loop -- it only
// needs to check in one place -- convert this to "while (start <= end) {"
// instead of a "do {...} while (whatever);" loop [which is usually not
// as good]
do {
if (start > end) {
printf("item isn't found");
break;
// NOTE/BUG: this return will _never_ be executed because we
// break out of the loop and what is worse we'll return an
// unknown/undefined value because
return -1;
}
middle = (start + end) / 2;
// NOTE/BUG: these are reversed
left = middle - 1;
right = middle + 1;
if (array[middle] == givenValue) {
printf("Location: %d, Number: %d", middle, givenValue);
return middle;
break;
}
// NOTE/BUG: these recursive calls do nothing because they do _not_
// further limit the scope and cause infinite recursion -- to make
// a recursive version work, start/end would need to be arguments:
// return binary_search(array,givenValue,start,end)
// and we shouldn't loop -- the recursive calls just aren't needed
// NOTE/BUG: the tests here are broken -- they are the reverse of
// the correct ones
if (array[middle] > givenValue) {
end = right;
return binary_search(array, givenValue);
}
if (array[middle] < givenValue) {
start = left;
return binary_search(array, givenValue);
}
// NOTE/BUG: this extra test is extraneous and would be done too late
// if the array size was zero -- a case that isn't handled here
} while (start <= end);
// NOTE/BUG: when we break out of the loop, we need to return _some_ value
// here -- this would be flagged by the compiler using the -Wall option
}
int
main(void)
{
int array[size] = { 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 24, 53, 100 };
// NOTE/BUG: the return value from main can only handle numbers 0-255
// better to print the return value
return binary_search(array, 24);
}
Here is a working version of your revised code.
The loop is now a simple while. The function takes a separate count argument. It loops instead of recurses. The reversed if logic has been fixed. Again, a diagnostic test has been added.
#include <stdio.h>
#include <stdlib.h>
int
binary_search(int *array, int size, int givenValue)
{
// NOTE: this is _function_ scope
int start = 0;
int end = size - 1;
int middle;
int left;
int right;
int match_index;
// assume failure
match_index = -1;
// NOTE: this calc of middle is extraneous because it is recalculated
// below
middle = (start + end) / 2;
printf("\nstart: %d \nend: %d \nmiddle: %d \n\n", start, end, middle);
while (start <= end) {
middle = (start + end) / 2;
left = middle - 1;
right = middle + 1;
if (array[middle] == givenValue) {
printf("Location: %d, Number: %d\n", middle, givenValue);
match_index = middle;
break;
}
if (array[middle] > givenValue) {
end = left;
}
if (array[middle] < givenValue) {
start = right;
}
}
if (match_index < 0)
printf("match not found -- givenValue=%d\n",givenValue);
return match_index;
}
int
main(void)
{
int array[] = { 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 24, 53, 100 };
int count = sizeof(array) / sizeof(array[0]);
int curidx;
int valwant;
int match;
printf("%d\n",binary_search(array, count, 24));
// run diagnostic on all values
for (curidx = 0; curidx < count; ++curidx) {
// get value to search for
valwant = array[curidx];
match = binary_search(array,count,valwant);
if (match != curidx) {
printf("fault: curidx=%d valwant=%d match=%d\n",
curidx,valwant,match);
}
}
// test lower range failure
valwant = array[0] - 1;
match = binary_search(array,count,valwant);
if (match >= 0)
printf("fault: valwant=%d match=%d\n",valwant,match);
// test upper range failure
valwant = array[count - 1] + 1;
match = binary_search(array,count,valwant);
if (match >= 0)
printf("fault: valwant=%d match=%d\n",valwant,match);
return 0;
}

A typical binary search implementation would either loop or recurse until the number is found. The naive recursive code is something like this:
#include <stdio.h>
#define size 1000
int binary_search(int array[size], int givenNumber, int start, int end) {
int middle;
middle = (start + end) / 2;
if (start > end)
return -1;
if (array[middle] == givenNumber) {
printf("location: %d, number: %i", middle, givenNumber);
return middle;
} else if (array[middle] < givenNumber) {
return binary_search(array, givenNumber, middle + 1, end);
} else { // if (array[middle] > givenNumber)
return binary_search(array, givenNumber, start, middle - 1);
}
}
int main() {
int sorted_array[size];
for (int i = 0; i < size; i++) {
sorted_array[i] = i * 2;
}
if (binary_search(sorted_array, 349, 0, size - 1) < 0) {
printf("value not found\n");
}
if (binary_search(sorted_array, 34, 0, size - 1) < 0) {
printf("value not found\n");
}
}
Notice in the recursion we call binary_search each time with a new start and end range based on the sorted array and the provided input in givenNumber.

You can't declare a variable inside a while statement. Move it to the top of your function.
You have a misplaced curly brace in your code.
Try this:
#include <stdlib.h>
#include <stdio.h>
#define size 1000
int binary_search(int array[size], int givenNumber) {
int middle;
do {
int start = 0;
int end = size;
int middle = start + end / 2;
int left = size - 1;
int right = size + 1;
if (array[middle] == givenNumber) {
printf("location: %d, number: %i", middle, givenNumber);
return middle;
} else if (array[left] < givenNumber) {
int start = 0;
int end = array[left];
int middle = start + end / 2;
return middle;
} else if (array[right] > middle) {
int start = array[right];
int end = size;
int middle = start + end / 2;
return middle;
} else {
return -1;
}
} while (middle != givenNumber);
}
int main() {
int sorted_array[size];
for (int i = 0; i < size; i++) {
sorted_array[i] = i;
}
return binary_search(sorted_array, 349);
}

Related

How can this non-recursive quicksort be optimized to reduce stack usage(c language)

This is a non-recursive program to realize quicksort, and I use an array to simulate the implementation of the stack, but every time I sort a part of the array, the footprint increases exponentially. How to optimize this program to make the usage smaller? Like reusing the space that was previously discarded (where X has been traversed) or whatever, I really want to figure out, thank you in advance.
#include <stdio.h>
#include <stdlib.h>
int stack[1000];
int partion(int a[],int begin,int end)
{
int l= begin;
int r= end;
int pole = a[begin];
while(l<r)
{
while(l<r && a[r]>=pole)
r--;
if(l<r)
{
a[l] = a[r];
l++;
}
while(l<r && a[l]<= pole)
l++;
if(l<r)
{
a[r] = a[l];
r--;
}
}
a[r]= pole;
return r;
}
void nonRecursiveQuickSort(int a[], int begin, int end);
int main()
{
int i;
int a[10]={0,1,-3,4,2,6,-9,0,8,10};
nonRecursiveQuickSort(a, 0, 10);
for(i=0;i<10;i++)
{
printf("%d ",a[i]);
}
}
void nonRecursiveQuickSort(int a[], int begin, int end)
{
int l = begin;
int r = end;
//replace the stack with an array, which stores the split boundaries
int x = 0;
int y = 0;
//add left boundary
stack[y++] = l;
//add right boundary
stack[y++] = r;
// If the left boundary is smaller than the right boundary, the data to be processed is indicated
while (x < y)
{
// Simulate stack popout left boundary
int left = stack[x++];
//Simulate stack popout right boundary
int right = stack[x++];
// Call partition function
int pos = partion(a, left, right);
//if Meet the conditions, repress the stack
if (left < pos - 1)
{
stack[y++] = left;
stack[y++] = pos - 1;
}
if (pos + 1 < right)
{
stack[y++] = pos + 1;
stack[y++] = right;
}
}
}
The call nonRecursiveQuickSort(a, 0, 10) is wrong, since this causes a[10] to be referenced in partion; correct is nonRecursiveQuickSort(a, 0, 9).
reusing the space that was previously discarded (where X has been traversed)
To do that, the indexes x and y into the FIFO named stack are to wrap around at the end of the array. This amended version of your loop in nonRecursiveQuickSort does it, also with checks to prevent overrun:
#define DIM sizeof stack / sizeof *stack // DIM must be even
while (x != y)
{
// Simulate stack popout left boundary
int left = stack[x++];
//Simulate stack popout right boundary
int right = stack[x++];
if (x == DIM) x = 0; // wrap around
// Call partition function
int pos = partion(a, left, right);
//if Meet the conditions, repress the stack
if (left < pos - 1)
{
stack[y++] = left;
stack[y++] = pos - 1;
if (y == DIM) y = 0; // wrap around
if (y == x) puts("FIFO full"), exit(1);
}
if (pos + 1 < right)
{
stack[y++] = pos + 1;
stack[y++] = right;
if (y == DIM) y = 0; // wrap around
if (y == x) puts("FIFO full"), exit(1);
}
}

Counting swaps in Quicksort in C

I am trying to count the number of swaps that occur in my quicksort in C. However, I am getting values that are incorrect and not sure where I went wrong. I am using a structures as my arrays to be sorted.
struct anArray{
int numbers[maxSize];
int swaps;
};
/* Partition function */
int partition(struct anArray *array, int start, int end){
if(start == end){
return start;
}
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for(;;){
do{
low++;
} while(array->numbers[low] < pivot);
do{
high--;
} while(array->numbers[high] > pivot);
/* Detector for when the cells meet */
if(low >= high){
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
This is my partition function used to "separate" the arrays.
void quickSort(struct anArray *array, int start, int end){
if(end - start <= 0){ return; }
else{
int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
}
This is my quicksorting function. It's a recursive function.
My swap function increments counter by 1 every time it's called.
In my main, I set
myArray->swaps = counter;
But the number of times the swaps occurs isn't right. For example, if I sort an array that goes from 1 to 9, the number of swaps should be 0 but I get 9. I've tried incrementing counter when it's in the partition function only but it gives me the same result.
Is there something wrong with my partition function?
Thank you very much
Edit 1:
Here's my swap function.
void swap(struct anArray *array, int first, int second){
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
counter++;
}
I've tried using
void swap(struct anArray *array, int first, int second, int swapCount)
and then have swapCount be array->swaps when calling the swap function, and incrementing it by 1 but it gives me the same answer.
Here's a part of my main.
int main(){
struct anArray *ascending = (struct anArray*)malloc(10 * sizeof(struct anArray));
int ascend[maxSize] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
initArray(ascending, ascend);
quickSort(ascending, 0, maxSize - 1);
ascending->swaps = counter;
printf("Test: Unique random values\nSorted: [ ");
for(int i = 0; i < maxSize; i++){
printf("%i ", ascending->numbers[i]);
}
printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);
The other parts of my main are just other arrays to be sorted. The initArray is used to set the values of array->numbers and also reset array->swaps to 0.
Your quicksort code seems pretty good. I didn't examine it rigorously, but it passed a simple test, so I didn't investigate further. (Edit: Based on your feedback, I created a third version in my second update that shows that the sort has an issue for larger data inputs).
The main bug was the malloc at the top of main. We do not want an array of the struct anArray:
struct anArray *ascending = malloc(10 * sizeof(struct anArray));
That is, we do not want (e.g.) 10 structs, we want a single struct and to fill in 10 ints that go into the numbers field that is in that single struct.
The initArray function was not posted, so I had to guess/deduce what it might be. Based on the above bug, I'm not sure that numbers would have been initialized correctly.
From the code fragments posted, I was able to piece together a whole program. I've created two versions:
One with the bugs annotated [but not fixed] that compiles cleanly.
And, a second that is fully cleaned up, working, and generalized for arbitrary array sizes [please pardon the gratuitous style cleanup]
Here is [something close to] your original code with the bugs annotated:
#include <stdio.h>
#include <stdlib.h>
// NOTE/BUG: this was not defined and _fixed_ defines should be all caps
#define maxSize 10
struct anArray {
int numbers[maxSize];
int swaps;
};
int counter;
void
initArray(struct anArray *array,const int *src)
{
for (int idx = 0; idx < maxSize; ++idx)
array->numbers[idx] = src[idx];
array->swaps = 0;
}
void
swap(struct anArray *array, int first, int second)
{
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
counter++;
}
/* Partition function */
int
partition(struct anArray *array, int start, int end)
{
if (start == end) {
return start;
}
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for (;;) {
do {
low++;
} while (array->numbers[low] < pivot);
do {
high--;
} while (array->numbers[high] > pivot);
/* Detector for when the cells meet */
if (low >= high) {
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
void
quickSort(struct anArray *array, int start, int end)
{
if (end - start <= 0) {
return;
}
else {
// NOTE/BUG: pivot is _not_ used
int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
}
int
main(void)
{
// NOTE/BUG: we do _not_ want an array of the struct, but an array of int
// that is allocated for "number" _inside_ the struct
struct anArray *ascending = malloc(10 * sizeof(struct anArray));
int ascend[maxSize] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// NOTE/BUG: this was not defined
initArray(ascending, ascend);
quickSort(ascending, 0, maxSize - 1);
ascending->swaps = counter;
printf("Test: Unique random values\nSorted: [ ");
for (int i = 0; i < maxSize; i++) {
printf("%i ", ascending->numbers[i]);
}
printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);
return 0;
}
Here is a cleaned up and working version. I've generalized it so it can take an arbitrarily long array. I've also done a bit of style and code cleanup:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *numbers;
int size;
int swaps;
} Array;
Array *
initArray(const int *src,int size)
{
Array *array = malloc(sizeof(Array));
array->numbers = malloc(size * sizeof(int));
array->size = size;
// store in reverse order so the sort will actually do something
for (int idx = 0; idx < size; ++idx)
array->numbers[size - 1 - idx] = src[idx];
array->swaps = 0;
return array;
}
void
freeArray(Array *array)
{
free(array->numbers);
free(array);
}
void
swap(Array *array, int first, int second)
{
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
array->swaps += 1;
}
/* Partition function */
int
partition(Array *array, int start, int end)
{
if (start == end)
return start;
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for (;;) {
do {
low++;
} while (array->numbers[low] < pivot);
do {
high--;
} while (array->numbers[high] > pivot);
/* Detector for when the cells meet */
if (low >= high) {
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
void
quickSort(Array *array, int start, int end)
{
if (end - start <= 0)
return;
//int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
int
main(void)
{
// NOTE/BUG: we do _not_ want an array of the struct, but an array of int
// that is allocated for "number" _inside_ the struct
int original[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int size = sizeof(original) / sizeof(original[0]);
Array *ascending = initArray(original, size);
quickSort(ascending, 0, ascending->size - 1);
printf("Test: Unique random values\nSorted: [ ");
for (int i = 0; i < ascending->size; i++) {
int expected = original[i];
int actual = ascending->numbers[i];
printf("%d%s ", actual, (actual == expected) ? "" : "???");
}
printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);
freeArray(ascending);
return 0;
}
UPDATE:
What does the line int size = sizeof(original) / sizeof(original[0]); do exactly?
Does it give me an integer for size which I set to be the size of how many numbers I can hold in an array?
Yes, that is common/idiomatic trick to get the count of the number of elements of a fixed size array:
int array[] = { 1, 2, 3 };
size_t count = sizeof(array) / sizeof(array[0]);
Here, sizeof(array) is 3 times the size [in bytes] of the individual elements [which are int, which is 4 bytes], so we have 3 * 4 or 12.
sizeof(array[0]) is the size of the single, first element of the array, which is [again] an int, so this is 4.
So, when we divide the two, we have 12 / 4 or 3, which is the number of elements.
If so, wouldn't the amount of numbers I can hold be really small if sizeof(original[0]) happens to be very large?
No, because of the division. It doesn't care how large the element size [in bytes] is, because the ratio always produces the number of elements.
The sizeof(arr) / sizeof(arr[0]) trick is useful to get the count when we do: int arr[] = { ... };
If we do:
#define ARRCOUNT 3
int arr[ARRCOUNT] = { 1, 2, 3 };
We already know the count (i.e. it is ARRCOUNT).
The [slight] advantage to the sizeof/sizeof trick is that if we had incorrectly defined ARRCOUNT as 4 by mistake, it would still compile, link, and run, but would produce incorrect results [because there were only 3 elements].
This is a common enough trick that we can define a generic macro [that we can reuse by putting it a .h file]:
#define ARRAY_COUNT(arr_) (sizeof(arr_) / sizeof(arr_))
UPDATE #2:
I've tried your code (even tried copying and pasting it) but my swaps is still showing 9 despite my array to be sorted is just going from { 1 to 10}. Not sure why this keeps occurring.
I believe [now] you have a bug in the sort itself.
I've produced another version that has much more extensive test data generation and comparison.
At a minimum, because of the way the tests are structured, the first element of the sorted array should always have a value of 1.
The test that fails is the one that does a random shuffle of the original array before sending it in to be sorted.
You can add other tests as needed. The array needn't be so large to show the problem. For example, the following single test is enough to produce the error:
bigtest(100,237,1);
Anyway, here is the enhanced diagnostic code:
#include <stdio.h>
#include <stdlib.h>
#define MAXLEN 60
typedef struct {
int *numbers;
int size;
int swaps;
} Array;
Array *
initArray(const int *src,int size,int randshuf)
{
int idx;
Array *array = malloc(sizeof(Array));
array->numbers = malloc(size * sizeof(int));
array->size = size;
array->swaps = 0;
// store in reverse order so the sort will actually do something
switch (randshuf) {
case 0: // reverse the numbers
for (idx = 0; idx < size; ++idx)
array->numbers[size - 1 - idx] = src[idx];
break;
default: // do _crude_ random shuffle
for (idx = 0; idx < size; ++idx)
array->numbers[idx] = 0;
for (idx = 0; idx < size; ++idx) {
while (1) {
int ridx = rand() % size;
if (array->numbers[ridx] == 0) {
array->numbers[ridx] = src[idx];
break;
}
}
}
break;
}
return array;
}
void
freeArray(Array *array)
{
free(array->numbers);
free(array);
}
void
swap(Array *array, int first, int second)
{
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
array->swaps += 1;
}
/* Partition function */
int
partition(Array *array, int start, int end)
{
if (start == end)
return start;
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for (;;) {
do {
low++;
} while (array->numbers[low] < pivot);
do {
high--;
} while (array->numbers[high] > pivot);
/* Detector for when the cells meet */
if (low >= high) {
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
void
quickSort(Array *array, int start, int end)
{
if (end - start <= 0)
return;
//int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
void
print_orig(const int *orig,int count)
{
int len = 0;
printf("Test: Original numbers (%d):\n",count);
for (int idx = 0; idx < count; ++idx) {
len += printf(" %10d ", orig[idx]);
if (len >= MAXLEN) {
printf("\n");
len = 0;
}
}
if (len > 0)
printf("\n");
}
int
print_array(Array *array,const int *orig,const char *reason)
{
int len = 0;
int cmp;
int err = -1;
printf("Test: Array Values (%s):\n",reason);
for (int idx = 0; idx < array->size; ++idx) {
int actual = array->numbers[idx];
if (orig != NULL) {
int expected = orig[idx];
cmp = (actual == expected);
}
else
cmp = 1;
len += printf(" %10d%c", actual, cmp ? ' ' : '?');
if (len >= MAXLEN) {
printf("\n");
len = 0;
}
if (cmp)
continue;
if (err < 0)
err = idx;
}
if (orig != NULL)
printf("\nSwaps: %i\nComps: \n\n", array->swaps);
else {
if (len > 0)
printf("\n");
}
return err;
}
void
bigtest(int count,int randgap,int randshuf)
// count -- number of elements (negative means random)
// randgap -- gap between element values (negative means random)
// randshuf -- 0=simple reverse, 1=random shuffle
{
int *orig;
Array *array;
printf("\n");
for (int idx = 1; idx <= 80; ++idx)
printf("-");
printf("\n");
printf("COUNT: %d, RANDGAP: %d, RANDSHUF: %d\n",count,randgap,randshuf);
// get number of elements
if (count < 0)
count = (rand() % count) + 1;
// get element gap (e.g. 1 --> {1, 2, 3}, 2 --> { 1, 3, 5 }
if (randgap < 0)
randgap = (rand() % randgap) + 1;
printf("COUNT: %d, RANDGAP: %d, RANDSHUF: %d\n",count,randgap,randshuf);
// get original array
orig = malloc(sizeof(int) * count);
// fill in original array
do {
int val = 1;
// simple gap
if (randgap >= 0) {
if (randgap == 0)
randgap = 1;
for (int idx = 0; idx < count; ++idx, val += randgap)
orig[idx] = val;
break;
}
// random gap
int gap;
for (int idx = 0; idx < count; ++idx, val += gap) {
orig[idx] = val;
gap = (rand() % randgap) + 1;
}
} while (0);
print_orig(orig,count);
array = initArray(orig,count,randshuf);
print_array(array,NULL,"Shuffled");
quickSort(array, 0, array->size - 1);
print_array(array,orig,"Sorted");
freeArray(array);
free(orig);
}
int
main(void)
{
bigtest(10,0,0);
bigtest(-100,23,0);
bigtest(-1000,-2337,0);
bigtest(-1000,-2337,1);
return 0;
}

UVA problems 12503 gives TLE

I am trying to solve 12503 problem on UVA online judge. I think I have figured out the solution, but it gives me TLE. Here is the problem :
You have a robot standing on the origin of x axis. The robot will be given some instructions.
Your task is to predict its position after executing all the instructions.
• LEFT: move one unit left (decrease p by 1, where p is the position of the robot before moving)
• RIGHT: move one unit right (increase p by 1)
• SAME AS i: perform the same action as in the i-th instruction. It is guaranteed that i is a positive
Input
integer not greater than the number of instructions before this.
The first line contains the number of test cases T (T <= 100). Each test case begins with an integer n (1 <= n <= 100), the number of instructions. Each of the following n lines contains an instruction.
Output
For each test case, print the final position of the robot. Note that after processing each test case, the
robot should be reset to the origin.
Sample Input
2
3
LEFT
RIGHT
SAME AS 2
5
LEFT
SAME AS 1
SAME AS 2
SAME AS 1
SAME AS 4
Sample Output
1
-5
Here is my code in C:
#include <stdio.h>
char com[102][20];
int command(char comd[], int pos);
int main() {
int t;
int pos;
int i, n;
char tmp[20];
scanf("%d", &t);
for(i = 0; i < t; i++) {
scanf("%d", &n);
int j;
pos = 0;
for (j = 0; j < n; j++) {
gets(com[j]);
if (strcmp(com[j], "LEFT") == 0)
pos--;
else if(strcmp(com[j], "RIGHT") == 0)
pos++;
else {
pos = command(com[j], pos);
}
}
printf("%d\n", pos);
}
return 0;
}
int command(char comd[], int pos) {
if (strcmp(comd, "LEFT") == 0) {
pos--;
return pos;
}
else if (strcmp(comd, "RIGHT") == 0) {
pos++;
return pos;
}
else{
int a = atoi(&comd[8]);
return command(com[a-1], pos);
}
}
Is there any suggestion why this code gives TLE ?
In the int command(char comd[], int pos) function, you are using recursive call at the last line. This may lead to TLE.
To solve this problem you can use another array to store the number of steps taken by the robot at a command. You just have to access the index of the array later to get step at a previous command.
Here is how I would do it-
#include <stdio.h>
#include <string.h>
int move[110];
int getMove(char inp[], int);
int main()
{
int t, n;
char inp[50];
scanf(" %d ",&t);
while(t--)
{
scanf(" %d ",&n);
int i, pos = 0;
for(i = 0; i < n; i++)
{
gets(inp);
pos += getMove(inp, i);
}
printf("%d\n",pos);
}
return 0;
}
int getMove(char inp[], int i)
{
if(inp[0]=='S')
{
int j;
sscanf(strrchr(inp,' ')," %d",&j);
move[i] = move[j - 1];
}
else
{
move[i] = (inp[0] == 'L') ? -1 : 1;
}
return move[i];
}

Shorter way to get 5 highest and 5 lowest values without changing the stack

So here is my code . I am trying to find a short way to make this programme work withouth changing any of the arregment.I have been tought the buble way i think its called to arrange a group from highest to lowest but it clearly say in my given orders not to change the entire group.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int randomInRange (unsigned int min, unsigned int max)
{
//srand(time(NULL));
int base_random = rand();
if (RAND_MAX == base_random) return randomInRange(min, max);
int range = max + 1 - min,
remainder = RAND_MAX % range,
bucket = RAND_MAX / range;
if (base_random < RAND_MAX - remainder) {
return min + base_random/bucket;
} else {
return randomInRange (min, max);
}
}
int main()
{
int ari,i,min,max;
printf("Gi'me length of the group")
scanf("%d",&ari);
int pinakas[ari];
printf("Gi'me lowest and highest values");
scanf("%d",&min);
scanf("%d",&max);
for(i = 0; i < ari; i++)
{
pinakas[ari] = randomInRange(min,max);
}
int el,meg,c;
el = max+1;
meg = min-1;
c = 0;
printf("Highest Lowest");
while( c != 4;)
{
for(i = 0; i < ari; i++)
{
if(el > pinakas[ari])
{
el = pinakas[ari];
}
if( meg < pinakas[ari])
{
meg = pinakas[ari];
}
if(i == 4)
{
printf("%d %d",el,meg);
( is there something that i can put here is order to make el,meg to go for the second lowest ,second highest? and so on till i get the 5 highest and 5 lowests.Keep in mind the the lowest length of my group will be pinakas[5].)
}
}
c++;
}
For each item in the array, up to 5 comparisons are done for the min list and 5 for the max list.
Suggest calling a function to do this in a tidy fashion.
#include<assert.h>
// `list` is `const` as OP says "withouth changing any of the arregment".
void sort_ends(const int *list, size_t listlen, int *minlist, int *maxlist,
size_t mlen) {
assert(list);
assert(minlist);
assert(maxlist);
assert(mlen >= 1);
assert(listlen >= mlen);
minlist[0] = list[0];
// For each element after the first ...
for (size_t i = 1; i < listlen; i++) {
int mincandidate = list[i];
size_t mini = i;
if (mini > mlen) mini = mlen;
do {
mini--;
if (mincandidate >= minlist[mini])
break;
// swap mincandidate and minlist[mini]
int t = mincandidate;
mincandidate = minlist[mini];
minlist[mini] = t;
} while (mini > 0);
}
// Do similar for maxlist, left for OP
}
int main() {
int ari;
// ...
int pinakas[ari];
// ...
int mlen = 5;
int minlist[mlen];
int maxlist[mlen];
sort_ends(pinakas, ari, minlist, maxlist, mlen);
return 0;
}
Alternative approach, find min index and then memove().

C: segfault on merge sort only on large files

The following code sorts an array of words, working on small arrays, and segfaulting on large ones (>400000 words, though I haven't found a limit). It is being called by a program that passes it an array of words (read from a file) to be sorted and tests its success:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "csort.h"
#include "sort.h"
// array points to array of pointers to strings, count is number of entries in array
void sortC(char** array, unsigned int count){
array = merge_sort(array, count);
// testing:
/*for (int i = 0; i < count; i++){
printf("%s ", array[i]);
}*/
}
char** merge_sort(char** array, int count){
if (count <= 1) return array;
else {
int lcount = 0;
int rcount = 0;
int middle = count/2;
lcount = middle;
char* left[lcount];
subArray(array, left, 0, middle);
rcount = count-middle;
char* right[rcount];
subArray(array, right, middle, count);
return merge(merge_sort(left, lcount), merge_sort(right, rcount), array, 0, lcount, rcount);
}
}
void subArray(char** array, char** subarray, int start, int end){
int ai; // index in original array
int si; // index in subarray
for (ai = start, si = 0; ai < end; ai++, si++){
subarray[si] = array[ai];
}
}
char** merge(char** left, char** right, char** output, int oi, int lcount, int rcount){
if (lcount > 0 && rcount > 0){
int lmin = findMinimum(left, lcount);
int rmin = findMinimum(right, rcount);
if (strcmp(left[lmin], right[rmin]) < 0){
output[oi] = left[lmin];
removeFromArray(left, lmin, lcount);
lcount--;
}
else {
output[oi] = right[rmin];
removeFromArray(right, rmin, rcount);
rcount--;
}
}
else if (lcount == 0) {
if (rcount == 1) {
output[oi] = right[0];
return output;
} else {
int rmin = findMinimum(right, rcount);
output[oi] = right[rmin];
removeFromArray(right, rmin, rcount);
rcount--;
}
}
else if (rcount == 0) {
if (lcount == 1) {
output[oi] = left[0];
return output;
} else {
int lmin = findMinimum(left, lcount);
output[oi] = left[lmin];
removeFromArray(left, lmin, lcount);
lcount--;
}
}
return merge(left, right, output, ++oi, lcount, rcount);
}
int findMinimum(char** array, int count){
char* minvalue = array[0];
char* currentvalue = minvalue;
int minindex = 0;
for (int i = 1; i < count; i++){
currentvalue = array[i];
if (strcmp(currentvalue, minvalue) < 0){
minvalue = currentvalue;
minindex = i;
}
}
return minindex;
}
void removeFromArray(char** array, int index, int count){
// removes specified index from an array
for (int i = index; i < count; i++){
if (i+1 == count){
array[i] = 0; // this entry will be gone when count decrements
} else {
array[i] = array[i+1];
}
}
}
If there's no bug on your code then the problem might be how you are storing the data. Do you use malloc() to allocate the array to store your data or are you declaring an array that is big enough?
For large data sets you must use malloc(), which will allocate space on the HEAP instead of the stack. The stack has a limited space. This would explain why with smaller data your program works and with bigger data sets it crashes.
Also one very important point is that you are using recursion: merge() calling merge(). Too many recursive calls could lead to a stack overflow (segfault).
Looks like a stack overflow, you are allocating automatic arrays of thousands if items in each invocation, and then recursing.
These lines, to be specific:
char* left[lcount];
and
char* right[rcount];
For the values in your comment, where count == 7157, this would be quite costly in terms of stack space.
Consider either using malloc() for these, or figure out a way to represent a sub-array without requiring new memory.

Resources