1

I need a guidance to understand where my logic is not working. I need to write a recursive function that receives array A, and array B from a user input. The function is checking if the arrays are reversed. For example: A = {1, 4, 6, 7, 5, 3, 2}, B = {2, 3, 5, 7, 6, 4, 1} the function will return 1. If they are not reversed, the function will return 0. The size of the arrays is irrelevant as it's the same for both A and B. When I run my program, no matter what's the input, the result is 0, even if the input is correct(B is reversed of A).

int areReversed(int* A, int* B, int n)
{
    if (n <= 0) return 1; // all elements have been compared and are equal

    // Compare first element of array A and last element of array B
    if (A[0] != B[n - 1])
        return 0; // elements are not equal

    // Recursively compare remaining elements of arrays A and B
    return areReversed(A + 1, B - 1, n - 2);
}

This is my code so far. Would love to know what's the reason the function returns 0 all the time and where my logic fails. On paper it should work from what I see.

I played around a bit, and changed the recursion call to (A + 1, B, n - 1) Did a few tests and it appears to be working. Would love a second opinion on the change and see if it flawless or there's still some work to do. In the original code I decremented n too much so it skipped few elements of B, thus the comparison was wrong.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Must
  • 19
  • 3
  • 5
    Have you tried running your program through a [debugger](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) to check each step and your assumptions? – nanofarad Dec 26 '22 at 16:38
  • 3
    You are _so_ very close. Whatever you think `B - 1` is doing... it isn’t. Remember, you want to recurse over the inner array in `B` the same way you do with `A`. – Dúthomhas Dec 26 '22 at 16:40
  • Why n-2 and not n-1? – pm100 Dec 26 '22 at 16:41
  • @Pablo No, op is taking 2 elements off: one from each end. – Dúthomhas Dec 26 '22 at 16:41
  • @Dúthomhas no, every array is indepedent of each other, so he takes only one element. – Pablo Dec 26 '22 at 16:42
  • @Must Make sure you check _both_ ends of the arrays, too. – Dúthomhas Dec 26 '22 at 16:42
  • 1
    @Pablo Perhaps you ought to solve the problem first. ;-) – Dúthomhas Dec 26 '22 at 16:43
  • @Dúthomhas perhaps I'm blind, but I don't see where OP is taking two elements of `A`. In the last line, he does `A+1` which is pointer to the next element of the array, hence the array has one element less. Where is the second element talking off the array? – Pablo Dec 26 '22 at 16:45
  • So palindromes, but this time it's an array of integers instead of an array of characters. But why ```n - 2```? You're taking one element off, of each array. Both the arrays decrease by ```1```, so ```n``` should also decrease by ```1```. And as you're already decreasing the ```B``` pointer, you don't need ```B - 1```. It was an interesting exercise, nevertheless. :-) – Harith Dec 26 '22 at 16:49
  • Wait a second, I was right and wrong at the same time. I still think that my observation was right, we only take one element off, but, the pointer of `B` should not be modified. If I call `return areReversed(A + 1, B, n - 1);` then I got the correct result – Pablo Dec 26 '22 at 16:52
  • 1
    `A` is advanced by one because you've already check the fist element. However, if you pass `B+1` in the recursion, you are moving the beginning of the array, hence in the next check you are not checking the last element of `B` (on `if (A[0] != B[n - 1])`), but the penultimate element of the original `B` which is wrong. – Pablo Dec 26 '22 at 16:54

4 Answers4

5

The problem with this code is that you are passing the incorrect base of B and the wrong size.

In if (A[0] != B[n - 1]) you are comparing the first element of A with the last element of B (in reference to n being the length of the array).

So on the next iteration you have to advance A by one, but the base of B must remain as it is and the size must also decrease by one. So the correct call would be return areReversed(A + 1, B, n - 1);.

If you do this:

#include <stdio.h>

int areReversed(int* A, int* B, int n)
{
    if (n <= 0) return 1; // all elements have been compared and are equal

    printf("checking %d and %d, n is %d\n", *A, B[n-1], n); 

    // Compare first element of array A and last element of array B
    if (A[0] != B[n - 1]) 
        return 0; // elements are not equal

    // Recursively compare remaining elements of arrays A and B
    return areReversed(A + 1, B, n - 1); 
}

int main()
{
    int a[] = {1,2,3,4,5};
    int b[] = {5,4,3,2,1};

    printf("Palindrom? %d\n", areReversed(a,b, sizeof a / sizeof *a));
    return 0;
}

then you get the right result:

$ ./a 
checking 1 and 1, n is 5
checking 2 and 2, n is 4
checking 3 and 3, n is 3
checking 4 and 4, n is 2
checking 5 and 5, n is 1
Palindrom? 1
Pablo
  • 13,271
  • 4
  • 39
  • 59
  • It would appear that OP was trying to do this solution...? I personally like it, because it is clean. – Dúthomhas Dec 26 '22 at 17:09
  • @Dúthomhas yes it seems so. I think that OP applied a pattern on the last line that was not really there, hence passed `B-1` by mistake which is why he passed `n-2`. But that not only changes the check, it has potentially an undefined behaviour because `B-1` is pointing outside of the boundaries. – Pablo Dec 26 '22 at 17:14
2

Alright, so, we want to see whether array B is a reverse copy of A.

Here are two arrays:

A = 1 2 3 4 5
B = 5 4 3 2 1

Recursion works by doing something repeatable to reduce the problem to a simpler version. OP has a right idea (not the only right idea, but a valid one):

A = [1] 2 3 4  5    these are equal
B =  5  4 3 2 [1]

A =  1  2 3 4 [5]   and these are equal
B = [5] 4 3 2  1

Since the first and last elements of both arrays are reversed, we can continue with the inner elements of each array:

A′ = 2 3 4
B′ = 4 3 2

Repeat until we run out of elements:

A″ = 3
B″ = 3

A‴ =
B‴ =

No more elements, return true.

Do it with code

As an argument to a function, an array is just given by a pointer. Very conveniently, we can just add one to the pointer value to get the next element, or, more to-the-point, a sub-array starting with the next element.

We also need to decrement the number elements in each array. How many elements come off of the array each time?

Notice that an array of a single element is a special condition: if we subtract 2 elements we wind-up with... -1.

Make sure that you check that n is greater-than zero (and definitely not less-than zero) before trying to compare first and last elements.

Remember, thinking a problem through, using paper and pencil (or a text editor), before writing any code — it is an invaluable exercise when programming. (Especially with tricky recursive stuff!)

Dúthomhas
  • 8,200
  • 2
  • 17
  • 39
  • First of all, thank you for the effort you put into it. It's much appreciated. Second of all, I did try to to either psuedocode or just plan the workflow before attempting to write anything at all, but after a few lines I just feel lost. This function is not the case, however with much more complex functions and multiple loops it's so overwhelming that I get confused. Any suggestions on how to work on that, or how to learn to write psuedocodes or just how to plan the workflow? – Must Dec 26 '22 at 17:12
  • 3
    Just practice. Writing pseudocode isn’t much different from writing code — and when solving a problem it is too formal a process. You must first understand how to make something work. The pencil and paper part is to try to solve the problem _yourself_, before you try to tell the computer how to solve it. Note how I just drew what I wanted to happen to `A` and `B` in this post? I didn’t actually write any code — pseudo or otherwise. That comes _after_ drawing all over the walls with crayon. – Dúthomhas Dec 26 '22 at 17:23
1

Suggestion: we don't need to modify the A and `B pointers at all in the recursion if we add an offset argument which increments up.

We also don't need to check the entire length of both arrays, but just halfway.

#include <stdio.h>

int areReversed(int* A, int* B, int n, int offset) {
    if (offset > n / 2) return 1;
    if (A[offset] == B[n - offset - 1] && A[n - offset - 1] == B[offset])
        return areReversed(A, B, n, offset+1);
    return 0;
}

int main(void) {
    int A[] = {1, 2, 3, 4, 5};
    int B[] = {5, 4, 3, 2, 1};

    if (areReversed(A, B, 5, 0)) {
        printf("They are mirrors of each other.\n");
    }

    return 0;
}

As a further note, consider that it's important to understand how recursion works, but unless your compiler is optimizing tail calls, with larger enough arrays you may encounter a stack overflow. This algorithm is readily adapted to a loop which runs in constant stack space.

int areReversed(int* A, int* B, int n) {
    for (int offset = 0; offset < n / 2; offset++)
        if (A[offset] != B[n - offset - 1] || A[n - offset - 1] != B[offset])
            return 0;

    return 1;
}
Chris
  • 26,361
  • 5
  • 21
  • 42
  • 1
    I also thought about only checking half the array, but that's not correct. With `int a[] = {1,2,3,4,5}; int b[] = {5,9,3,2,1};` your code delivers **true** – Pablo Dec 26 '22 at 17:10
  • Using `offset` is another valid idea, though in this particular case I wouldn’t personally use it because it exposes internal complexity to the caller. – Dúthomhas Dec 26 '22 at 17:12
  • 1
    @Dúthomhas which is _another_ reason to use a loop rather than recursion in a case like this. – Chris Dec 26 '22 at 17:13
  • Don’t discount the learning objective here. Sometimes recursion is a cleaner, more readable, more maintainable solution than using a loop. Here we may only be indexing and comparing elements of an array, but it is often just a stand-in for much more complicated _processes_. – Dúthomhas Dec 26 '22 at 17:16
  • 1
    The original idea was good where you were checking `offset > n / 2`. The only thing need to be modified in it was - `if ((A[offset] == B[n - offset - 1]) && (A[n - offset - 1] == B[offset]))`. Checking only till half the size of array was efficient as well as it save 50% of recursive calls. – H.S. Dec 26 '22 at 17:17
  • 1
    UV for standing different with others in a way where not checking for entire length. – H.S. Dec 26 '22 at 17:22
  • 1
    Thanks. I'll admit that I'm so used to seeing _UB_ come up in C discussions, I misread that and was really confused for a second. – Chris Dec 26 '22 at 17:24
  • AFAIK most modern compilers (MSVC, Clang, GCC, at least) implement tail recursion whenever possible. I wish it were possible to guarantee that behavior, though. That’s why, for example, specification of the Introsort algorithm requires a loop over tail recursion: only because there is no guarantee of a safe, functioning tail-recursive call in the language. – Dúthomhas Dec 26 '22 at 17:26
0

At least in my view, it's much cleaner and simpler if you make a choice up front: either deal entirely with pointers, or deal entirely with array subscripts.

But mixing the two as you are here, using subscripts off of moving pointers, is a recipe for code that's hard to get working, and hard to maintain in the long term.

If I were gong to do it, I'd use just pointers. For the moment, let's assume that you're required to use the function signature you did:

int areReversed(int* A, int* B, int n)

If so, I'd use this solely as a front-end for another function that presumes it's receiving a pointer to the beginning of A and the end of B:

int areReversed(int *A, int *B, int n) { 
    return areReversedImpl(A, B+n-1, n);
}

That function would then implement something like:

  • if n == 0, we're done--return true
  • if *A != *B, we're done--return false
  • otherwise (i.e., if *A equals *B) do a recursive call on (A+1, B-1, n-1)

Sorry, but no, I'm not going to completely write your homework for you though.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 1
    Hey. First of all, thanks for the effort, it's not taken for granted. Second of all, this is the way the assignment was built so I can't change things like what the function returns or receives. Also, I never meant for anyone to give me the answer, I know I'm the one who needs to figure it out. I was just stuck at a point where I couldn't find my logic failure so I was asking for hints on where to look, so I will be able to realize what's now working correctly. – Must Dec 27 '22 at 07:57
  • @Must: Glad to hear it--and good luck. I know how hard it can be... – Jerry Coffin Dec 27 '22 at 09:34