我很难理解这个数据结构。在我的课程中,我们使用以下代码使用链接列表实现队列:
class Queue
{
private Item sentinel , tail;
public Queue () {
sentinel = new Item(0,null);
tail = sentinel;
}
public void enqueue(int value) {
tail.next = new Item(value , null);
tail = tail.next;
}
public int dequeue ()
{
int value = sentinel.next.value;
sentinel.next = sentinel.next.next;
return value;
}
}
我不知道这应该如何工作,当我们调用构造函数方法时,我们sentinel[0|null]
然后我们让tail=sentinel
,tail[0|null]
。通过致电.enqueue(x)
,我们得到以下信息:
[0|pointer to tail], tail[x|null]
如果我们现在致电.dequeue()
,sentinel.next
为null
。
我向我的讲师询问了这个问题,我收到了以下回复,这对我来说并不是更清楚:“当我们通过Queue q = new Queue();
调用构造函数时,我们将创建虚拟项目sentinel,其值为零,其下一个指针为空。同时我们让尾部指向sentinel。所以显然向队列中添加元素不是问题。“
我看不到让尾巴指向哨兵的位置:/
答案 0 :(得分:1)
事实上,你的直觉是正确的。这个课程不能正常工作。
当你排队的东西,它运作良好 - 在尾部添加东西,一切正常。
当您将所有物品出列时,麻烦就开始了。当您这样做时,sentinel.next
将变为null
- 您已从队列中删除了所有内容 - 但tail
仍将指向您排队的最后一项。所以基本上你的尾巴与你的sentinel
断开了。
您可以将这些内容排入队列,并将其添加到旧尾后,但不再可以从sentinel
访问。如果您尝试将任何内容进一步出列,您将获得NullPointerException
。
为了证明这一点,我在您的Queue
类中添加了以下方法(并添加了Item类,因为您没有将它放在帖子中):
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Queue: ");
for ( Item item = sentinel.next; item != null; item = item.next ) {
sb.append('[').append(item.value).append(']');
}
return sb.toString();
}
现在,有了这个主程序:
public static void main(String[] args) {
Queue queue = new Queue();
queue.enqueue(5);
queue.enqueue(10);
System.out.println(queue);
queue.dequeue();
System.out.println(queue);
queue.dequeue();
System.out.println(queue);
queue.enqueue(15);
queue.enqueue(20);
System.out.println(queue);
queue.dequeue();
System.out.println(queue);
}
你得到:
Queue: [5][10] Queue: [10] Queue: Queue: Exception in thread "main" java.lang.NullPointerException at testing.Queue.dequeue(SimpleTest.java:48) at testing.SimpleTest.main(SimpleTest.java:27)
你应该得到的是:
Queue: [5][10] Queue: [10] Queue: Queue: [15][20] Queue: [20]
要实现这一点,您必须在出列时到达时纠正尾部。
public int dequeue ()
{
int value = sentinel.next.value;
if ( sentinel.next == tail ) {
tail = sentinel;
}
sentinel.next = sentinel.next.next;
return value;
}
实际上,当队列为空时,还应该保护dequeue()
方法不被调用。抛出一个NullPointerException
并不好,一个更明智的例外会更好。事实上它有助于创造一个更优雅的dequeue()
,而不是纠正尾巴,我们只需更改哨兵 - 抛弃旧哨兵并使用我们刚出列的项目作为我们的新哨兵:
public int dequeue ()
{
int value = sentinel.next.value;
if ( sentinel.next == null ) {
throw new IllegalStateException("No items to dequeue");
}
sentinel = sentinel.next;
return value;
}
如果我们没有进行空检查,那么当我们尝试出列时,sentinel
将变为null
,然后我们再也无法出列。通过空检查,我们确保我们有一个要出列的项目,它就成了哨兵。如果它恰好是队列中的最后一项,那么我们tail
和sentinel
指向相同的项目,就像他们在开始时所做的那样,因此我们知道我们可以继续添加项目可通过sentinel
访问。
请注意,在尝试出队之前检查队列是否为空的方法也会派上用场。
答案 1 :(得分:0)
如果我们现在调用.dequeue(),则sentinel.next仍然指向null。这个不对。 senitel只在构造函数中初始化,不再进一步改变。
所以,在connstructor之后你得到 - (Senitel) - > null和here指向Senitel。你拨打enqueue
就可以了。 (Senitel) - > (Element1) - >空值。
这里,尾部指向Element1和Senitel仍然指向(Senitel)。你叫dequeue,它变成(Senitel) - >空。