这是对数组进行碎片整理的代码。
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
}
我从this 答案中复制了代码。
我认为更有效的方法是使用存储桶的链接列表。桶由位串内存管理器管理。是这样的,
struct elem {
uint32_t index; /* helper to locate it's position in the array */
int x; /* The content/object kept in the array */
}
假设,我们的数组内容是int,它被封装在一个名为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 */
};
存储桶定义为struct elem 和使用掩码的数组。
struct bucket_list {
struct bucket*head; /* dynamically allocated bucket */
}container;
而桶列表是一个包含所有桶的链表。
所以我们需要编写内存管理器代码。
首先我们需要在需要时分配新的桶。
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;
}
现在我们有了桶,需要在需要时设置数组中的值。
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 */
}
删除数组元素很容易,因为将它们标记为未使用。现在,当桶中的所有元素都未使用时,我们可以从链接列表中删除桶。
我们有时可以对存储桶进行碎片整理或优化它们以适应更小的空间。否则,当我们分配新元素时,我们可以选择更拥挤的桶而不是拥挤的桶。当我们删除时,我们可以将不太拥挤的元素交换为更拥挤的元素。
可以有效地删除数组元素,
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
}
这是通过将元素与最后一个值交换来完成的。