1

I got a question where I need to write a function that reads an integer X and an array A of type int (size N) from the keyboard and eliminates all occurrences of X in A. for example the input is:

5

1 2 3 4 3

3

and it would return:

A : 1 2 3 4 3

New A : 1 2 4

my code so far is

#include <stdio.h>
#include<stdlib.h>
#define DIM 50
int main() {
    int *A;
    int N, X;
    int *P1, *P2;
    do{
     scanf("%d", &N);
    }while(N<0 || N>DIM);

A= (int*)malloc(N*sizeof(int));

for(P1=A; P1<A+N ; P1++)
 scanf("%d ", P1);
printf("\n");

scanf("%d",&X);

printf("A : ");
for(P1=A; P1<A+N ; P1++)
 printf("%d ", *P1);
printf("\n");

but I don't know how to continue if you could please help

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335

4 Answers4

2

What you need is to write a function that will erase elements equal to the specified value and reallocate the result array.

Here is a demonstration program where such a function is shown in action.

#include <stdio.h>
#include <stdlib.h>

size_t erase_remove( int **a, size_t n, int value )
{
    size_t m = 0;

    for (int *p = *a, *q = *a; p != *a + n; ++p)
    {
        if (*p != value)
        {
            if (q != p) *q = *p;
            ++q;
            ++m;
        }
    }

    if (m != n)
    {
        int *tmp = realloc( *a, m * sizeof( int ) );

        if (tmp != NULL)
        {
            *a = tmp;
        }
        else
        {
            m = -1;
        }
    }

    return m;
}

int main( void )
{
    size_t n = 5;
    int *a = malloc( n * sizeof( int ) );

    size_t i = 0;
    a[i++] = 1, a[i++] = 2, a[i++] = 3, a[i++] = 4, a[i++] = 3;

    int value = 3;

    size_t m = erase_remove( &a, n, value );

    if (m != -1) n = m;

    for (const int *p = a; p != a + n; ++p)
    {
        printf( "%d ", *p );
    }
    putchar( '\n' );

    free( a );
}

The program output is

1 2 4

If the memory reallocation for the array within the function was not successful the function returns the value (size_t)-1.

The function preserves the order of elements after removing elements equal to the target value.

If to make the function more general that can deal not only with arrays dynamically allocated then it can look very simply.

size_t erase_remove( int *a, size_t n, int value )
{
    size_t m = 0;

    for (int *p = a, *q = a; p != a + n; ++p)
    {
        if (*p != value)
        {
            if (q != p) *q = *p;
            ++q;
            ++m;
        }
    }

    return m;
}

In this case the caller of the function should reallocate the result dynamically allocated array (if it is required) based on the returned value m from the function.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0
#define N_MAX 50
#define N_MIN 0

int main(void) {
    int n;

    do{
        scanf("%d", &n);
    }while(N<N_MIN  || N>N_MAX);

    int *array = (int*) malloc(sizeof(int) * n);

    int i; // number of elements in array
    for (i = 0; i < n; i++) {
        scanf("%d", array + i);
    }

    int x;
    scanf("%d", &x);

    //remove x from arr
    for (int j = 0; j <= i; j++) {
        if (*(array + j) == x) {
            *(array + j) = *(array + i); // replace removed value by last value in array
            i--; // decremment number of elements in array
        }
    }

    // print
    for (int j = 0; j <= i; j++) {
        print("%d", *(array + j)); 
    }

    free(array)
}


  • You could `realloc` the array after removing `x` to shrink it. Also, array indexing notation is usually easier to read than pointer math: Ex: `if (*(array + j) == x)` → `if (array[j] == x)`, etc, – 001 Jan 05 '22 at 18:33
  • 1
    Where is `N` declared? Should it be `n`? Why the additional `scanf` call for the value if `N`/`n` (did you mean to change the `do ... while` to a `while`)? Missing a semicolon after the allocation. The cast is [not needed](https://stackoverflow.com/q/605845/2505965). Missing headers. This program does not compile - how did you test it? – Oka Jan 05 '22 at 18:33
0

Try this out!

#include <stdio.h>
#include <stdlib.h>

// Required Prototypes
int *get_nums(char *, size_t *);
int *remove_num(int *, size_t *, int);
void display(char *, int *, size_t);

int main(int argc, char *argv[])
{
    size_t size = 0;
    int *arr = get_nums("Enter numbers (seperated by space): ", &size);
    
    int num;
    printf("Enter number to be removed: ");
    scanf("%d", &num);
    display("Old Array: ", arr, size);
    
    arr = remove_num(arr, &size, num);
    display("New Array: ", arr, size);
    
    free(arr);
    return 0;
}

int *get_nums(char *label, size_t *size)
{
  size_t length = 0;
  int *arr = NULL;
  
  printf("%s", label);
    
    int c, num;
    do {
      scanf("%d", &num);
      arr = realloc(arr, (length + 1) * sizeof(int));
      arr[length++] = num;
    } while ( (c = getchar()) != '\n' && c != EOF);
    
    *size = length;
    return arr;
}

int *remove_num(int *arr, size_t *size, int num)
{
  // Copy elements to the new array
  // Return the new array
  size_t new_size = 0;
  int *new_arr = NULL;
  
  for (size_t i = 0; i < *size; ++i) {
    if (arr[i] != num) {
      new_arr = realloc(new_arr, (new_size + 1) * sizeof(int));
      new_arr[new_size++] = arr[i];
    }
  }
  
  *size = new_size;
  free(arr);
  return new_arr;
}

void display(char *label, int *arr, size_t size)
{
  printf("%s", label);
  
  for (size_t i = 0; i < size; ++i)
    printf("%d ", arr[i]);
    
  printf("\n");
}

The main idea is you create an array of integers. Then you copy those elements to a new array which you do not want to remove. And finally you display the new array. That's all. Yes, it's that simple. ;-)

Enter numbers (seperated by space): 1 2 3 4 3
Enter number to be removed: 3
Old Array: 1 2 3 4 3
New Array: 1 2 4

As @Ahmed Masud said in comments about too many reallocations, here's my modified answer. Please do note that the code below is little bit complex but far more efficient than my previous one.

#include <stdio.h>
#include <stdlib.h>

typedef struct {
  int *a;
  size_t length;
  size_t capacity;
} Array;

// Required Prototypes
Array *init_Array(void);
void destroy(Array *);

Array *get_nums(char *);
void remove_num(Array *, int);
void display(char *, Array *);

int main(int argc, char *argv[])
{
    Array *arr = get_nums("Enter Numbers (seperated by space): ");
    
    int num;
    printf("Enter number to be removed: ");
    scanf("%d", &num);
    display("Old Array: ", arr);
    
    remove_num(arr, num);
    display("New Array: ", arr);
    
    destroy(arr);
    return 0;
}

Array *init_Array(void)
{
  Array *arr = malloc( sizeof(Array) );
  arr->capacity = 1;
  arr->length = 0;
  arr->a = malloc( sizeof(int) );
  return arr;
}

Array *get_nums(char *label)
{
  printf("%s", label);
  
  Array *arr = init_Array();
  int c, num;
  
  do {
    scanf("%d", &num);
    // check and reallocate
    if (arr->length == arr->capacity) {
      arr->a = realloc(
        arr->a, 
        (2 * arr->capacity) * sizeof(int)
      );
      arr->capacity *= 2;
    }
    
    arr->a[arr->length++] = num;
  } while ((c = getchar()) != '\n' && c != EOF);
  
  return arr;
}

void remove_num(Array *arr, int num)
{
  int remv_idx = -1;
  int *a = arr->a;
  size_t count = 0;
  
  for (size_t i = 0; i < arr->length; ++i) {
    if (a[i] == num) count++;
    
    if (a[i] == num && remv_idx == -1)
      remv_idx = i;
      
    if (remv_idx != -1 && remv_idx < i && a[i] != num)
      a[remv_idx++] = a[i];
  }
  
  arr->length -= count;
  arr->capacity = arr->length;
  arr->a = realloc(a, arr->capacity * sizeof(int));
}

void display(char *label, Array *arr)
{
  printf("%s", label);
  
  for (size_t i = 0; i < arr->length; ++i)
    printf("%d ", arr->a[i]);
    
  printf("\n");
}

void destroy(Array *arr)
{
  free(arr->a);
  free(arr);
}

Here I did not consider any new array but removed the elements in place. I'm keeping both of my solution because you might not need the 2nd one if your input space is small. One more thing, since the question did not mention about any reallocations failures so I did not check it in my code.

Tsubasa
  • 1,389
  • 11
  • 21
  • how would this work for an array of a million entries? that's a massive reallocation. – Ahmed Masud Jan 05 '22 at 18:49
  • 1
    `sizeof(size_t)` for an array of objects of `int` size? This is a good example of why `sizeof *arr * (...)` is generally preferred. – Oka Jan 05 '22 at 18:55
  • @Ahmed Masud then we can reallocate by doubling the size of the current array. This might solve the problem. – Tsubasa Jan 05 '22 at 18:55
  • @Oka you're right. Fixed it – Tsubasa Jan 05 '22 at 18:57
  • Nezuko, curious, why use `sizeof(int)` vs. [@Oka suggested](https://stackoverflow.com/questions/70597413/function-that-remove-an-integer-from-an-array-using-pointer-in-c#comment124799933_70598035) `sizeof *arr`? `sizeof(type)` is harder to review. ` – chux - Reinstate Monica Jan 05 '22 at 20:24
  • In my first code snippet, that was not really possible because arr points to `NULL` while you're inserting the value for the first time. If I were to do that then I would have to explicitly check for that condition. In my second code snippet however, even though I could have done what Oka suggested but I wanted to be explicit so I went for `sizeof int`. – Tsubasa Jan 06 '22 at 05:01
  • @Nezuko The [`sizeof`](https://en.cppreference.com/w/c/language/sizeof) operator is almost always a compile time construct, and its operand expression is not evaluated at runtime ([VLAs](https://en.cppreference.com/w/c/language/array#Variable-length_arrays) are an exception). See: [example](https://godbolt.org/z/1aj7acrs7), it does not matter what the pointer value is, as you are not actually dereferencing it. This form is generally preferred, because if the type of the object is changed the calculation remains correct. – Oka Jan 10 '22 at 02:38
0

Here is an approach:


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void print_arr(int *arr, size_t size);

size_t remove_by_value(int *arr, size_t len, int value)
{
  int count = 0; // maintain how many times we see value
  int k;
  for(k = 0; k < len ; k++) {
      if ( arr[k] == value ) {
          while(arr[count+k] == value) {
              count++;
          } // skip over conscutive values
          if ( count + k >= len )
              break;
          arr[k] = arr[k+count];
          arr[k+count] = value;
          print_arr(arr, len);
      }
  }
  return len-count;
}

void print_arr(int *arr, size_t size)
{
  for(int k = 0; k < size; k++) {
    printf("%02d ", arr[k]);
  }
  printf("---\n");
}

int main()
{
  int test_values[] = { 0, 1, 3, 2, 3, 5, 4, 7, 8 };
  size_t len = sizeof(test_values)/sizeof(int);
  int *arr = malloc(len*sizeof(int));
  memcpy(arr, test_values, len*sizeof(int));
  print_arr(arr, len);
  len = remove_by_value(arr, len, 3);
  print_arr(arr, len);
  arr = realloc(arr, len);
  print_arr(arr, sizeof(int)*len);
  return 0;
}

It bubbles the value to be extracted to the end of the array and lops it off. The nice thing is that it doesn't use any extra memory to do its work.

The second part that is that it is NOT O(n^2) I have to think a bit about the complexity of it (seems bigger than O(n))

However it's a simple solution that keeps the order of the array, removes unneeded values simply.

i've put in the print_arr function at each step of the loop so that you can see what's happening.

Hopefully the code is clear in its purpose, if you have questions please comment and I will further explanation.

Notes

  1. I expressly did not use sizeof(*arr) because i wanted it to be a bit more clear as to what it is. In production code one would use sizeof(*arr) instead of sizeof(int) .... However I would not be able to create a consistent example (e.g. remove_by_value would have to be similarly made generic) so I didn't to keep the example simple to understand.
Ahmed Masud
  • 21,655
  • 3
  • 33
  • 58