我在C中有一个常规的结构数组,每秒运行一次并更新结构中的所有数据。满足条件时,其中一个元素将被清除,并用作新元素(在本例中为定时器)的空闲插槽,可以随时进入。
我所做的只是解析数组的所有元素,寻找需要更新的活动元素。但即使元素的数量很少(<2000),我觉得这会浪费时间来处理不活跃的元素。有没有办法让数组无间隙,所以我只需要迭代当前分配的元素数量?
答案 0 :(得分:2)
假设元素的特定顺序无关紧要,可以很容易地完成。
如果您拥有数组A
和活动元素数N
,则可以添加如下元素E
:
A[N++] = E;
并删除索引为I
的元素,如下所示:
A[I] = A[--N];
那么这是如何工作的?嗯,这很简单。我们希望数组只存储活动元素,因此我们可以假设当我们开始执行这些操作时,数组就像那样。
添加一个元素总是把它放在最后,因为当前在数组中的所有元素以及新添加的元素都是活动的,我们可以安全地添加一个元素到最后。
移除元素是通过移动最后一个元素来接管我们要删除的元素的数组索引来完成的。因此,A[0..I-1]
处于有效状态,A[I+1..N]
处于活跃状态,通过将A[N]
移至A[I]
,整个范围A[0..N-1]
处于有效状态(A[N]
不活动,因为它不再存在 - 我们将其移至A[I]
,这就是我们将N减少1的原因。
如果您在迭代元素的同时删除元素以更新它们,请注意您只能在处理不被删除的元素后递增循环计数器,否则,您将永远不会处理移动的元素。
答案 1 :(得分:0)
实现此目的的一种相对简单的方法:
void remove(struct foo *foo_array, int *n)
{
struct foo *src = foo_array, *dst = foo_array;
int num_removed = 0;
for (int i=0; i<*n; ++i)
{
// Do we want to remove this? (should_remove() left as exercise for reader.)
if (should_remove(src))
{
// yes, remove; advance src without advancing dst
++src;
++num_removed;
}
else if (src != dst)
{
// advance src and dst (with copy)
*dst++ = *src++;
}
else
{
// advance both pointers (no copy)
++src;
++dst;
}
}
// update size of array
*n -= num_removed;
}
这个想法是你跟踪数组中有多少元素是有效的(这里是*n
),并将其指针作为“输入/输出参数”传递。 remove()
决定删除哪些元素并复制不合适的元素。请注意,无论决定删除多少元素,这都是O(n)。
答案 2 :(得分:0)
每秒遍历2,000个条目可以忽略不计。这真的不值得优化。如果您确实认为必须,请将非活动条目交换为最后一个活动条目。
答案 3 :(得分:0)
如何在结构中添加链表行为,即指向下一个活动元素的指针成员?
您必须在元素激活和停用时更新这些指针。
编辑:此方法不适用于动态调整大小的数组,因为这可能会更改内存对象的地址,使列表使用的指针无效..
答案 4 :(得分:0)
听起来你没有理由不使用链表。如果你很好地执行实现,你将获得O(1)插入,O(1)删除,你只需要保持(和迭代)活动结构。有一些内存开销......即使是中等大小的结构,即使是一个双向链表也会非常高效。这种方法的好处在于,您可以将元素保留在其插入顺序中,而无需额外的计算开销。
答案 5 :(得分:0)
可以考虑一些替代方案,根据您的需求进行选择:
1)保持原样,除非您遇到一些性能问题或需要扩展。
2)向每个结构添加一个“next”指针,将其用作双向链表中的元素。保留两个列表,一个列表用于活动列表,另一个列表用于未使用的列表。根据您使用struts的方式,还可以考虑使列表双向链接。 (如果需要索引结构,也可以在数组中包含元素,否则可以停止使用数组。)
3)如果您不需要数组中结构的索引(或顺序)为常量,请将未使用的条目移动到数组的末尾。然后,当您从头开始遍历数组时,无论何时到达第一个未使用的数组,都可以停止。 (您可以存储最后一个活动结构的索引,这样无论何时停用结构,您都可以让它切换最后一个活动结构的位置,然后递减最后一个活动结构的索引。)