remove all occurrences of a given number from an array in C

Viewed 2860

I'm trying to solve this question :

You've got an array of integers of somewhat SIZE - i.e, arr[SIZE]. Write a function which remove all occurrences of a given number x from an array

I've wrote this function (below) which is suited for a case that the number is of 1 and only occurrence but not for multiple and I'm struggling to find a breakthrough for the last case (multiple occurrences) . My way of thinking was : in case of match - shift left one index all the element which are to the right side of the match and return the user a "new" size.

For the example below : { 3, 0, 5, 6, 6 }; and the number 5 the result will be (and indeed is) : { 3, 0, 6, 6 } , but for : { 3, 0, 5, 5, 6 }, I've got : { 3, 0, 5, 6 }. I know why do I get it but I don't know how to fix it. I tried using a flag which will give the indication of whether I have two adjacent match element and if it is loop again on this index

Can someone please guide me to a solve ?

#include <stdio.h>

#define SIZE    5
int arr[SIZE] = { 3, 0, 5, 6, 6 };

int remove_occurences(int x)
{
    int i, j;
    int removed = 0;

    for (i = 0; i < SIZE; i++)
    {
        if (arr[i] == x)
        {
            removed++;
            for (j = i; j < (SIZE - 1) ;j++)
            {
                arr[j] = arr[j+1];
            }
        }
    }
    return (SIZE - removed);
}

int main()
{
    int new_length;
    int i;

    new_length = remove_occurences(5);
    for (i = 0; i < new_length; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

Thanks

4 Answers

The problem is that after removing the element of the array by shifting all its elements one position backward, the inner loop starts from the iteration i + 1. That is why for:

{ 3, 0, 5, 5, 6 }

you will remove the first 5

{ 3, 0, x, 5, 6 }

shift the elements of the array:

{ 3, 0, 5, 6, x}

but now you should start again at the i position not i+1. Otherwise, you will skip the current second 5. Moreover, you need to adapt the SIZE every time you remove an element, namely:

int remove_occurences(int x){
    int i = 0, j;
    int total_elements = SIZE;
    while (i < total_elements){
        if (arr[i] == x){
            for (j = i; j < (total_elements - 1) ;j++)
                arr[j] = arr[j+1];
             total_elements--; // We have one element less now
        }
        else
           i++; // Only increment if you did not remove elements from the array
    }
    return total_elements;
}

In the worst-case scenario, this algorithm has a time complexity of O(N^2), with N being the size of the array. Nonetheless, there is an algorithm with linear time complexity (i.e., O(N)), as one can see here and on "Nishad C M" answer. For instance:

int remove_occurences(int value_to_remove){
    int i = 0, total_elements = 0;
    for (; i < SIZE; i++)
         if (arr[i] != value_to_remove)
            arr[total_elements++] = arr[i];
    return total_elements;
}

To better understand how this algorithm works, let us look at what would happen with the following example:

int arr[SIZE] = { 3, 5, 0, 9, 5 };

and removing '5'. First iteration:

if (3 != 5)

true, then arr[0] = 3; and total_elements++;

Since the first position of the array already contains 3 it looks kind of redundant to do arr[0] = 3;, however, this will be useful when one finds the value to be removed from the array (i.e., 5).

Second iteration:

if (5 != 5)

false, here the algorithm just skips and moves to the next position of the array. Now the consequence of that is that the variable i will continue to move forward, whereas the variable total_elements will stop at the position that contained the element to be removed. So in the third iteration:

if (0 != 5)

true, then arr[total_elements] = arr[i]; which in this case is arr[1] = arr[2];, and consequently arr[1] = 0;.

Using this approach removes the need (when one finds the element to remove) to do the shift of the elements explicitly, namely:

        for (j = i; j < (total_elements - 1) ;j++)
            arr[j] = arr[j+1];

the shift is done rather implicitly while going through the array.

Some advice regarding your code, one has to consider the following:

  1. The variable SIZE cannot be decremented;
  2. The array will have the same allocated space throughout the application live time, regardless if you removed an element or not.
  3. When one removes an element from an array using your code, one is not actually removing the element but rather shifting all the elements of the array backward, and establishing that the last position of that array will not be used.

Based on the aforementioned points, I would suggest that after having allocated the array, you should not use the constant SIZE any longer because it can easily get out of sync and lead to bugs. Instead, use a variable that will keep track of the current number of elements in the array. Naturally, this would imply changes to your code such as:

int remove_occurences(int size, int value_to_remove)

Otherwise, you cannot safely call the function remove_occurences more than once since the number of elements on the array might have decreased, but the variable SIZE did not reflect that.

    #include<stdio.h>

    #define ARSIZE 5

    int print_array(int * parray, int size) {

       int i = 0;
       if (parray == NULL || size == 0) {
           return 0;
       }
       for (i =0; i<size; i++) {
           printf("%d", parray[i]);
       }
       printf("\n");
       return 0;
    }

    int delete_val(int * parray, int size, int delval) {

       int i = 0;
       int j = 0;
       if (parray == NULL || size == 0) {
           return 0;
       }
       for (i = 0; i< size; i++) {
           if (parray[i] != delval) {
               parray[j] = parray[i];
               j++;
           }
       }
       return j;
    }

    int main () {

        int arr[ARSIZE] = {3, 0, 5, 6, 6};
        int array_size = ARSIZE;
        print_array(arr, array_size);
        array_size = delete_val(arr, array_size, 5);
        print_array(arr, array_size);
        return 0;
    }

When you find the number a lot of elements 5 adjacent, you should decrement i-- every time (to check if exist a number 5 before it)

#include <stdio.h>

#define SIZE 27
int arr[SIZE] = { 3, 0, 5, 5, 5, 5, 6, 6, 0, 9, 5, 5,5, 5,5,5,5,5,55, 5, 5, 7, 5, 5, 0, 9, 5 };

int remove_occurences(int x)
{
  int size=SIZE;
  int removed = 0;
  for (int i = 0; i < size; i++)
  {
    if (x==arr[i])
    {
        removed++;
        for (int j = i+1; j < size ;j++)
        {
            arr[j-1] = arr[j];
        }
        size--;
        i--;
    }

 }
 return SIZE-removed;
}


int main()
{

   int new_length = remove_occurences(5);
   for (int i = 0; i < new_length; i++)
   {
       printf("%d ", arr[i]);
   }
   return 0;
}

Output : 3 0 6 6 0 9 55 7 0 9

You can do that multiple times instead of trying to build a new function. Like this:

#include <stdio.h>

#define SIZE 6
int arr[SIZE] = {3, 0, 5,5, 6, 6};

int is_member(int x)
{
    int i = 0;
    for (i = 0; i < SIZE; i++)
    {
        if (arr[i] == x)
            return 1;
    }
    return 0;
}

int remove_occurences(int x, int length)
{

    int i, j;
    int removed = 0;

    for (i = 0; i < length; i++)
    {
        if (arr[i] == x)
        {
            removed++;
            for (j = i; j < (length - 1); j++)
            {
                arr[j] = arr[j + 1];
            }
        }
    }
    return (length - removed);
}

int main()
{

    int new_length = SIZE;
    int i;
    while (is_member(5))
    {
        new_length = remove_occurences(5, new_length);
    }

    for (i = 0; i < new_length; i++)
    {
        printf("%d ", arr[i]);
    }

    return 0;
}
Related