Ecco il codice per deframmentare la matrice.
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
}
Ho copiato il codice this risposta.
Penso che il modo più efficiente sia utilizzare un elenco di collegamenti di bucket. E i bucket sono gestiti da un gestore di memoria bit-string. E 'come il seguente,
struct elem {
uint32_t index; /* helper to locate it's position in the array */
int x; /* The content/object kept in the array */
}
Supponiamo, i contenuti in array è int
ed è incapsulato in una struttura di nome struct elem
.
enum {
MAX_BUCKET_SIZE = 1024,
MAX_BITMASK_SIZE = (MAX_BUCKET_SIZE + 63) >> 6,
};
struct bucket {
struct bucket*next; /* link to the next bucket */
uint64_t usage[MAX_BITMASK_SIZE]; /* track memory usage */
struct elem[MAX_BUCKET_SIZE]; /* the array */
};
Un secchio è definito come un array di struct elem
e maschera utilizzo.
struct bucket_list {
struct bucket*head; /* dynamically allocated bucket */
}container;
E un elenco di bucket è un elenco collegato contenente tutti i bucket.
Quindi abbiamo bisogno di scrivere il codice gestore della memoria.
All'inizio abbiamo bisogno di un nuovo bucket da allocare quando necessario.
struct bucket*bk = get_empty_bucket(&container);
if(!bk) { /* no empty bucket */
/* allocate a bucket */
struct bucket*bk = (struct bucket*)malloc(sizeof(struct bucket));
assert(bk);
/* cleanup the usage flag */
memset(bk->usage, 0, sizeof(bk->usage));
/* link the bucket */
bk->next = container.head;
container.head = bk;
}
Ora come abbiamo il bucket, è necessario impostare il valore nell'array quando necessario.
for(i = 0; i < MAX_BITMASK_SIZE; i++) {
uint64_t bits = ~bk.usage[i];
if(!bits) continue; /* no space */
/* get the next empty position */
int bit_index = _builtin_ctzl(bits);
int index = (i<<6)+bit_index;
/* set the array value */
bk->elem[index].index = index;
bk->elem[index].x = 34/* my value */;
bk.usage[i] |= 1<<bit_index; /* mark/flag the array element as used */
}
Eliminare gli elementi di matrice è facile da contrassegnarli inutilizzati. Ora, quando tutti gli elementi di un bucket non sono utilizzati, è possibile eliminare il bucket dall'elenco dei collegamenti.
A volte possiamo deframmentare i bucket o ottimizzarli per adattarli a spazi più piccoli. Altrimenti, quando assegniamo nuovi elementi, possiamo selezionare secchi più affollati su uno meno affollato. Quando cancelliamo possiamo scambiare l'elemento di quello meno affollato in quello più affollato.
È possibile eliminare elementi di matrice in modo efficiente,
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
}
Si è fatto scambiando l'elemento con l'ultimo valore.
Non capisco l'esempio. – Boiethios
A meno che non abbia letto male l'esempio di codice, x non è utilizzato in nessun punto nell'altro ciclo o nell'indicizzazione dell'array, quindi qual è il punto? – OldProgrammer
Cosa vuoi fare? È necessario cancellare i dati in alcuni elementi dell'array o ridurre la memoria eliminando definitivamente i blocchi indesiderati? –