【问题标题】:Void function: removing elements from an array?void 函数:从数组中删除元素?
【发布时间】:2016-12-05 21:53:58
【问题描述】:

我想做一个从数组中删除元素的函数。

我尝试自己制作该功能,但并未完全成功:

void remove_element(int* array, int number, int array_length)
{
    int i, j;

    for (i = 0; i < array_length; i++){
        if (array[i] == number) {
            for (j = i; j < array_length; j++)
                array[j] = array[j+1];
            array_length--;
        }
    }
}

我注意到的第一件事是在 main 中调用函数后,array_lenght 没有改变,因此删除元素后数组的长度保持不变。

那么第一个问题是如何在void函数中改变数组的长度?

不能正常工作的第二件事是如果数组中有两个或多个相同的数字彼此相邻,例如,如果数组包含1,1,3,4,5,6 并且用户想要删除1 函数将只删除一个元素。

我的输出示例(错误):

1,1,3,4,5,6 after fucntion 1,3,4,5,6

谢谢

【问题讨论】:

  • array_lengthremove_element 的本地。但是,您在函数内部进行操作将不会在函数外部可见。如果您希望其操作在函数范围之外持续存在,则需要传入指向 array_length 的指针。
  • 对于问题 #2:if(array[i]==number){ => while(array[i]==number){
  • j&lt;array_length --> j&lt;array_length-1
  • 可以使用返回值作为数组的新长度:array_length = remove_element(A, number, array_length);或通过其地址remove_element(A, number, &amp;array_length)
  • 有多少相同的数字并不重要,在上述情况下,如果用户想要删除 1,则应删除所有 1 。由于让-弗朗索瓦·法布尔(Jean-François Fabre)解决了这个问题,但仍然无法绕过长度......

标签: c arrays


【解决方案1】:

在当前签名中不能修改数组,因为返回新数组还需要返回新长度。

有很多方法可以做到,这里有一个单循环的方法:

void
remove_element(int* array, int number, int *array_length)
{
    int i, j;

    for (i = 0, j=0; i < *array_length; i++) {
        if (array[i] != number)
            array[j++] = array[i];
    }
    *array_length = j;
}

这样做,这将改变初始数组和在调用者中保持其长度的变量,您必须通过引用传递 array_length 的值,类似于

int* some_array, some_array_length, n;

remove_element(some_array, n, &some_array_length)

其他方法是返回新长度并改变数组或在堆中分配一个新数组并返回分配的数组和新长度。

【讨论】:

    【解决方案2】:

    除了您的问题的其他答案之外,如果您可能平均执行多次删除(或者实际上每次调用不止一次),最好不要一次又一次地移动所有后续值。试试这个:

    void remove_element(int *array, int number, int *array_length) {
        for(int iRead = 0, iWrite = 0; iRead < *array_length; iRead++) {
            if(array[iRead] == number)
                continue; /* skip without increasing iWrite */
            if(iWrite < iRead) /* it works without this line, too, what's better depends */
                array[iWrite] = array[iRead];
            iWrite++;
        }
        *array_length = iWrite;
    }
    

    这是如何工作的,例如,array = {1,1,2,2,3}number = 2

    1 1 2 2 3
    ^ iRead
    ^ iWrite
    (increases both, writes nothing)
    
    1 1 2 2 3
      ^ iRead
      ^ iWrite
    (increases both, writes nothing)
    
    1 1 2 2 3
        ^ iRead
        ^ iWrite
    (continue happens: increases only iRead)
    
    1 1 2 2 3
          ^ iRead
        ^ iWrite
    (continue happens: increases only iRead)
    
    1 1 2 2 3
            ^ iRead
        ^ iWrite
    (writes 3 to iWrite, increases both)
    
    1 1 3 2 3
              ^ iRead = 5
          ^ iWrite = 3
    (iRead is no longer < *array_length, loop stops, 3 == iWrite is the new length)
    

    【讨论】:

      【解决方案3】:

      现在根据您的评论,我会这样做:

      #include <stdio.h>
      
      void remove_element(int number, int *arr, size_t *size);
      
      int main(void){
          int arr[] = {1,1,3,4,5,6};
      
          long unsigned int arrSize;
          int number = 1;
          arrSize = sizeof arr / sizeof arr[0];
      
          printf("Before:\n");
          for ( size_t i = 0 ; i < arrSize ; i++ ){
              printf("%d ", arr[i]);
          }
      
          printf("\nAfter:\n");
          remove_element( number, arr, &arrSize );
      
          for ( size_t j = 0 ; j < arrSize ; j++ ){
              printf("%d ", arr[j]);
          }
          printf("\n");
      }
      
      void remove_element(int number, int *arr, size_t *size){
          int arrTemp[*size];
          long unsigned int j = 0,c;
      
          for ( size_t i = 0 ; i < *size ; i++ ){
              if ( arr[i] == number ){
                      continue;
              }else{
                  arrTemp[j] = arr[i];
                  j++;
              }
          }
      
          for ( size_t k = 0 ; k < j ; k++ ){
              arr[k] = arrTemp[k];
          }
      
          c = j;
          while ( *size > c ){
              arr[c] = 0;
              c++;
          }
      
          *size = j;
      }
      

      输出:

      Before:
      1 1 3 4 5 6 
      After:
      3 4 5 6
      

      顺便说一句,您在问题中说:

      Examples of output:
      
      1,1,3,4,5,6 after fucntion 1,3,4,5,6
      

      在你的评论中你说:

      It doesn't matter how many same numbers there are, in that case above if user wants to remove 1, all 1 's should be removed. 
      

      请下定决心。

      【讨论】:

      • 这是我输出的一个例子,它是错误的,我编辑了它,我测试了你的代码,根据我的喜好修改它并且它有效,它比其他代码更容易理解。谢谢
      • @Coolermaster 如果有帮助,请接受我的回答。
      【解决方案4】:

      因为 array_length 没有作为指针传递,所以你在堆栈上拥有它的副本,所以在你离开这个函数之后,你仍然拥有旧值。找到相同的数字后,您可以在此处使用 memmove 或 memcpy 而不是对整个数组进行迭代。您没有删除数组中的下一个 1 的原因是,当您在 i = 0 -> array[0] == 1 上时,但是当您将所有数字移回时,您将第二个 1 移到array[0],之后不检查它,因为在你完成迭代后你只是增加 i,而不检查你移动到 array[i] 的当前值是否与前一个相同。

      void remove_element(int *array, int number, int *array_length){
          int sameValues = 0;
          for(int i = 0; i < *array_length; i++){
              while(array[i] == number){
                  *array_length--;
                  sameValues++;
              }
              if(sameValues > 0){
                  if(i < *array_length){
                      memmove(&array[i], &array[i + sameValues], (*array_length - i) * sizeof(int));
                  }
                  sameValues = 0;
              }
          }
      }
      

      为了使其更加动态,您可以使用 realloc (并在删除成员后实际调整数组占用的空间,但是您需要返回新指针,或者您需要指向堆栈上的指针调用函数:

      void remove_element(int **array, int number, int *array_length){
          int sameValues = 0;
          for(int i = 0; i < *array_length; i++){
              while((*array)[i] == number){
                  *array_length--;
                  sameValues++;
              }
              if(sameValues > 0){
                  if(i < *array_length){
                      memmove(&(*array)[i], &(*array)[i + sameValues], (*array_length - i) * sizeof(int));
                  }
                  realloc(*array, *array_length);
                  sameValues = 0;
              }
          }
      }
      

      附:要使用 memmove,您需要包含 string.h

      【讨论】:

        【解决方案5】:

        当您声明一个数组时,编译器会影响一定数量的内存,而对于数组结构,您无法更改它。有 2 个解决方案,更改您的 array length 但您仍然可以访问最后一个“框”或重新定义所需大小的数组。 第一种情况:

        void remove_element(int* array, int number, int * array_length){
        
        int i = 0,j = 0;
        
        while( i < *(array_length)-1){
            if(array[i] == number && !find){
                i++; //we step over
                find = 1; //we find 1 element stop remove
            }
            array[j] = array[i];
            i++;
            j++;   
        }
        *(array_length)--;
        }
        

        int * array_length 是指向内存的指针,因此当您返回 main 时,您的更改会保留。在您的示例中,remove_element array_length 之前等于 6,之后等于 5。但是您仍然可以执行 array[5] (第 6 个元素导致 c 中的符号 0 到 5)。

        第二种情况

        int * remove_element(int * array, int number, int * array_length){
        
        int * new_array = malloc((array_length -1) * sizeof(int)); // len - 1 cause we want to delet only 1 element
        
        int i = 0;
        int j = i;
        int find = 0;
        while( i < *(array_length)-1){
            if(array[i] == number && !find){
                i++; //we step over
                find = 1; //we find 1 element stop remove
            }
            new_array[j] = array[i];
            i++;
            j++;   
        }
        if (array[array_length] != number && !find){ //if we didn't find att all the element return array
            return array;
        } else {
            *(array_length)--;
            return new_array;
        }
        }
        

        这里你必须返回 new_array 因为你 main 中的 int array[6] 是一个常量:你不能用 array = new_array 改变它。

        编辑:是的,当然,当您不再需要数组时,您必须使用 free(array) 释放它(或者它会在进程结束时自动完成,但这不是正确的代码)

        还有一个更短的方法,可以添加指针,但你的 c lvl 似乎太复杂了:)

        【讨论】:

        • 这里要记得释放数组,否则会造成内存泄漏。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-31
        • 2015-06-12
        • 1970-01-01
        • 2020-04-28
        • 2022-01-13
        • 2016-03-08
        相关资源
        最近更新 更多