【问题标题】:Special kind of queue特殊的队列
【发布时间】:2010-03-21 18:19:17
【问题描述】:

我正在寻找类似队列的东西,它允许我将元素放在队列的末尾并在开始时将它们弹出,就像常规队列一样。

不同之处在于我还需要不时压缩队列。也就是说,假设我的队列中有以下项目(每个字符,包括点,都是队列中的一个项目):

e d . c . b . a
(this Queue has 8 items)

然后,我需要例如删除最后一个点,以便获得:

e d . c . b a

Java Collection 类中有类似的东西吗?我需要将它用于我正在做的程序,除了 Java 的类之外我不能使用任何东西。我不允许为自己设计一个。目前我只是使用 LinkedList,但我认为这可能更像是一个队列而不是 LinkedList。

谢谢

编辑:

项目的主要内容如下: 有一个交通信号灯可以是绿色的(相关的符号是'-')或红色的('|')。那个红绿灯在右边: alt text http://img684.imageshack.us/img684/9602/0xfhiliidyxdy43q5mageu0.png 一开始,你没有任何汽车,交通灯是绿色的,所以我们的列表表示为:

.......  -

现在,在下一次迭代中,我有一个随机变量,它会告诉我哪里有汽车来了。如果有车来,那么我们可以看到它从左边出现。在每次迭代中,所有汽车都向右移动一步。如果他们右边有任何汽车,那么他们就不能移动:

a......  -   (iteration 1)
.a.....  -   (iteration 2)
..a....  -   (iteration 3)

等等

现在,发生的情况是,有时红绿灯会变成红色('-')。在这种情况下,如果你有几辆车,那么即使它们在移动时有一定的距离,当它们必须停下来等待红绿灯时,它们也会靠近:

...b.a.  -   (iteration n)
....b.a  -   (iteration n+1)
.....ba  -   (iteration n+2) here they got close to each other

现在,这就是为什么它像队列一样工作的原因,但有时当汽车靠近红色交通信号灯时,我必须删除这些点。 还要记住,这里街道的大小是 7 个字符,但有时会增长,所以我们不能假设这是一个固定长度的列表。

【问题讨论】:

  • 您能详细说明“压缩队列”的概念吗?在我看来,您会想要自己推出实现 Queue 接口的产品(它非常小)。
  • 你不能自己设计一个吗? 哈???
  • @SLaks :也许那是为了一门课程,他们希望他重新设计它以理解它
  • 这是我必须为大学做的一个小项目,他们告诉我我只能使用一个类(所有代码逻辑所在的类)。所以我不能设计、继承/任何其他类。
  • 点和字母之间有什么特别的对应关系吗?除了最后一个字母之外,您还需要删除字母前的点吗?

标签: java collections queue


【解决方案1】:

队列基本上是具有已定义行为的项目列表,在本例中为 FIFO(先进先出)。您在最后添加项目,并从头开始删除它们。

现在可以以您选择的任何方式实现队列;使用链表或数组。我认为你在正确的道路上。链表肯定会让它更容易。

您将有 O(1) 用于队列的添加和删除(如果您保持对前面和后面的引用),但压缩(删除点)的最坏情况是 O (n)。

如果您使用辅助数据结构,我相信可能有一种方法可以将 compact 操作减少到 O(1)(如果您一次只删除一个点)。您需要的是另一个队列(使用另一个链表实现),它维护对第一个链表中点的引用。

因此,当您插入 (a, ., b, c, ., d) 时,您会得到一个如下所示的列表:

[pointer to rear] -> [d] -> [.] -> [c] -> [b] -> [.] -> [a] <- [pointer to front]

并且您还有一个辅助队列(实现为链表),它维护对点的引用:

[pointer to rear] -> [reference to second dot] -> [reference to first dot] <- [pointer to front]

然后,当您需要执行compact 操作时,您所要做的就是从第二个队列中删除第一个元素并保留引用。所以你现在有一个对第一个链表中间的点的引用。您现在可以轻松地从第一个列表中删除该点。

您在评论中提到您需要跟踪订单。根据定义,队列是一个有序的结构(在某种意义上,事物保持插入的顺序)。因此,您需要做的就是在将点插入第一个队列时,将对该点的引用插入第二个队列。这样,秩序得以维持。因此,当您从第二个队列中提取对点的引用时,您将获得对第一个队列中实际对应点的引用。

这里对速度的权衡是您需要更多内存,因为您要维护第二个引用列表。最坏情况下的内存需求是您现在使用的 2 倍。但这是获得 O(1) 与 O(n) 的一个不错的权衡。

【讨论】:

  • 看到您的最后评论,我对您的建议进行了更深入的研究:确实,它(几乎)将任务变成了 O(1)。虽然遍历队列的定义为 O(n) 是一个事实,但您的方法根本不需要遍历队列。我看到的唯一问题是它需要在任一列表上保存一个额外的引用:要从链表中删除一个元素,您需要到达前一个。现在,我的平衡效率 + 简单性与以额外内存为代价的更高速度之间是极乐世界的选择。在任何情况下,+1 表示不遍历列表;)
  • 谢谢!是的,陷阱是额外的内存,因为必须保存额外的参考。我注意到在处理效率的大多数情况下,您必须在内存和效率之间做出选择。但是现在内存很便宜(除非您要处理非常大的数据集),所以我认为在大多数情况下,这种权衡是值得的。
【解决方案2】:

家庭作业练习/学校项目总是很棘手,在要求中添加一些微妙的东西可能会使某人的大脑崩溃。您是否有任何要求将空格作为队列的一部分?

就个人而言,除非明确要求,否则我不会这样做:将您的汽车表示为 Car、Space 对似乎更简单(您可以将这对定义为结构,假设您可以使用结构)其中空间是代表车辆中下一辆车的空间的数值。然后,为了紧凑,你只需要查看列表项:当你找到一个有Space &gt; 0的,做Space--; return;,所有其他的车都已经“先进”了,因为它们与那些保持空间在他们面前。为了输出,请确保在汽车本身之后(如果红绿灯在右侧,汽车从左侧来)或之前(红绿灯在左侧,汽车从右侧来),为每辆汽车抛出Space 点,你去吧。另请注意,第一辆车的Space 表示到红绿灯本身的距离,因为它前面没有车。

如果你在结构中添加一个指向下一辆车的指针(以及一个指向最后一辆车的空指针),你已经有了一个链表:保留一个指向第一辆车的“全局”变量(或者 null 表示最后一辆车)空队列)。由于 Java 不直接支持指针,因此将结构转换为类并使用“对象引用”(与 C'ish 指针算术以外的所有用途的指针相同),然后就可以了:只有一个类,已构建从头开始。您需要从 Java 库中接触的唯一内容是标准 IO,也许还有一些字符串操作,这是由于必须接受输入和产生输出而产生的内在需求(有些大学有自己的特定于课程的 IO库,但这在这里并没有太大的区别)。要遍历队列,您可以执行以下操作(假设该类名为“Node”,这是非常通用的,并且字段名称很明显):

for(Node pos = First; pos != null; pos = pos.Next) {
    /* Do your stuff here, knowing that "pos" points to the "current" item on each iteration. */
}

要添加新节点,您可能必须遍历队列以确定新车“生成”时与最后一个节点的距离;当您这样做时,请保留最后一个节点的引用并将其“下一个”引用指向新节点:

Node last = First;
int distance = 0;
for(Node pos = First; pos != null; pos=pos.Next) {
    distance += pos.Space;
    last = pos;
}
last.Next = new Node(the_letter_for_this_car, MaxDistance-distance, null);

当然,将构造函数调整为您拥有的任何内容。

考虑到这是一个大学项目,我们来看看一些细节:compact 进程时间变成O(n),它的内存使用是O(0)(进程本身不需要任何“本地”变量,其他而不是一个遍历集合的指针,它与队列的长度无关。)此外,队列本身的内存使用量保证小于或等于它将空间表示为项目的内存使用量(它只得到在最坏的情况下是相等的,当有足够多的汽车停在红灯时)。因此,除非要求包含与此方法不兼容的内容,否则我希望这是您的老师想要的:它合理、高效且符合您的要求。

希望这会有所帮助。

【讨论】:

  • 我说我只能有一个类,因为在 Java 世界中,人们普遍认为每个文件应该只有一个类,而我只能有一个文件。如果我尝试将多个类放在同一个文件中,Eclipse 会开始发誓。
  • 嗯,在 Java 中,每个文件必须有一个 public 类,但对私有或嵌套类的数量没有限制。此外,您从拥有的类继承的内容或引用的库类没有限制。实际上,如果要获取任何输入和/或生成任何输出,您的代码将不得不(直接或间接地)引用标准 IO 类。我建议使用“程序”公共类作为入口点,外加一个实现链表概念的私有“节点”类。
  • 啊!关于单个文件中的私有类的事实使一切变得不同!
  • 糟糕,我错过了一个小细节:我上面所说的适用于源 (.java) 文件。 Java 编译器将为它在文件中找到的每个类生成一个单独的 .class 文件:公共类的文件与源文件具有相同的名称(也必须是类的名称,IIRC),而其他文件将有某种混乱的名字。我假设您必须以源代码形式提交项目。如果您需要发送编译后的代码(这很有意义),您会发现自己有几个 .class 文件,但您仍然可以将它们打包为 .jar。
  • 有趣的方法,但仍然是 O(n) :)
【解决方案3】:

我想说 LinkedList 将是这里最好的方法......因为 LinkedList 允许您从前/后推/弹出并允许您删除列表中间的项目。

显然,查找时间很糟糕,但如果您更频繁地从列表的前面/后面添加/删除然后查找,那么我会说坚持使用 LinkedList。

【讨论】:

    【解决方案4】:

    也许是第二个保留点元素的 LinkedList ?

    【讨论】:

    • 但我需要它们的顺序。如果我有第二个 LinkedList,那么我必须有一些东西来告诉原始 LinkedList 中每个点的位置。这会令人困惑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-08
    • 2012-11-23
    • 1970-01-01
    • 2012-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多