I have a question: write a function that gets an ascending array of whole numbers and their size.
It is given that the array contains at least one negative number and one positive number, and I need to find the closest negative number to the number 0.
for example: [-30,-25,-18,-10,11,11,20,30]
the function will return -10.
The problem is that I need to do it in O(log n) complexity and I don't know how to do this.
I made it only with O(n).
`
int f(int* arr, int size)
{
int i;
int result = arr[0];
for (i = 1;i < size;i++)
{
if (arr[i] < 0 && result < arr[i])
result = arr[i];
else
return result;
}
return result;
}
Here is a simple C implementation of a binary search, which works in O(log n) time.
#include <stdio.h>
int find(int *arr, size_t size)
{
size_t bot = 0;
size_t top = size; // it will never be top
size_t dif;
while((dif = top - bot) > 1) {
size_t mid = bot + dif / 2;
if(arr[mid] >= 0) { // eliminate non-negatives
top = mid;
}
else {
bot = mid;
}
}
return arr[bot];
}
int main(void) {
int arr[] = { -30, -25, -18, -10, 11, 11, 20, 30 };
size_t size = sizeof arr / sizeof arr[0]; //parentheses only needed for types
printf("%d\n", find(arr, size));
}
I like to use a binary search so that the top element is never a candidate.
Program output:
-10
Here's a possible way
int f( int *n, int size )
{
int low = 0, mid, high = size-1;
while( (mid = (low+high)/2) && n[mid] * n[mid+1] > 0 ) n[mid] < 0 ? (low = mid) : (high = mid);
return n[mid] ? n[mid] : n[mid-1];
}
I posted it condensed to give you something to do. Rewrite it properly using if else and do while.
The program uses min, max and mid as indexes for the array n[].
min is set to 0 and will always be the index of a negative number, max is set to size-1 and will always be the index of a positive number or of a 0. mid will always be between them (or exactly them).
The loop breaks when find an element (that is n[mid]) such that multiplied for the next one gives a negative number or zero.
Then the function returns n[mid], unless it is 0, in this case it returns the element before n[mid].
Note that if the array can contain dupes, then you need to handle the case of multiple 0s adding something like that at the end
while( !n[mid] ) --mid;
EDIT: I forgot to tell you that since the algorithm for the function is a binary search, it suits your time complexity requirements.
I'm trying to implement a binary search in a slightly non-traditional way by using only 3 arguments int value (what I'm looking for), int values[] (the array), int n (the size of the array). The code below finds the number 2 and recognizes that 13 is not there, but cannot find numbers like 6 or 7. I think the problem is in the final recursive call. It could be a pointer issue. I'm certain the rest of the code works fine. Any thoughts on where I might be going wrong would be appreciated.
#include <stdio.h>
#include <stdbool.h>
bool search(int value, int values[], int n);
int main(void)
{
int value = 6;
int values[] = {1, 2, 3, 4, 5, 6, 7};
int n = 7;
bool x = search(value, values, n);
if (x == true)
printf("found\n");
else
printf("not found\n");
}
bool search(int value, int values[], int n)
{
int midpoint = n/2;
if (n/2 <= 0)
{
return false;
}
if (value == values[midpoint])
{
return true;
}
if (value < values[midpoint])
{
return search(value, values, n/2);
}
else if (value > values[midpoint])
{
return search(value, values, n/2);
}
return false;
}
Yes, the problem is that when you call search with the upper half of the array, you should pass it with the offset like
return search(value, values + (n + 1) / 2, n / 2);
Note that I also skipped the middle element that you have already compared for the cases when n is odd. You can of course optimize the recursive calls, always taking care that also the length is calculated correctly.
I have broken my head trying to solve this question on an exam. I'm stil shocked by it.
Can anybody help me write a function:
void get2(int a[], int n, *i1, *i2)
that receives the massive a[] of integers and his length n and saves to pointers i1 and i2 the indexes of the two biggest numbers in the massive (i1 - the biggest, i2 - the second biggest).
The solution must be recursive and can not contain loops. It has be done by the complextiy of O(n). The numbers in the massive are all diffrent. We cant use any helper functions.
There aren't any additional conditions. I tried some solutions, but they werent good enough.
I dont know how to save the indexes without losing there values when I recurse.
There was additional hint, that I should pay attention to how I use the *i1, *i2 to pass information between recursive steps, but i tried to use it some how and it didn't work for me anything I tried. Help somebody ?
There is only one trick to play on this exercise: Recognize when we are in the outermost recursive call and when we are not, or simpler terms when the current call of get2 is the first one and when it is not.
Using the pointers and the n parameter to do some basic Divide et Impera strategy is straightforward and require no further explanation.
We need to tell the first and subsequent calls because in the first, and only there, call we need to initialize the pointers.
We cannot rely on i1 or i2 values as they are uninitialized.
Only n has enough structure to carry some information: When we are called from the extern, i.e. not by recurring, then n >= 0.
We can then perform the one-time initialization and then negate n. This way if n<=0 we know that we are in the recursive calls.
However we still have a problem: if n >= 0 then it's opposite is n <= 0. The two condition overlaps for n == 0.
This will make the initialization happen two time and further worst the second one at the end of the recursion stack thereby voiding the state calculated.
In order to avoid them, the n is not simply negated but negated and decremented by 1, so the 5 becomes -6.
We can then recover the usable value of n by again negating and decrementing by 1. In the code below this is done and stored in m.
The else part is straightforward as pointed above. We only additionally check that the second maximal element is not equal to the maximal element in the second if.
If one index is -1 it means that it is not pointing to any element and can be assigned unconditionally.
When the function return, i1 cannot point to -1 unless the array is empty.
i2 can point to -1 if all elements are the same.
#include <stdio.h>
void get2(int a[], int n, int* i1, int* i2)
{
/* Transform from encoded n to actual length */
int m = -n-1;
/* Is this the first call? */
if (n >= 0)
{
/* Initialize the pointer to -1, i.e. no elements */
*i1 = *i2 = -1;
/* Start the recursion, encode the length */
get2(a, -n-1, i1, i2);
}
/* Here we are in the subsequent calls, we use m and not n */
else if (m-- > 0)
{
/* Assign i1 */
if (*i1 == -1 || a[m] > a[*i1])
{
*i2 = *i1; /* Don't forget to update i2 too! */
*i1 = m;
}
/* Opportunity to assign i2, check that the second max != max */
else if ((*i2 == -1 || a[m] > a[*i2]) && a[m] != a[*i1])
{
*i2 = m;
}
/* Tail recursion, do proper encoding of the n parameter */
get2(a, -m-1, i1, i2);
}
}
This function can be tested with this code
void test(int a[], int n)
{
int max, smax, i;
get2(a, n, &max, &smax);
printf("------------------------------\n");
for (i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\nMax is ");
if (max >= 0)
printf("%d", a[max]);
else
printf("NOT FOUND");
printf("\nSecond Max is ");
if (smax >= 0)
printf("%d\n", a[smax]);
else
printf("NOT FOUND\n");
}
int main()
{
int v1[] = {};
int v2[] = {1};
int v3[] = {1,2};
int v4[] = {2, 1};
int v5[] = {2, 2};
int v6[] = {1,2,3,4};
int v7[] = {4,3,2,1};
int v8[] = {2,1,4,3};
int v9[] = {2,2,2,2};
int v10[] = {1,2,3,4,3,2,1};
int v11[] = {1,1,2,3,4,4};
test(v1, 0);
test(v2, 1);
test(v3, 2);
test(v4, 2);
test(v5, 2);
test(v6, 4);
test(v7, 4);
test(v8, 4);
test(v9, 4);
test(v10, 7);
test(v11, 6);
return 0;
}
With the below output
------------------------------
Max is NOT FOUND
Second Max is NOT FOUND
------------------------------
1
Max is 1
Second Max is NOT FOUND
------------------------------
1 2
Max is 2
Second Max is 1
------------------------------
2 1
Max is 2
Second Max is 1
------------------------------
2 2
Max is 2
Second Max is NOT FOUND
------------------------------
1 2 3 4
Max is 4
Second Max is 3
------------------------------
4 3 2 1
Max is 4
Second Max is 3
------------------------------
2 1 4 3
Max is 4
Second Max is 3
------------------------------
2 2 2 2
Max is 2
Second Max is NOT FOUND
------------------------------
1 2 3 4 3 2 1
Max is 4
Second Max is 3
------------------------------
1 1 2 3 4 4
Max is 4
Second Max is 3
I will give you another hint instead of writing the actual code for you:
You have 4 arguments, use all of them to pass the relevant data to the recursive calls. What I mean by that is - the first 2 arguments can describe the part of the array that is interesting for us, the pointers store the largest integers we have found, you modify them each time you find a bigger one.
And here lies the trap that most likely caught you - those are int *, that is a pointer to an integer. What the function receives is simply an address in memory. If you set that pointer to point to anything, the caller will not receive that data. You can simply write directly to that address instead of setting the pointer, like so:
int myInteger;
int *ptr = &myInteger;
*ptr = 123;
Here is a non-threadsafe way to do it: by keeping track of the original start of the array using a static variable. The addresses of the largest elements are stored in the pointers, which are used as pointers to pointers, and right at the end they are replaced by the indexes of the elements by using some pointer arithmetic aided by the static variable.
void get2(int a[], int n, int *one, int *two) {
static int *arr_start = 0;
if (n > 0) {
if (arr_start == 0) {
arr_start = a;
}
if (a[0] > **((int **)one)) {
*one = a;
} else if (a[0] > **((int **)two)) {
*two = a;
}
get2(a + 1, n - 1, one, two);
} else {
*one = *((int **)one) - arr_start;
*two = *((int **)two) - arr_start;
}
}
EDIT
Without using static variables: Use the second pointer as an indicator of whether or not we have already allocated space to keep track of the the start of the array and the indexes. Use the allocated space to keep track of the initial array size and the two largest indexes, then assign them to the correct pointers at the end:
#include <stdlib.h>
void get2(int a[], int n, int *one, int *two) {
int **pp_one = (int **)one;
if (*two != NULL) {
*pp_one = malloc(sizeof(int) * 3);
(*pp_one)[0] = n;
(*pp_one)[1] = 0;
(*pp_one)[2] = 0;
}
if (n > 0) {
if (a[0] > (*pp_one)[1]) {
(*pp_one)[1] = a;
} else if (a[0] > (*pp_one)[2]) {
(*pp_one)[2] = a;
}
get2(a + 1, n - 1, one, two);
} else {
int *t = *one;
*two = (*pp_one)[1] - (*pp_one)[0];
*one = (*pp_one)[2] - (*pp_one)[0];
free(t);
}
}
Thanks for the brain teaser. It sounded like an interesting problem. Even though it's homework, I decided to solve it.
This gets a bit annoying to do without having the indices pre-initialized somehow, but it's doable. Sounds like an artificial restriction to create artificial pain.
void
get2(int a[], int n, int *i1, int *i2) {
if (n == 0) {
*i1 = *i2 = -1;
return;
}
get2(&a[1], n - 1, i1, i2);
if (*i1 != -1)
(*i1)++;
if (*i2 != -1)
(*i2)++;
if (*i1 == -1) {
*i1 = 0;
} else if (*i2 == -1 || a[*i2] < a[0]) {
*i2 = 0;
if (a[*i1] < a[0]) {
*i2 = *i1;
*i1 = 0;
}
}
}
Here we just recurse until we have no array left to look at, then initialize the indices and on the way back we figure out the indices we're interested in. On the way back through all the recursions we update the indices i1 and i2 if they are initialized to properly point into the array we have in this iteration, then if i1 isn't initialized yet, then we know that n == 0 and must be the biggest element. On iterations after that we know that is the only interesting element of the array is element 0, if that's not bigger than a[*i2], then it's not bigger than a[*i1] either. If it is (or we're just initializing i2), we compare to see if we need to swap i1 and i2.
"But wait!", you might yell. "You're not doing tail recursion, if the array is massive you will run out of stack.". Glad you mentioned it, in fact, C doesn't say anything about the compiler having to do optimizations for tail recursion so I'd like to argue that any solution has to account for this problem. Here's a solution that works on the same principles (run out of array before we initialize the indices) that solves this in O(log n) stack space:
void
get2(int a[], int n, int *i1, int *i2) {
/*
* Reset the indices at the end of the recursion.
*/
switch (n) {
case 0:
*i1 = -1;
*i2 = -1;
return;
case 1:
*i1 = 0;
*i2 = -1;
return;
}
/*
* Number of elements in the lower and upper halves of the array.
* Notice the '+ (n & 1)' which is there to add one element to the
* upper half of the array if n is odd.
*
* The asserts document the invariants.
*/
int lower_half = n / 2, upper_half = (n / 2) + (n & 1);
int li1, li2, ui1, ui2;
assert(lower_half >= 1);
assert(upper_half >= 1);
assert(lower_half + upper_half == n);
get2(&a[0], n - upper_half, &li1, &li2);
get2(&a[lower_half], n - lower_half, &ui1, &ui2);
ui1 += lower_half;
if (ui2 != -1)
ui2 += lower_half;
assert(li1 != -1);
assert(ui1 != -1);
assert(li2 == -1 || a[li2] < a[li1]);
assert(ui2 == -1 || a[ui2] < a[ui1]);
if (a[li1] < a[ui1]) {
*i1 = ui1;
if (ui2 == -1 || a[li1] > a[ui2])
*i2 = li1;
else
*i2 = ui2;
} else if (a[li1] > a[ui1]) {
*i1 = li1;
if (li2 == -1 || a[ui1] > a[li2])
*i2 = ui1;
else
*i2 = li2;
} else {
*i1 = li1;
if (li2 == -1 || a[ui2] > a[li2])
*i2 = ui2;
else
*i2 = li2;
}
}
I'm not going to explain it, the comments and asserts should be enough to show what's going on. I'm pretty sure that the big block of ifs at the end can be written much more efficiently, but I didn't bother.
It might not be perfectly correct, it worked on the first try with a few test cases taken from the previous answers and I didn't do more testing. There might be some edge cases I haven't considered, but the general idea works.
Here is a nice answer I got. The next few strokes are just to get enough characters to publish my answer with this image. this it requieres 30 characters, so i wiil write anything.
It's brilliant as simple as it is. Isnt it ))?
* This code need a little correction, with one more condition, if one of the pointers is NULL, not to loop.
Here's my take on it. This implementation is pretty concise.
void get2(int a[], int n, int *i1, int *i2)
{
if (n == 0) return;
if (*i1 == -1 || a[n-1] > a[*i1]) {
*i2 = *i1;
*i1 = n-1;
} else if (*i2 == -1 || a[n-1] > a[*i2]) {
*i2 = n-1;
}
get2(a, n-1, i1, i2);
}
To test:
#define NUM 10
int main()
{
int a[NUM];
int i, i1, i2;
srand(time(NULL));
for (i=0;i<NUM;i++) {
a[i] = rand() & 0xFFFF;
fprintf(stderr,"%d ",a[i]);
}
fprintf(stderr,"\n");
i1 = i2 = -1;
get2(a,NUM,&i1,&i2);
fprintf(stderr,"i1=%d, i2=%d\n", i1, i2);
fprintf(stderr,"a[i1]=%d, a[i2]=%d\n", a[i1], a[i2]);
}
Just another try on your function prototype,
void get2(int a[], int n, int *i1, int *i2)
{
if(n <= 0)
return;
if(*i1 == -1 && n < 2){//initial a[] is null or with only one element
*i1 = n-1;
return;
}
if(*i1 == -1 && n >=2){
if(a[n-1] > a[n-2]){
*i1 = n-1;
*i2 = n-2;
}else{
*i1 = n-2;
*i2 = n-1;
}
return get2(a, n-2, i1, i2);
}
int max= a[*i1], less = a[*i2];
if(a[n-1] > max){
*i2 = *i1;
*i1 = n-1;
}else if (a[n-1] > less){
*i2 = n-1;
}
get2(a, n-1, i1, i2);
}
Test as follows,
int main()
{
int a[] = { 1, 2, 3, 4, 7, 8, 6, 5, 0};
int maxIndex = -1, lessIndex= -1;
get2(a,sizeof(a)/sizeof(int),&maxIndex,&lessIndex);
printf("maxIndex=%d(value:%d), lessIndex=%d(value:%d)\n",
maxIndex,a[maxIndex],lessIndex, a[lessIndex]);
}
Output,
maxIndex=5(value:8), lessIndex=4(value:7)
i would like to ask about the array recursion in C, Let say i have an array in float
float arr[] = {12.5, 5.5, 6.0, 18.0};
i want to count the number which is greater than 10, so the result should be 2. However, below is what i did
int cntArray(float arr[], int size)
{
int number = 0;
if((cntArray(&arr[1], size - 1))>=5)
number++;
return number;
}
int main()
{
float arr[] = {12.5, 5.5, 6.0, 18.0};
int result;
result = cntArray(arr, 4);
printf("The result is : %d", result);
}
But it returns 0 as result, any solutions for it? Thanks
Another example:
int cntArray(float arr[], int size) {
if (!size) return 0;
return (arr[0] > 10.0 ? 1 : 0) + cntArray(&arr[1], size - 1);
}
Edit #1 (in reply to comment):
This simply evaluate through a ternary operator (?:) if the first index of arr[] is greater than 10.0. If true, 1 will be the lvalue, otherwise will be 0. The lvalue of cntArray(&arr[1], size - 1) (which will process the next element on the array after decrementing size and if size is different than 0, which in this case will immediately return 0) will be added to the lvalue of the ternary operator. Applying this recurring logic, the aftermath will be the number of elements in the array that are greater than 10.0.
Your cntArray() function lacks a recursion base case, and also seems to hardcode the wrong value (5 instead of 10).
It should be something like:
int cntArray(const float *arr, size_t size)
{
if(size > 0)
{
/* Count the first element, then recurse. */
const int count = arr[0] >= 10.f;
return count + cntArray(arr + 1, size -1);
}
return 0;
}
Your logic is severely flawed: You don't actually check if a value in the array is larger than 10. you also always return number which will always be zero meaning the condition will always be false.
int cntArray(float arr[], int size){
int number = 0;
if((number = cntArray(&arr[1], size - 1))>=5)
number++;
return number;
}
You maybe want to change 5 to 10.
int cntArray(float arr[], int size) {
int number = 0;
if (size > 0) number = cntArray(&arr[1], size - 1);
if (arr[0] > 10) number += 1;
return number;
}
I'm not sure what does this portion of your code does.
if((cntArray(&arr[1], size - 1))>=5)
number++;
I would something like this instead:
int cntArray(float arr[], int index, int size){
if (index == size) return 0; // base case
int rest = cntArray(arr, index + 1, size); // how many elements are greater
// then 10 in rest of the array
if (arr[index] > 10)
return rest + 1;
else
return rest;
}
And call it in the main like this:
cntArray(arr, 0, 4);
#include <stdio.h>
int bsearch(int a[], int n, int lo, int hi) {
int mid;
mid = (hi + lo) / 2;
if(a[mid] == n)
return 1;
else if(a[mid] > n)
bsearch(a, n, lo, mid);
else
bsearch(a, n, mid, hi);
return 0;
}
int main(void) {
int n, a[7] = {2, 4, 5, 67, 70, 80, 81};
int hi = 6, lo = 0, j;
scanf("%d", &n);
j = bsearch(a, n, lo, hi);
if(j)
printf("Found");
else
printf("Not Found");
return 0;
}
input : 5 output: Not Found
Can anyone tell me why I'm getting this result?
You need to fix several big issues to make it work (see details in following code comments).
Change your binary search function to the following:
int bsearch(int a[], int n, int lo, int hi)
{
// add base case
if (high < low)
return 0; // not found
int mid;
mid=(hi+lo)/2;
if(a[mid]==n)
return 1;
else if(a[mid]>n)
return bsearch(a,n,lo,mid-1); // add return
else
return bsearch(a,n,mid+1,hi); // add return
}
P.S.: And based on your usage in the main() body, you actually only need to return 0/1 to indicate contains the value or not. I will suggest you to use bool return type to make it more clear.
Add "return" to the recursive calls, e.g.:
return bsearch(a,n,lo,mid);
Otherwise, when you return 1 in bsearch, it does not get returned all the way to main.
That will make it work for 5. You have other bugs, so try with many values and use an IDE and/or printf to see what's happening. Good luck and have fun!
That's because the return 0; statement in your bsearch function is always executed because you are simply discarding the values returned by the recursive calls. In a recursive function, you must first decide the base case. Here in your bsearch, the base case should be
low <= hi
This is the first condition which must be true to start the search for the sought value. If this condition is not fulfilled, then you must return false, i.e., 0.
Next, a value returning function call is an expression, i.e., it evaluates to a value. When you simply call the function and do nothing with the result, you will always fall down to the last return statement in your function. Here I list some points in comments alongside the statements in your bsearch function.
int bsearch(int a[], int n, int lo, int hi) {
// first check for the base condition here
// if(hi < low) return 0;
int mid;
// can cause integer overflow. should be
// mid = lo + (hi - lo) / 2;
mid = (hi + lo) / 2;
if(a[mid] == n)
return 1;
else if(a[mid] > n)
// you are doing nothing with the value returned
// think of the function call as an expression
// return the value of the expression
// should be
// return besearch(a, n, lo, hi);
bsearch(a, n, lo, mid);
else
// same follows here
// should be
// return bsearch(a, n, mid, hi);
bsearch(a, n, mid, hi);
// finally you will always return 0 because this statement is always executed
// all cases have been taken care of.
// no return statement needed here
return 0;
}