web-dev-qa-db-fra.com

Suppression d'éléments d'un tableau en C

J'ai juste une question simple sur les tableaux en C

Quelle est la meilleure façon de supprimer des éléments d'un tableau et, ce faisant, de réduire le tableau.

c'est-à-dire que le tableau est de taille n, alors je retire des éléments du tableau, puis le tableau devient plus petit de la quantité dont je l'ai retiré.

en gros, je traite le tableau comme un jeu de cartes et une fois que j'ai retiré une carte du dessus du jeu, elle ne devrait plus être là.

EDIT: Je vais me rendre fou avant la fin de la journée, merci pour toute l'aide que j'essaie de changer de valeur, mais cela ne fonctionne pas correctement.

#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;
}
20
Rini Posny

Il y a vraiment deux problèmes distincts. La première consiste à garder les éléments du tableau dans le bon ordre afin qu'il n'y ait pas de "trous" après avoir supprimé un élément. La seconde consiste à redimensionner le tableau lui-même.

Les tableaux en C sont alloués en tant que nombre fixe d'éléments contigus. Il n'y a aucun moyen de supprimer réellement la mémoire utilisée par un élément individuel dans le tableau, mais les éléments peuvent être déplacés pour combler le trou fait en supprimant un élément. Par exemple:

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];
}

Les tableaux alloués statiquement ne peuvent pas être redimensionnés. Les tableaux alloués dynamiquement peuvent être redimensionnés avec realloc (). Cela déplacera potentiellement l'ensemble du tableau vers un autre emplacement en mémoire, donc tous les pointeurs vers le tableau ou ses éléments devront être mis à jour. Par exemple:

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 retournera un pointeur NULL si la taille demandée est 0, ou s'il y a une erreur. Sinon, il renvoie un pointeur sur le tableau réaffecté. Le pointeur temporaire est utilisé pour détecter les erreurs lors de l'appel à realloc, car au lieu de quitter, il est également possible de laisser simplement le tableau d'origine tel qu'il était. Lorsque realloc ne parvient pas à réallouer un tableau, il ne modifie pas le tableau d'origine.

Notez que ces deux opérations seront assez lentes si le tableau est volumineux ou si de nombreux éléments sont supprimés. Il existe d'autres structures de données comme les listes chaînées et les hachages qui peuvent être utilisées si l'insertion et la suppression efficaces sont une priorité.

12
Ben

Vous ne voulez pas vraiment réallouer la mémoire chaque fois que vous supprimez quelque chose. Si vous connaissez la taille approximative de votre deck , choisissez une taille appropriée pour votre tableau et gardez un pointeur sur le courant fin de la liste. Il s'agit d'un pile .

Si vous ne connaissez pas la taille de votre deck et que vous pensez qu'il peut devenir très gros tout en continuant à changer de taille, alors vous devrez faire quelque chose d'un peu plus complexe et implémenter une liste de liens .

En C, vous disposez de deux méthodes simples pour déclarer un tableau.

  1. Sur la pile, en tant que tableau statique

    int myArray[16]; // Static array of 16 integers
    
  2. Sur le tas, en tant que tableau alloué dynamiquement

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

La norme C ne permet pas de redimensionner des tableaux de ces deux types. Vous pouvez soit créer un nouveau tableau d'une taille spécifique, puis copier le contenu de l'ancien tableau dans le nouveau, ou vous pouvez suivre l'une des suggestions ci-dessus pour un type de données abstrait différent (par exemple: liste liée, pile, file d'attente, etc).

7
Chris Farmiloe

Fait intéressant, le tableau est accessible au hasard par l'index. Et la suppression aléatoire d'un élément peut également avoir un impact sur les index d'autres éléments.

    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
    }

Notez que memcpy ne fonctionnera pas dans ce cas en raison du chevauchement de la mémoire.

L'un des moyens efficaces (mieux que le déplacement de mémoire) pour supprimer un élément aléatoire est de permuter avec le dernier élément.

    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
    }

Mais l'ordre est modifié après le retrait.

Encore une fois, si le retrait est effectué en boucle, le réarrangement peut avoir un impact sur le traitement. Le déplacement de mémoire est une alternative coûteuse pour conserver l'ordre tout en supprimant un élément de tableau. Une autre façon de conserver la commande en boucle est de différer la suppression. Cela peut être fait par un tableau de validité de la même taille.

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

Il créera un tableau clairsemé. Enfin, le tableau clairsemé peut être rendu compact (qui ne contient pas deux éléments valides contenant des éléments invalides entre eux) en effectuant un réarrangement.

    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
    }
3
shuva