在自由派C:
/* 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|
+---+ +---+ +---+
*/
这为所有push / pop / peek和O(n)遍历提供O(1),但使用2 + 2n内存。 一个天真的阵列版本给出最少2 + n(约为最佳),但通常是 更糟糕的是,有时会导致修改需要更长时间(重新分配)。
struct Queue
{
size_t len;
size_t head;
void (*data)[];
}
/*
+-----+
|* * *-----~~~-+
+|-|--+ |
v +-+ |
+----v-----~~~-v+
|0 0 a b c . . z|
+----------~~~--+
*/
看起来没有办法通过牺牲性能来提高内存使用率,但我想至少把它放在那里以防万一有人知道解决这个问题。
struct QueueItem
{
QueueItem* next;
size_t len;
void (*data)[];
}
struct Queue
{
QueueItem* head;
QueueItem* tail;
size_t start;
size_t end; //edit: need to know where to push data. edit: not per-item
}
/*
+-----+
|* * *------------------------------+
+|-|--+ |
v +---+ (2x longer) v (much longer)
+------v----+ +-----------+ +---------------+
|@ 0 0 a b *-->|@ c . . h *-->...->|@ . . . x y z 0|
+-----------+ +-----------+ +---------------+
*/
答案 0 :(得分:1)
首先,我认为你忽略了分配QueueItems的成本。因此,两种方法之间的分配成本是相同的。 (有人更了解内存分配,如果我错了,请说出来。)
您可以这样做以减轻阵列中弹出/未填充项目的浪费空间。混合使用列表和数组。让每个列表节点包含一个大小为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以获得更好的性能,但我认为现在超出了我的范围。
答案 1 :(得分:-1)
如果您的队列具有最大容量而您仅操纵其前端或尾部,我将使用Circular Array。以下图片来自链接网站,并说明了圆形阵列背后的想法:
circular array http://lcm.csa.iisc.ernet.in/dsa/img42.gif
引用:
- 队列的后方是从正面
的顺时针方向- 要将元素排入队列,我们顺时针向后移动一个位置并将元素写入该位置
- 要出列队,我们只需顺时针向前移动一个位置
- 当我们入队和出队时,队列以顺时针方向迁移 要仔细检查空虚和丰满。
使用这种数据结构你显然不能在已存储的两个后续元素之间插入元素,你不能超过一定的最大容量 - 我希望它符合你的需要。