views:

130

answers:

4

I am looking for something like a Queue that would allow me to put elements at the end of the queue and pop them out in the beginning, like a regular Queue does.

The difference would be that I also need to compact the Queue from time to time. This is, let's assume I have the following items on my Queue (each character, including the dot, is an item in the Queue):

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

Then, I'd need for example to remove the last dot, so to get:

e d . c . b a

Is there anything like that in the Java Collection classes? I need to use this for a program I am doing where I can't use anything but Java's classes. I am not allowed to design one for myself. Currently I'm just using a LinkedList, but I thought maybe this would be more like a Queue than a LinkedList.

Thanks

edit:

Basically here is what the project is about: There is a traffic light that can be either green(and the symbol associated is '-') or red('|'). That traffic light is on the right: alt text In the beggining, you don't have any cars and the traffic light is green, so our list is represented as:

.......  -

Now, on the next iteration, I have a random variable that will tell me wherever there is a car coming or not. If there's a car coming, then we can see it appearing from the left. At each iteration, all cars move one step to the right. If they have any car directly on their right, then they can't move:

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

etc.

Now, what happens is that sometimes the traffic light can turn red('-'). In that case, if you have several cars, then even if they had some distance between them when moving, when they have to stop for the traffic light they will get close:

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

Now, that is the reason why this works like a Queue, but sometimes I have to take down those dots, when the cars are near the red traffic light. Keep also in mind that the size of of the street here was 7 characters, but it sometimes grows, so we can't assume this is a fixed length list.

+1  A: 

Maybe a second LinkedList which keeps the dot element ?

Kartoch
But I need them in that order. If I had a second LinkedList, then I'd have to have something to tell where is each dot in the original LinkedList. It'd be confusing.
devoured elysium
+3  A: 

I would say that a LinkedList would be the best approach here... as a LinkedList allows you to push/pop from the front/back and allows you to remove an item in the middle of the list.

Obviously, the lookup time sucks, but if you are adding/removing from the front/back of the list more often then looking up, then I'd say stick with the LinkedList.

Polaris878
+4  A: 

A queue is basically a list of items with a defined behavior, in this case FIFO (First In First Out). You add items at the end, and remove them from the beginning.

Now a queue can be implemented in any way you choose; either with a linked-list or with an Array. I think you're on the right path. A linked list would definitely make it easier.

You'll have O(1) for the add and the remove with your queue (if you maintain a reference to the front and the back), but the worst-case scenario for compacting (removing the dot) would be O(n).

I believe there might be a way to reduce the compact operation to O(1) (if you're only removing one dot at a time) if you use a secondary data structure. What you need is another queue (implemented using another linked-list) that maintains a reference to dots in the first linked list.

So, when you insert (a, ., b, c, ., d) you have a list that looks like:

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

and you also have a secondary queue (implemented as a linked list) that maintains a reference to the dot:

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

Then, when you need to perform a compact operation, all you have to do is remove the first element from the second queue and retain the reference. So you now have a reference to a dot that is in the middle of the first linked list. You can now easily remove that dot from the first list.

You mentioned in a comment that you need to keep track of the order. A queue by definition is an ordered structure (in the sense that things remain in the order they were inserted). So all you need to do is insert a reference to the dot into the second queue when you insert a dot into the first. That way, order is maintained. So when you pull off a reference to a dot from the second queue, you have a reference to the actual and corresponding dot in the first queue.

The trade-off here for speed is that you need more memory, because you're maintaining a second list of references. Worst-case memory requirement is 2x what you're using now. But that is a decent trade-off to get O(1) vs O(n).

Vivin Paliath
Seeing your last comment, I've taken a deeper look at your proposal: indeed, it (almost) turns the task into O(1). While it's a fact that traversing a queue is by definition O(n), your approach just saves the need to traverse the queue at all. Only gotcha I've seen is that it needs saving an extra reference on either of the lists: to remove an element from a linked list, you need to reach the previous one.Now it's elysiums choice between my balanced efficiency + simplicity vs. higher speed at the cost of extra memory. In any case, +1 for not traversing the list ;)
herenvardo
Thanks! Yeah, the gotcha is extra memory because of having to save that extra reference.I've noticed in most of the cases in dealing with efficiency, you have to make a choice between memory and efficiency. But memory is cheap these days (unless you are dealing with inordinately large data-sets) and so I think in most cases the trade-off is worth it.
Vivin Paliath
+3  A: 

Homework exercises/school projects are always tricky, adding subtle stuff to the requirements that may make someone's brain melt down. Do you have any requirement to include the spaces as part of the queue?

Personally, I wouldn't do that unless explicitly required: it seems simpler to represent your cars as Car, Space pairs, (you can define the pair as a struct, assuming you are allowed to use structs) where space is a numeric value representing the space towards the next car in the vehicle. Then, to compact, you only need to look through the list items: when you find one that has Space > 0, do Space--; return;, and all other cars will have already "advanced", as they keep the space with the ones in front of them. In order to output, make sure to toss out Space dots for each car after (if the stoplight is at the right and the cars come from the left) or before (stoplight at left and cars coming from right) the car itself, and there you go. Also note that the Space of the first car represents the distance to the stoplight itself, since it has no car before it.

If you add to the struct a pointer to the next car (and a null pointer for the last car), you already have a linked list: keep a "global" variable that points to the first car (or null for an empty queue). Since Java doesn't directly supports pointers, turn the struct into a class and use "object references" (which are the same as pointers for all purposes other than C'ish pointer arithmetics), and there you go: only one class, built from scratch. The only thing you'll need to touch from Java's libraries is the standard IO and, maybe, a bit of string manipulation, which is an inherent need derived from having to take input and produce output (some colleges have their own course-specific IO libraries, but that doesn't make a big difference here). To loop through the queue you'd do something like this (assuming the class is named "Node", which is quite generic, and obvious names for the fields):

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

To add new nodes you probably have to traverse the queue to figure out at which distance from the last will the new car "spawn"; when you do so keep the reference from the last node and make its "next" reference point to the new node:

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);

Of course, tune the constructor to whatever you have.

Considering that this is a college project, let's take a look at some details: the compact process time becomes O(n) and it's memory usage is O(0) (the process itself doesn't need any "local" variables, other than maybe a pointer to traverse the collection which is independent from the length of the queue.) In addition, the memory usage for the queue itself is guaranteed to be smaller or equal to what it would be representing the spaces as items (it only gets to be equal on the worst scenario, when enough cars are stuck at a red light). So, unless the requirements include something incompatible with this approach, I'd expect this to be what your teachers want: it's reasoned, efficient, and compliant to what you were asked for.

Hope this helps.

herenvardo
I say I can only have one class as it seems in the Java world it is widely accepted that you should have only one class per file, and I can only have one file. If I try to put several classes in the same file, Eclipse will start swearing about it.
devoured elysium
Well, in Java you must have exactly one _public_ class per file, but there is no limit to how many private or nested classes you may have. Also, there is no limits to what you inherit from the classes you have, or which library classes you reference. Actually, your code will have to reference (either directly or indirectly) the standard IO classes if it's going to take any input and/or generate any output.I'd suggest using a "Program" public class as entry point plus a private "Node" class that implements the linked list concept.
herenvardo
ah! That fact about the private classes in a single file makes all the difference!
devoured elysium
Oops, I missed a little detail: what I said above applies to source (.java) file. The Java compiler will generate a separate .class file for each class it finds in the file: the file for public class has the same name as the source file (which must also be the name of the class, IIRC), while the others will have some kind of mangled names.I assume you'll have to turn in the project in source form. If you are required to send the compiled code (which would make much sense), you'll find yourself with several .class files, but you can still pack'em as a .jar.
herenvardo
+1 for doing away with the "dots". :)
andras
Interesting approach, but still O(n) :)
Vivin Paliath
Yep, technically it's O(n), but:1) Traversing a queue or linked list can't really get better than O(n)2) n is expected to be smaller with this approach for the vast majority of cases; and guaranteed to never be greater than with the "dots in the queue" approach.and 3) I got rid of the dots! :P Jokes aside, it's illogical for the spaces between the cars to be "queueing" at a stoplight, so it's semantically wrong to include them as queue node: this can be acceptable as an implementation simplification, but my approach prooves that there is no need for it.
herenvardo
That is a good point. I was thinking about this off-line and you make a valid point about the semantics of a situation. Queue operations are defined to be at the front and rear of the Queue and nowhere else. I guess my attempt was to make it O(1) in all cases with the cost of additional memory. But I guess in 90% of the situations, when you're trying to improve efficiency, the trade-off is memory.
Vivin Paliath