简体   繁体   中英

C recursive function to check between two arrays if they are reversed or not

I need to check between two arrays if they are reversed, eg A[3] = {1, 2, 3} and B[3] = {3, 2, 1}

Returning zero if A is not the reverse of B of a given length, and 1 otherwise.

Here is what I managed to do for now

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int* input_array(int);
int areReversed(int*, int*, int);

int main()
{
    int n = 0;
    int* A, * B;
    printf("please enter the size of arrays: \n");
    scanf("%d", &n);
    printf("please enter elements of the first array: \n");
    A = input_array(n);
    printf("please enter elements of the second array: \n");
    B = input_array(n);
    areReversed(A, B, n) ? printf("Arrays are the opposite to one another.\n") : printf("Arrays are not the opposite to one another.\n");
    free(A); free(B);
}

int areReversed(int* A, int* B, int n) {
    int i = 0, j = n-1;
    int reverse = 0;
    if (n > 1)
    {
        for (i = 0, j = 0; i < n && j >= 0; i++, j--)
        {
            if (A[i] == B[j])
            {
                return areReversed(A + 1, B + 1, n);
            }
            if (A[i] != B[j])
                return 0;

        }
        return 1;
    }
}

int* input_array(int n) {
    int i;
    int* A = (int*)malloc(n * sizeof(int));
    assert(A);
    printf("Enter %d integers\n", n);
    for (i = 0; i < n; i++) {
        scanf("%d", A + i);
    }
    return A;
}

`

but sadly its not working and I have tried so many things... even if you can give hints its will be awesome

With recursion the key is:

  1. To have a robust terminating condition that will be met, and which has a definitive answer.
  2. The problem is a function of a smaller but identical problem.

In this case, the terminating condition is when the length of the arrays n is zero (return true), or when A[0] != B[n-1] (return false)

For an array length n where the two opposite ends are equal ( A[0] == B[n-1] ), A may be the reverse of B , so you turn the problem into a smaller one and test that. The smaller problem is "one-in" from each end of each array - ie:

areReversed( &A[1], B, n - 1 ) ;

If you were doing this iteratively rather then recursively, the "smaller" test after comparing A[0] with B[n-1] would be to compare A[1] with B[n-2] . In recursion only one pair is tested, but the recursive call modifies the parameters to achieve the same effect. So here the recursive call's A is the parent calls &A[1] (or A + 1 if you like - I don't), and the array length is one shorter so that the recursive call's B[n-1] is the parent calls B[n-2] .

So:

#include <stdio.h>
#include <stdbool.h>

bool areReversed( int* A, int* B, int n) 
{
    int is_reverse = false ;
    if( n == 0 )
    {
        is_reverse = true ;
    }
    else if( A[0] == B[n-1] )
    {
        is_reverse = areReversed( &A[1], B, n - 1 ) ;
    }

    return is_reverse ;
}

int main()
{
    int A1[] = {1, 2, 5, 14, 9, 3} ;
    int B1[] = {3, 9, 14, 5, 2, 1} ;
    int A2[] = {1, 2, 5, 14, 9, 3} ;
    int B2[] = {3, 9, 14, 5, 2, 7} ;
    
    bool R1 = areReversed( A1, B1, sizeof(A1) / sizeof(*A1) ) ;
    bool R2 = areReversed( A2, B2, sizeof(A2) / sizeof(*A2) ) ;

    printf( "A1 %s the reverse and B1\n", R1 ? "is" : "is not" ) ;
    printf( "A2 %s the reverse and B2\n", R2 ? "is" : "is not" ) ;
}

Outputs:

A1 is the reverse and B1
A2 is not the reverse and B2

And to demonstrate its function with an odd number of elements:

int A1[] = {1, 2, 5,   99, 14, 9, 3} ;
int B1[] = {3, 9, 14, 101, 5, 2, 1} ;
int A2[] = {1, 2, 5,  100, 14, 9, 3} ;
int B2[] = {3, 9, 14, 100, 5, 2, 1} ;

Output then is:

A1 is not the reverse and B1
A2 is the reverse and B2

I recommend that to understand recursion, you use your debugger to step through the code, stepping into each recursive call to observe the "smaller problem" and the meeting the terminating condition, and stepping-out to observe the "unwinding" of the algorithm and final return. In any event you should learn effective use of a debugger - it is a great learning tool to observe the precise behaviour of code and state of variables as well as a debugging aid.

I would also suggest that while a simple function such as this is a useful way of exploring recursion as a concept, it is also trivially implemented using iteration and should probably be done that way in practice. Some problems are much less amenable to iteration and lend themselves to recursion. I'd reserve recursion for such problems - binary search trees , and flood-fill for example spring to mind, though even then recursion is not required, merely simpler.

The problem with recursion is that it has a non-deterministic call-stack requirement and the call-stack is a finite resource - you can literally get a _stack-overflow. In your test case where the data is provided at runtime and is of unlimited length, a malicious or unwary user could cause a stack-overflow with no means in your code to protect against such an attack or misuse.

areReversed can be simply:

int areReversed(int *A, int *B, int n) 
{
    return n == 0 || A[0] == B[n-1] && areReversed(A+1, B, n-1);
}

This works:

  • If n is zero, the two arrays are trivially reverses of each other, since both are empty. (We expect n is not negative.)
  • Otherwise, we compare the first element of A to the last element of B . If they are unequal, this == and the && fail (produce zero for “false”), and “false” is returned, since the arrays are not reverses of each other.
  • If they are equal, we also require the rest of the arrays to be reversed, which is handled by a recursive case: The last n-1 elements of A (starting at A+1 ) must be the reverse of the first n-1 elements of B (starting at B ).

Let's at first start with your function declaration.

int areReversed(int*, int*, int);

Within the function the passed arrays are not changed within the function are they?

So the corresponding first two function parameters should be declared with the qualifier const

int areReversed( const int *, const int *, int);

Also sizes of entities in C are values of the type size_t . So the third parameter should have the type size_t

int areReversed( const int *, const int *, size_t );

And at last it is a bad idea to use upper case letters as identifiers.

Further, the function returns logical true if either the third parameter is equal to 0 or when the first element of the first array is equal to the last element of the second array and this condition is valid for other elements of the two arrays in recursive calls of the function. Thus it is enough to write only one return statement with the logical expression as described above.

So the function can be defined the following way

int areReversed( const int *a, const int *b, size_t n )
{
    return n == 0 || ( a[0] == b[n - 1] && areReversed( a + 1, b, n - 1 ) );
}

But you can reduce the number of recursive calls of the function if you will compare the first element of the first array with the last element of the second array and vice versa when you will compare the last element of the first array with the first element of the second array.

In this case the function will look the following way.

int areReversed( const int *a, const int *b, size_t n )
{
    return ( n == 0 )                 || 
           ( n == 1 && a[0] == b[0] ) ||
           ( a[0] == b[n - 1] && a[n - 1] == b[0] && areReversed( a + 1, b + 1, n - 2 ) );
}

But what to do if the arrays have a different element type?

In this case you have to write another function when for example the array element type is for example double . And moreover you will have to name the function differently that will only confuse readers of your code.

Unfortunately you may not overload functions in C as you may do in C++.

In this case the approach to solve the problem is the same as the approach used in such C standard functions as bsearch and qsort .

That is in this case you have to deal with pointers of the type const void * and to use an auxiliary function that will convert the pointers of the type const void * to the appropriate pointer types.

In this case a general function can look the following way as it is shown in the demonstration program below..

#include <stdio.h>

int areReversed( const void *a, const void *b,
                 size_t nmemb, size_t size,
                 int cmp( const void *, const void * ) )
{
    return ( nmemb == 0 )                ||
           ( nmemb == 1 && cmp( a, b ) ) ||
           ( cmp( a, ( const char * )b + ( nmemb - 1 ) * size ) &&
             cmp( b, ( const char * )a + ( nmemb - 1 ) * size ) &&
             areReversed( ( const char * )a + size, ( const char * )b + size, nmemb - 2, size, cmp ) );
}

static inline int cmp( const void *a, const void *b )
{
    return *( const int * )a == *( const int * )b;
}

int main( void )
{
    enum { N = 7 };
    int a[N] = { 1, 4, 6, 7, 5, 3, 2 };
    int b[N] = { 2, 3, 5, 7, 6, 4, 1 };

    printf( "The arrays are reversed relative to each other is %s.\n",
        areReversed( a, b, N, sizeof( int ), cmp ) ? "true" : "false" );
}

Opposite to C in C++ you could just use the standard algorithm std::equal as for example

#include <iostream>
#include <iterator>
#include <algorithm>

template <typename T1, typename T2>
bool areReversed( const T1 &a, const T2 &b )
{
    return std::size( a ) == std::size( b ) &&
           std::equal( std::begin( a ), std::end( a ), std::rbegin( b ) );
}

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

    std::cout << "The arrays are reversed relative to each other is " 
              << ( areReversed( a, b ) ? "true" : "false" ) << '\n';
}

Bear in mind that if you say that you know C and you do not know C++ and vice versa that you know C++ and you do not know C then actually it just means that you know neither C nor C++.:)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM