Eliminando elementos de una matriz en C

10 minutos de lectura

avatar de usuario
Rini Posni

Solo tengo una pregunta simple sobre matrices en C

¿Cuál es la mejor manera de eliminar elementos de una matriz y, en el proceso, hacer que la matriz sea más pequeña?

es decir, la matriz tiene un tamaño n, luego tomo elementos de la matriz y luego la matriz se vuelve más pequeña en la cantidad de la que la eliminé.

Básicamente, estoy tratando la matriz como una baraja de cartas y una vez que tomo una carta de la parte superior de la baraja, ya no debería estar allí.

EDITAR: Me volveré loco antes de que termine el día, gracias por toda la ayuda. Estoy intentando cambiar el valor, pero no funciona bien.

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

enum faces{Ace = 0, Jack = 10, Queen, King};
char * facecheck(int d); 
int draw(int deck, int i); 
int main() 
{ 
    int deck[52], i, n;
    char suits[4][9] = 
    {
        "Hearts",
        "Diamonds",
        "Clubs",
        "Spades"};


    n = 0;

    for(i = 0; i<52; i++)
    {
        deck[i] = n;
        n++;
     };



    for(i=0; i<52; i++)
    {       
        if(i%13 == 0 || i%13 == 10 || i%13 == 11 || i%13 == 12)
            printf("%s ", facecheck(i%13) );
        else printf("%d ", i%13+1);
        printf("of %s \n", suits[i/13]);
    }

    draw(deck, i);


    return 0; 
}  

char * facecheck(int d)
{
    static char * face[] = 
    {
        "Ace",
        "Jack",
        "Queen",
        "King" };

    if(d == Ace)
        return face[0];
    else
    {
        if(d == Jack) 
            return face[1];
        else
        {
            if(d == Queen)
                return face[2];
            else 
            { 
                if(d == King)
                    return face[3];
            }
        }
    } 
}

int draw(int deck,int i ) 
{ 
    int hand[5], j, temp[j];

    for(i=0; i<52; i++)
    {
             j = i
             }; 

    for(i = 0; i < 5; i++)
    {
          deck[i] = hand[]; 
          printf("A card has been drawn \n");
          deck[i] = temp[j-1];
          temp[j] = deck[i];
          };

          return deck;
}

  • Si realmente desea que ‘se haga más pequeño’, implemente una lista vinculada. De lo contrario, realice un intercambio de valores y mantenga un valor de protección de ‘fin de matriz’ en su matriz. No puede simplemente eliminar un elemento de una matriz regular

    –Ron Dahlgren

    04/04/2013 a las 20:32

  • La compensación de rendimiento es terrible si va a reasignar o memcpy cada vez que cambia su matriz.

    -Joe Frambach

    04/04/2013 a las 20:33

  • Ignora a esos detractores: memcpy es el camino a seguir. (Aunque si realmente está tomando “una carta de la parte superior”, simplemente use una pila simple: haga que la “parte superior” sea el final de la matriz y disminuya un valor que indique su longitud con cada “pop” de la “pila “.)

    – Lame caliente

    04/04/2013 a las 20:36

  • @JoeFrambach memcpy() no funciona debido a la superposición de la copia de memoria. La forma correcta es usar memmove() .

    – KRoy

    24 de febrero de 2016 a las 6:10

Realmente hay dos problemas separados. El primero es mantener los elementos de la matriz en el orden correcto para que no haya “agujeros” después de eliminar un elemento. El segundo es en realidad cambiar el tamaño de la matriz en sí.

Las matrices en C se asignan como un número fijo de elementos contiguos. No hay forma de eliminar realmente la memoria utilizada por un elemento individual en la matriz, pero los elementos se pueden desplazar para llenar el hueco creado al eliminar un elemento. Por ejemplo:

void remove_element(array_type *array, int index, int array_length)
{
   int i;
   for(i = index; i < array_length - 1; i++) array[i] = array[i + 1];
}

Las matrices asignadas estáticamente no se pueden cambiar de tamaño. Las matrices asignadas dinámicamente se pueden cambiar de tamaño con realloc(). Esto moverá potencialmente toda la matriz a otra ubicación en la memoria, por lo que todos los punteros a la matriz o a sus elementos deberán actualizarse. Por ejemplo:

remove_element(array, index, array_length);  /* First shift the elements, then reallocate */
array_type *tmp = realloc(array, (array_length - 1) * sizeof(array_type) );
if (tmp == NULL && array_length > 1) {
   /* No memory available */
   exit(EXIT_FAILURE);
}
array_length = array_length - 1;
array = tmp;

realloc devolverá un puntero NULL si el tamaño solicitado es 0 o si hay un error. De lo contrario, devuelve un puntero a la matriz reasignada. El puntero temporal se usa para detectar errores al llamar a realloc porque en lugar de salir, también es posible dejar la matriz original como estaba. Cuando realloc no puede reasignar una matriz, no altera la matriz original.

Tenga en cuenta que ambas operaciones serán bastante lentas si la matriz es grande o si se eliminan muchos elementos. Hay otras estructuras de datos, como listas vinculadas y hashes, que se pueden usar si la inserción y eliminación eficientes es una prioridad.

  • En el primer ejemplo, la longitud de la matriz habrá cambiado debido a la eliminación, por lo que parece justo pasar array_length también como un puntero, ¿no? Entonces se convierte en (… int *array_length) y al final del cuerpo de la función: (*array_length)–;

    – de eddie

    28 de diciembre de 2020 a las 10:16


  • ¿Qué sucede con la dirección del artículo que se eliminó? ¿Se obtiene automáticamente? free()‘¿D? Específicamente si estamos eliminando un elemento que es una referencia a un objeto.

    – mazunki

    21 de marzo de 2021 a las 6:09

avatar de usuario
Chris Farmiloe

Realmente no desea reasignar memoria cada vez que elimina algo. Si conoce el tamaño aproximado de su plataforma luego elija un tamaño apropiado para su matriz y mantenga un puntero en el actual final de la lista Esto es un apilar.

Si no sabe el tamaño de su mazo y cree que podría crecer mucho y seguir cambiando de tamaño, entonces tendrá que hacer algo un poco más complejo e implementar un lista enlazada.

En C, tiene dos formas simples de declarar una matriz.

  1. En la pila, como una matriz estática

    int myArray[16]; // Static array of 16 integers
    
  2. En el montón, como una matriz asignada dinámicamente

    // Dynamically allocated array of 16 integers
    int* myArray = calloc(16, sizeof(int));
    

El estándar C no permite cambiar el tamaño de las matrices de ninguno de estos tipos. Puede crear una nueva matriz de un tamaño específico y luego copiar el contenido de la matriz anterior a la nueva, o puede seguir una de las sugerencias anteriores para un tipo de datos abstracto diferente (es decir, lista vinculada, pila, cola, etc.).

  • ¿Cómo implementaría algo usando un puntero, digamos que la matriz es una baraja de cartas, para que sepa el valor (52) y tal? Tengo problemas para encontrar el código para hacer esto, he estado devanándome el cerebro durante días tratando de resolver un montón de cosas para que mi cerebro esté codificado, por eso necesito ayuda.

    –Rini Posny

    04/04/2013 a las 20:49

  • ¿Puede editar su pregunta para incluir parte de su código e intentos hasta ahora? Una matriz es algo muy simple de crear. Más detalles aclararían las cosas.

    – Nube

    04/04/2013 a las 21:22

  • El estándar C permite cambiar el tamaño de las matrices asignadas dinámicamente, con realloc(). Ahora podría decir que realloc() copia, pero no lo hace (crecerá/reducirá en su lugar cuando sea posible, o usará memmove en lugar de memcpy cuando realmente necesite mover la matriz).

    – ceso

    7 de agosto de 2020 a las 21:18

avatar de usuario
KRoy

Curiosamente, el índice puede acceder aleatoriamente a la matriz. Y la eliminación aleatoria de un elemento también puede afectar los índices de otros elementos.

    int remove_element(int*from, int total, int index) {
            if((total - index - 1) > 0) {
                      memmove(from+i, from+i+1, sizeof(int)*(total-index-1));
            }
            return total-1; // return the new array size
    }

Tenga en cuenta que memcpy no funcionará en este caso debido a la superposición de memoria.

Una de las formas eficientes (mejor que el movimiento de memoria) para eliminar un elemento aleatorio es intercambiarlo con el último elemento.

    int remove_element(int*from, int total, int index) {
            if(index != (total-1))
                    from[index] = from[total-1];
            return total; // **DO NOT DECREASE** the total here
    }

Pero el orden se cambia después de la eliminación.

Nuevamente, si la eliminación se realiza en la operación de bucle, el reordenamiento puede afectar el procesamiento. El movimiento de memoria es una alternativa costosa para mantener el orden mientras se elimina un elemento de matriz. Otra forma de mantener el orden en un bucle es diferir la eliminación. Se puede hacer por matriz de validez del mismo tamaño.

    int remove_element(int*from, int total, int*is_valid, int index) {
            is_valid[index] = 0;
            return total-1; // return the number of elements
    }

Creará una matriz dispersa. Finalmente, la matriz dispersa se puede hacer compacta (que no contiene dos elementos válidos que contengan elementos no válidos entre ellos) reordenando un poco.

    int sparse_to_compact(int*arr, int total, int*is_valid) {
            int i = 0;
            int last = total - 1;
            // trim the last invalid elements
            for(; last >= 0 && !is_valid[last]; last--); // trim invalid elements from last

            // now we keep swapping the invalid with last valid element
            for(i=0; i < last; i++) {
                    if(is_valid[i])
                            continue;
                    arr[i] = arr[last]; // swap invalid with the last valid
                    last--;
                    for(; last >= 0 && !is_valid[last]; last--); // trim invalid elements
            }
            return last+1; // return the compact length of the array
    }

avatar de usuario
cmaster – reincorporar a monica

La solución que necesita depende de si desea que su matriz conserve su orden o no.

En general, nunca solo tiene el puntero de matriz, también tiene una variable que contiene su tamaño lógico actual, así como una variable que contiene su tamaño asignado. También estoy asumiendo que el removeIndex está dentro de los límites de la matriz. Con eso dado, la eliminación es simple:

Orden irrelevante

array[removeIndex] = array[--logicalSize];

Eso es todo. Simplemente copie el último elemento de la matriz sobre el elemento que se va a eliminar, disminuyendo el logicalSize de la matriz en el proceso.

Si removeIndex == logicalSize-1es decir, se va a eliminar el último elemento, esto se degrada en una autoasignación de ese último elemento, pero eso no es un problema.

orden de retención

memmove(array + removeIndex, array + removeIndex + 1, (--logicalSize - removeIndex)*sizeof(*array));

Un poco más complejo, porque ahora tenemos que llamar memmove() para realizar el cambio de elementos, pero sigue siendo una sola línea. Una vez más, esto también actualiza la logicalSize de la matriz en el proceso.

avatar de usuario
Liaquat Hussain

Prueba este sencillo código:

#include <stdio.h>
#include <conio.h>
void main(void)
{ 
    clrscr();
    int a[4], i, b;
    printf("enter nos ");
    for (i = 1; i <= 5; i++) {
        scanf("%d", &a[i]);
    }
    for(i = 1; i <= 5; i++) {
        printf("\n%d", a[i]);
    }
    printf("\nenter element you want to delete ");
    scanf("%d", &b);
    for (i = 1; i <= 5; i++) {
        if(i == b) {
            a[i] = i++;
        }
        printf("\n%d", a[i]);
    }
    getch();
}

  • Se desaconsejan las respuestas de solo código. Agregue alguna explicación sobre cómo esto resuelve el problema o cómo esto difiere de las respuestas existentes. De la revisión

    – Nick

    8 de febrero de 2020 a las 4:51

  • Este código tiene muchas desventajas y no puede competir por la “mejor manera de eliminar elementos”. Además, este algoritmo es incorrecto, no mueve todos los elementos (solo las copias hacen un duplicado). Por no hablar del comportamiento indefinido en a[i] = i++; y horrible estilo de codificación. Te aconsejo que elimines esta respuesta.

    – Dmitri Kuzminov

    8 de febrero de 2020 a las 6:20


avatar de usuario
werner henze

Yo suelo hacer esto y funciona siempre.


/prueba esto/

for (i = res; i < *size-1; i++) { 

    arrb[i] = arrb[i + 1];
}

*size = *size - 1; /*in some ides size -- could give problems*/

  • Se desaconsejan las respuestas de solo código. Agregue alguna explicación sobre cómo esto resuelve el problema o cómo esto difiere de las respuestas existentes. De la revisión

    – Nick

    8 de febrero de 2020 a las 4:51

  • Este código tiene muchas desventajas y no puede competir por la “mejor manera de eliminar elementos”. Además, este algoritmo es incorrecto, no mueve todos los elementos (solo las copias hacen un duplicado). Por no hablar del comportamiento indefinido en a[i] = i++; y horrible estilo de codificación. Te aconsejo que elimines esta respuesta.

    – Dmitri Kuzminov

    8 de febrero de 2020 a las 6:20


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad