Algorithm 有没有办法减少此队列的内存使用?
在C中:Algorithm 有没有办法减少此队列的内存使用?,algorithm,optimization,data-structures,queue,Algorithm,Optimization,Data Structures,Queue,在C中: /* i'm using this */ struct QueueItem { QueueItem* next; void* data; } struct Queue { QueueItem* head; QueueItem* tail; } /* +---+ +---+ |0 0| |* *| len 1 +---+ +|-|+ v v
/* i'm using this */
struct QueueItem
{
QueueItem* next;
void* data;
}
struct Queue
{
QueueItem* head;
QueueItem* tail;
}
/*
+---+ +---+
|0 0| |* *| len 1
+---+ +|-|+
v v
len +---+
0 |d 0|
+---+
+---+
|* *---------...---+
+|--+ | len n
v v
+---+ +---+ +---+
|a *-->|b *--...->|z 0|
+---+ +---+ +---+
*/
这为所有推送/弹出/窥视和O(n)遍历提供了O(1),但使用了2+2n内存。
一个朴素的数组版本给出的最小值为2+n(约为最优值),但通常是
更糟糕的是,有时会导致修改花费更长的时间(用于重新分配)
看起来没有办法通过牺牲性能来提高内存使用率,但我想至少把它放在那里,以防有人知道如何解决这个问题
编辑(因为我不能在注释中包含代码):
首先,我认为您忽略了分配队列项目的成本。因此,两种方法之间的分配成本是相同的。(如果有人对内存分配更了解,请说出我的错误。) 您可以这样做,以减少阵列中弹出/未填充项的浪费空间。将列表和数组混合使用。让每个列表节点包含一个大小为K的数组
struct QueueItem
{
QueueItem* next;
void (*data)[K];
}
struct Queue
{
QueueItem* head;
QueueItem* tail;
size_t head;
size_t end;
}
现在,性能取决于为阵列选择的大小。它越小,浪费的空间就越少。它越大,QueueItem开销占总空间的百分比就越小。如果您知道,比如说,队列的大小通常为N,那么您可以选择K作为N/10。在这种情况下,总内存开销为N/K+4+N。在未使用的元素上,数组中可以浪费的最大空间量为2*K-2
使用模式将决定实际性能。但是,如果您可以预测您的使用模式,那么您可以选择一个运行良好的K。甚至可能有一种方法可以为每个节点自适应地选择不同的K,以获得更好的性能,但我认为这是我目前无法做到的。首先,我认为您忽略了分配队列项的成本。因此,两种方法之间的分配成本是相同的。(如果有人对内存分配更了解,请说出我的错误。) 您可以这样做,以减少阵列中弹出/未填充项的浪费空间。将列表和数组混合使用。让每个列表节点包含一个大小为K的数组
struct QueueItem
{
QueueItem* next;
void (*data)[K];
}
struct Queue
{
QueueItem* head;
QueueItem* tail;
size_t head;
size_t end;
}
现在,性能取决于为阵列选择的大小。它越小,浪费的空间就越少。它越大,QueueItem开销占总空间的百分比就越小。如果您知道,比如说,队列的大小通常为N,那么您可以选择K作为N/10。在这种情况下,总内存开销为N/K+4+N。在未使用的元素上,数组中可以浪费的最大空间量为2*K-2
使用模式将决定实际性能。但是,如果您可以预测您的使用模式,那么您可以选择一个运行良好的K。甚至可能有一种方法可以为每个节点自适应地选择不同的K,以获得更好的性能,但我认为现在我无法做到这一点。如果您的队列具有最大容量,并且您只操纵其前端或尾部,我将使用一个。下图来自链接站点,说明了圆形阵列背后的理念:
(来源:) 引述:
- 队列的后面是从前面顺时针的某个地方
- 为了使一个元素排队,我们顺时针向后移动一个位置,并在该位置写入元素
- 要退出队列,我们只需顺时针向前移动一个位置
- 当我们排队和退队时,队列以顺时针方向移动 应仔细检查是否有空位和空位
使用这种数据结构,您显然无法在已存储的两个连续元素之间插入元素,也无法超过某个最大容量-我希望它能满足您的需要。如果您的队列具有最大容量,而您仅操纵其前端或尾部,我将使用。下图来自链接站点,说明了圆形阵列背后的理念:
(来源:) 引述:
- 队列的后面是从前面顺时针的某个地方
- 为了使一个元素排队,我们顺时针向后移动一个位置,并在该位置写入元素
- 要退出队列,我们只需顺时针向前移动一个位置
- 当我们排队和退队时,队列以顺时针方向移动 应仔细检查是否有空位和空位
使用这种数据结构,您显然不能在已存储的两个连续元素之间插入元素,也不能超过某个最大容量-我希望它能满足您的需要。此外,
item->head
不必是每个项目,因为除了q->head
之外,其他所有元素都应该是满的。@David X对注释desync感到抱歉。可能是因为我删除了我的第一个答案。@David X re:item->head
:很好。根据您的编辑,我们还需要一个结尾
。我会编辑。@$re:end,也不需要每个项目都编辑。现在我必须再次修改我的答案编辑。哈,我应该在删除第一个答案之前再等待一次投票。我本可以获得一枚“纪律严明”的徽章另外,item->head
不必是每个项目的,因为除了q->head
之外,其他所有项目都应该是满的。@David X对注释desync表示抱歉。可能是因为我删除了我的第一个答案。@David X re:item->head
:很好。根据您的编辑,我们还需要一个结尾
。我会编辑。@$re:end,也不需要每个项目都编辑。现在我必须再次修改我的答案编辑。哈,我应该在删除第一个答案之前再等待一次投票。我本可以获得一枚“纪律严明”的徽章嗯。在当前的解决方案中,您使用比上一个队列长2倍的数组显示每个新队列项。我不认为这是一个好主意,如果你是弹出项目的队列。现在,你的队列寿命越长,浪费的空间就越多,这与队列在任何时候的大小无关。@cape1232,是的,我仍然在为新结构“让它工作”;稍后我将添加尺寸上限。@$re:尺寸上限,初始尺寸=1怎么样
struct QueueItem
{
QueueItem* next;
void (*data)[K];
}
struct Queue
{
QueueItem* head;
QueueItem* tail;
size_t head;
size_t end;
}