Algorithm 有没有办法减少此队列的内存使用?

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

在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|
    +---+  +---+      +---+
*/
这为所有推送/弹出/窥视和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;
    }